import { Injectable } from '@nestjs/common';
import { type Prisma, type User as PrismaUser } from '@prisma/client';
import { PrismaService } from '@modules/shared';
import { UserEntity, type UserProps } from '../../domain/entities/user.entity';
import { type IUserRepository } from '../../domain/repositories/user.repository';
import { Email } from '../../domain/value-objects/email.vo';
import { HashedPassword } from '../../domain/value-objects/hashed-password.vo';
import { Phone } from '../../domain/value-objects/phone.vo';

@Injectable()
export class PrismaUserRepository implements IUserRepository {
  constructor(private readonly prisma: PrismaService) {}

  async findById(id: string): Promise<UserEntity | null> {
    const user = await this.prisma.user.findUnique({ where: { id } });
    return user ? this.toDomain(user) : null;
  }

  async findByPhone(phone: string): Promise<UserEntity | null> {
    const hash = this.prisma.fieldEncryption.computeHash(phone);
    if (hash) {
      const user = await this.prisma.user.findUnique({ where: { phoneHash: hash } });
      return user ? this.toDomain(user) : null;
    }
    // Fallback: encryption not configured — query plaintext
    const user = await this.prisma.user.findFirst({ where: { phone } });
    return user ? this.toDomain(user) : null;
  }

  async findByEmail(email: string): Promise<UserEntity | null> {
    const hash = this.prisma.fieldEncryption.computeHash(email);
    if (hash) {
      const user = await this.prisma.user.findUnique({ where: { emailHash: hash } });
      return user ? this.toDomain(user) : null;
    }
    // Fallback: encryption not configured — query plaintext
    const user = await this.prisma.user.findFirst({ where: { email } });
    return user ? this.toDomain(user) : null;
  }

  async save(entity: UserEntity): Promise<void> {
    await this.prisma.user.create({
      data: {
        id: entity.id,
        email: entity.email?.value ?? null,
        phone: entity.phone.value,
        passwordHash: entity.passwordHash?.value ?? null,
        fullName: entity.fullName,
        avatarUrl: entity.avatarUrl,
        role: entity.role,
        kycStatus: entity.kycStatus,
        kycData: entity.kycData as Prisma.InputJsonValue,
        isActive: entity.isActive,
        totpSecret: entity.totpSecret,
        totpEnabled: entity.totpEnabled,
        totpBackupCodes: entity.totpBackupCodes,
        totpEnabledAt: entity.totpEnabledAt,
      },
    });
  }

  async update(entity: UserEntity): Promise<void> {
    await this.prisma.user.update({
      where: { id: entity.id },
      data: {
        email: entity.email?.value ?? null,
        phone: entity.phone.value,
        passwordHash: entity.passwordHash?.value ?? null,
        fullName: entity.fullName,
        avatarUrl: entity.avatarUrl,
        role: entity.role,
        kycStatus: entity.kycStatus,
        kycData: entity.kycData as Prisma.InputJsonValue,
        isActive: entity.isActive,
        totpSecret: entity.totpSecret,
        totpEnabled: entity.totpEnabled,
        totpBackupCodes: entity.totpBackupCodes,
        totpEnabledAt: entity.totpEnabledAt,
      },
    });
  }

  async updateMfaSecret(userId: string, secret: string | null): Promise<void> {
    await this.prisma.user.update({
      where: { id: userId },
      data: { totpSecret: secret },
    });
  }

  async updateMfaEnabled(
    userId: string,
    enabled: boolean,
    secret: string,
    backupCodes: string[],
  ): Promise<void> {
    await this.prisma.user.update({
      where: { id: userId },
      data: {
        totpEnabled: enabled,
        totpSecret: secret,
        totpBackupCodes: backupCodes,
        totpEnabledAt: enabled ? new Date() : null,
      },
    });
  }

  async updateMfaDisabled(userId: string): Promise<void> {
    await this.prisma.user.update({
      where: { id: userId },
      data: {
        totpEnabled: false,
        totpSecret: null,
        totpBackupCodes: [],
        totpEnabledAt: null,
      },
    });
  }

  async updateBackupCodes(userId: string, backupCodes: string[]): Promise<void> {
    await this.prisma.user.update({
      where: { id: userId },
      data: { totpBackupCodes: backupCodes },
    });
  }

  private toDomain(raw: PrismaUser): UserEntity {
    const phone = Phone.create(raw.phone).unwrap();
    const email = raw.email ? Email.create(raw.email).unwrap() : null;
    const passwordHash = raw.passwordHash
      ? HashedPassword.fromHash(raw.passwordHash)
      : null;

    const props: UserProps = {
      email,
      phone,
      passwordHash,
      fullName: raw.fullName,
      avatarUrl: raw.avatarUrl,
      role: raw.role,
      kycStatus: raw.kycStatus,
      kycData: raw.kycData,
      isActive: raw.isActive,
      deletedAt: raw.deletedAt,
      totpSecret: raw.totpSecret,
      totpEnabled: raw.totpEnabled,
      totpBackupCodes: raw.totpBackupCodes,
      totpEnabledAt: raw.totpEnabledAt,
    };

    return new UserEntity(raw.id, props, raw.createdAt, raw.updatedAt);
  }
}
