/**
 * User Service
 *
 * Business logic for user profile management
 */

import { prisma } from '../../shared/database/prisma.client';
import { NotFoundError, ValidationError, ConflictError, AuthenticationError } from '../../shared/types';
import { logger } from '../../shared/utils/logger';
import type { User, UserRole } from '@prisma/client';
import bcrypt from 'bcrypt';

// ============================================
// SERVICE
// ============================================

class UserService {
  /**
   * Get user profile by ID
   */
  async getUserProfile(userId: string): Promise<User> {
    const user = await prisma.user.findUnique({
      where: { id: userId },
      select: {
        id: true,
        email: true,
        username: true,
        role: true,
        telegramChatId: true,
        isActive: true,
        createdAt: true,
        updatedAt: true,
        lastLoginAt: true,
        // Exclude passwordHash for security
      },
    });

    if (!user) {
      throw new NotFoundError('User');
    }

    return user as User;
  }

  /**
   * Update user profile
   */
  async updateProfile(userId: string, data: {
    username?: string;
    telegramChatId?: string | null;
  }): Promise<User> {
    // Validate username uniqueness if changing
    if (data.username) {
      const existingUser = await prisma.user.findFirst({
        where: {
          username: data.username,
          NOT: { id: userId },
        },
      });

      if (existingUser) {
        throw new ConflictError('Username already taken');
      }
    }

    // Validate telegramChatId uniqueness if setting
    if (data.telegramChatId) {
      const existingTelegram = await prisma.user.findFirst({
        where: {
          telegramChatId: data.telegramChatId,
          NOT: { id: userId },
        },
      });

      if (existingTelegram) {
        throw new ConflictError('Telegram chat ID already linked to another account');
      }
    }

    const updatedUser = await prisma.user.update({
      where: { id: userId },
      data: {
        ...data,
        updatedAt: new Date(),
      },
      select: {
        id: true,
        email: true,
        username: true,
        role: true,
        telegramChatId: true,
        isActive: true,
        createdAt: true,
        updatedAt: true,
        lastLoginAt: true,
      },
    });

    logger.info({ userId, updates: data }, 'User profile updated');

    return updatedUser as User;
  }

  /**
   * Change user password
   */
  async changePassword(userId: string, currentPassword: string, newPassword: string): Promise<User> {
    // Validate input
    if (!currentPassword || !newPassword) {
      throw new ValidationError('Current and new password are required');
    }

    if (newPassword.length < 8) {
      throw new ValidationError('New password must be at least 8 characters');
    }

    // Find user with password hash
    const user = await prisma.user.findUnique({
      where: { id: userId },
      select: {
        id: true,
        passwordHash: true,
      },
    });

    if (!user) {
      throw new NotFoundError('User');
    }

    // Verify current password
    const isPasswordValid = await bcrypt.compare(currentPassword, user.passwordHash);
    if (!isPasswordValid) {
      throw new AuthenticationError('Current password is incorrect');
    }

    // Hash the new password
    const saltRounds = 12;
    const newPasswordHash = await bcrypt.hash(newPassword, saltRounds);

    // Update user's password
    const updatedUser = await prisma.user.update({
      where: { id: userId },
      data: {
        passwordHash: newPasswordHash,
        updatedAt: new Date(),
      },
      select: {
        id: true,
        email: true,
        username: true,
        role: true,
        telegramChatId: true,
        isActive: true,
        createdAt: true,
        updatedAt: true,
        lastLoginAt: true,
      },
    });

    logger.info({ userId }, 'Password changed successfully');

    return updatedUser as User;
  }

  /**
   * Get user statistics
   */
  async getUserStats(userId: string): Promise<{
    totalExercisesCompleted: number;
    totalPoints: number;
    currentStreak: number;
    achievementsUnlocked: number;
  }> {
    // Get progress stats
    const progressStats = await prisma.progress.aggregate({
      where: { userId },
      _sum: { points: true, exercisesCompleted: true },
      _count: { _all: true },
    });

    // Get ranking for streak info
    const globalRanking = await prisma.ranking.findFirst({
      where: { userId, moduleId: null },
      select: { streak: true },
    });

    // Get achievements count
    const achievementsCount = await prisma.userAchievement.count({
      where: { userId, unlockedAt: { not: null } },
    });

    return {
      totalExercisesCompleted: progressStats._sum.exercisesCompleted || 0,
      totalPoints: progressStats._sum.points || 0,
      currentStreak: globalRanking?.streak || 0,
      achievementsUnlocked: achievementsCount,
    };
  }

  /**
   * Get all users (admin only)
   */
  async listUsers(options?: {
    role?: UserRole;
    isActive?: boolean;
    page?: number;
    limit?: number;
  }): Promise<{ users: User[]; total: number }> {
    const page = options?.page || 1;
    const limit = options?.limit || 50;
    const skip = (page - 1) * limit;

    const where: any = {};
    if (options?.role) where.role = options.role;
    if (options?.isActive !== undefined) where.isActive = options.isActive;

    const [users, total] = await Promise.all([
      prisma.user.findMany({
        where,
        skip,
        take: limit,
        select: {
          id: true,
          email: true,
          username: true,
          role: true,
          isActive: true,
          createdAt: true,
          lastLoginAt: true,
        },
        orderBy: { createdAt: 'desc' },
      }),
      prisma.user.count({ where }),
    ]);

    return { users: users as User[], total };
  }

  /**
   * Update user role (admin only)
   */
  async updateUserRole(userId: string, role: UserRole): Promise<User> {
    const user = await prisma.user.findUnique({
      where: { id: userId },
    });

    if (!user) {
      throw new NotFoundError('User');
    }

    const updatedUser = await prisma.user.update({
      where: { id: userId },
      data: { role },
      select: {
        id: true,
        email: true,
        username: true,
        role: true,
        isActive: true,
      },
    });

    logger.info({ userId, newRole: role }, 'User role updated');

    return updatedUser as User;
  }

  /**
   * Deactivate user (admin only)
   */
  async deactivateUser(userId: string): Promise<User> {
    const user = await prisma.user.update({
      where: { id: userId },
      data: { isActive: false },
      select: {
        id: true,
        email: true,
        username: true,
        role: true,
        isActive: true,
      },
    });

    logger.info({ userId }, 'User deactivated');

    return user as User;
  }
}

// ============================================
// EXPORT
// ============================================

export const userService = new UserService();
export default userService;