import { Injectable, ConflictException } from '@nestjs/common';
import * as bcrypt from 'bcrypt';
import { PrismaService } from '../../database/prisma.service';
import { BaseService } from '../../common/base';
import { CreateUserDto, UpdateUserDto } from './dto/user.dto';
import { User } 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);

    return super.create({
      ...dto,
      password: hashedPassword,
    });
  }

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

    return super.update(id, dto);
  }

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

  /**
   * Get user with roles and permissions
   */
  findOneWithRoles(id: string) {
    return this.prisma.user.findUnique({
      where: { id },
      include: {
        roles: {
          include: {
            role: {
              include: {
                permissions: {
                  include: {
                    permission: true,
                  },
                },
              },
            },
          },
        },
      },
    });
  }

  /**
   * Assign role to user
   */
  assignRole(userId: string, roleId: string) {
    return this.prisma.userRole.create({
      data: {
        userId,
        roleId,
      },
    });
  }

  /**
   * Remove role from user
   */
  removeRole(userId: string, roleId: string) {
    return this.prisma.userRole.deleteMany({
      where: {
        userId,
        roleId,
      },
    });
  }

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