import {
  Injectable,
  ConflictException,
  UnauthorizedException,
} from "@nestjs/common";
import * as bcrypt from "bcrypt";
import { PrismaService } from "../../database/prisma.service";
import { BaseService } from "../../common/base";
import { CreateUserDto, UpdateUserDto, UpdateProfileDto } from "./dto/user.dto";
import { User, UserRole } from "@prisma/client";

@Injectable()
export class UsersService extends BaseService<
  User,
  CreateUserDto,
  UpdateUserDto
> {
  constructor(prisma: PrismaService) {
    super(prisma, "User");
  }

  /**
   * Create a new user with hashed password
   */
  async create(dto: CreateUserDto): Promise<User> {
    // Check if email already exists
    const existingUser = await this.findOneBy({ email: dto.email });
    if (existingUser) {
      throw new ConflictException("EMAIL_ALREADY_EXISTS");
    }

    // Hash password
    const hashedPassword = await this.hashPassword(dto.password);

    // Map password to passwordHash for Prisma
    // Exclude plain password from the data and use hashed version

    const { password: _password, ...rest } = dto;
    return this.prisma.user.create({
      data: {
        ...rest,
        passwordHash: hashedPassword,
        role: UserRole.user,
      },
    });
  }

  /**
   * Update user, hash password if provided
   */
  async update(id: string, dto: UpdateUserDto): Promise<User> {
    const updateData: any = { ...dto };

    if (dto.password) {
      updateData.passwordHash = await this.hashPassword(dto.password);
      delete updateData.password;
    }

    return this.prisma.user.update({
      where: { id },
      data: updateData,
    });
  }

  /**
   * Find user by email
   */
  async findByEmail(email: string): Promise<User | null> {
    return this.prisma.user.findUnique({ where: { email } });
  }

  /**
   * Get user with subscription and usage info
   */
  findOneWithDetails(id: string) {
    return this.prisma.user.findUnique({
      where: { id },
      include: {
        usageLimit: true,
      },
    });
  }

  /**
   * Check if user has specific role
   */
  hasRole(user: User, role: UserRole): boolean {
    return user.role === role;
  }

  /**
   * Update user role
   */
  async updateRole(userId: string, role: UserRole): Promise<User> {
    return this.prisma.user.update({
      where: { id: userId },
      data: { role },
    });
  }

  /**
   * Get or create usage limit for user
   */
  async getOrCreateUsageLimit(userId: string) {
    let usageLimit = await this.prisma.usageLimit.findUnique({
      where: { userId },
    });

    if (!usageLimit) {
      usageLimit = await this.prisma.usageLimit.create({
        data: {
          userId,
          analysisCount: 0,
          couponCount: 0,
          lastResetDate: new Date(),
        },
      });
    }

    return usageLimit;
  }

  /**
   * Increment analysis count
   */
  async incrementAnalysisCount(userId: string): Promise<void> {
    await this.prisma.usageLimit.upsert({
      where: { userId },
      update: { analysisCount: { increment: 1 } },
      create: {
        userId,
        analysisCount: 1,
        couponCount: 0,
        lastResetDate: new Date(),
      },
    });
  }

  /**
   * Hash password using bcrypt
   */
  private async hashPassword(password: string): Promise<string> {
    const saltRounds = 12;
    return bcrypt.hash(password, saltRounds);
  }

  /**
   * Compare password with hash
   */
  async comparePassword(
    password: string,
    hashedPassword: string,
  ): Promise<boolean> {
    return bcrypt.compare(password, hashedPassword);
  }

  /**
   * Update user profile (first name, last name)
   */
  async updateProfile(userId: string, dto: UpdateProfileDto): Promise<User> {
    return this.prisma.user.update({
      where: { id: userId },
      data: dto,
    });
  }

  /**
   * Change user password with current password verification
   */
  async changePassword(
    userId: string,
    currentPassword: string,
    newPassword: string,
  ): Promise<void> {
    const user = await this.prisma.user.findUnique({
      where: { id: userId },
    });

    if (!user) {
      throw new UnauthorizedException("USER_NOT_FOUND");
    }

    const isCurrentPasswordValid = await this.comparePassword(
      currentPassword,
      user.passwordHash,
    );

    if (!isCurrentPasswordValid) {
      throw new UnauthorizedException("INVALID_CURRENT_PASSWORD");
    }

    const hashedNewPassword = await this.hashPassword(newPassword);

    await this.prisma.user.update({
      where: { id: userId },
      data: { passwordHash: hashedNewPassword },
    });
  }
}
