import crypto from 'crypto';

import { logger } from '@goodgo/logger';
import { PrismaClient } from '@prisma/client';
import QRCode from 'qrcode';
import speakeasy from 'speakeasy';

import { getPrismaClient } from '../../config/database.config';
import { encryptionService } from '../../core/security/encryption.service';


/**
 * EN: MFA Service for multi-factor authentication
 * VI: Service MFA cho xác thực đa yếu tố
 */
export class MFAService {
  private prisma: PrismaClient;

  constructor() {
    this.prisma = getPrismaClient();
  }

  /**
   * EN: Enable TOTP for user
   * VI: Bật TOTP cho người dùng
   */
  async enableTOTP(userId: string, serviceName: string = 'GoodGo'): Promise<{
    secret: string;
    qrCodeUrl: string;
  }> {
    const secret = speakeasy.generateSecret({
      name: `${serviceName} (${userId})`,
      length: 32,
    });

    // Store secret temporarily (user needs to verify first)
    // In production, store in encrypted form
    const qrCodeUrl = await QRCode.toDataURL(secret.otpauth_url!);

    return {
      secret: secret.base32!,
      qrCodeUrl,
    };
  }

  /**
   * EN: Verify TOTP and enable MFA
   * VI: Xác thực TOTP và bật MFA
   */
  async verifyAndEnableTOTP(
    userId: string,
    secret: string,
    token: string
  ): Promise<boolean> {
    const verified = speakeasy.totp.verify({
      secret,
      encoding: 'base32',
      token,
      window: 2, // Allow 2 time steps (60 seconds) before/after
    });

    if (verified) {
      // Encrypt the secret before storing
      const encryptedSecret = encryptionService.encrypt(secret);

      // Create MFA device with encrypted secret
      await this.prisma.mFADevice.create({
        data: {
          userId,
          type: 'TOTP',
          name: 'TOTP Authenticator',
          secret: encryptedSecret,
          isActive: true,
        },
      });

      // Generate and store backup codes
      const backupCodes = this.generateBackupCodes();
      await this.storeBackupCodes(userId, backupCodes);

      // Enable MFA for user
      await this.prisma.user.update({
        where: { id: userId },
        data: { mfaEnabled: true },
      });

      logger.info('TOTP enabled for user with backup codes', {
        userId,
        backupCodesCount: backupCodes.length,
      });
      return true;
    }

    return false;
  }

  /**
   * EN: Verify TOTP token
   * VI: Xác thực token TOTP
   */
  async verifyTOTP(userId: string, token: string): Promise<boolean> {
    const device = await this.prisma.mFADevice.findFirst({
      where: {
        userId,
        type: 'TOTP',
        isActive: true,
      },
    });

    if (!device || !device.secret) {
      return false;
    }

    // Decrypt the secret before verification
    const decryptedSecret = encryptionService.decrypt(device.secret);

    const verified = speakeasy.totp.verify({
      secret: decryptedSecret,
      encoding: 'base32',
      token,
      window: 2,
    });

    if (verified) {
      // Update last used
      await this.prisma.mFADevice.update({
        where: { id: device.id },
        data: { lastUsedAt: new Date() },
      });
    }

    return verified;
  }

  /**
   * EN: Disable MFA for user
   * VI: Tắt MFA cho người dùng
   */
  async disableMFA(userId: string): Promise<void> {
    await this.prisma.mFADevice.updateMany({
      where: { userId },
      data: { isActive: false },
    });

    await this.prisma.user.update({
      where: { id: userId },
      data: {
        mfaEnabled: false,
        mfaSecret: null,
        mfaBackupCodes: null,
      },
    });

    logger.info('MFA disabled for user', { userId });
  }

  /**
   * EN: Get MFA devices for user
   * VI: Lấy thiết bị MFA của người dùng
   */
  async getUserMFADevices(userId: string): Promise<any[]> {
    return this.prisma.mFADevice.findMany({
      where: {
        userId,
        isActive: true,
      },
      orderBy: { createdAt: 'desc' },
    });
  }

  /**
   * EN: Generate backup codes for MFA recovery
   * VI: Tạo mã backup để khôi phục MFA
   */
  generateBackupCodes(): string[] {
    const codes: string[] = [];
    for (let i = 0; i < 10; i++) {
      // Generate 8-character alphanumeric codes
      const code = crypto.randomBytes(4).toString('hex').toUpperCase();
      codes.push(code);
    }
    return codes;
  }

  /**
   * EN: Store backup codes for user (encrypted)
   * VI: Lưu mã backup cho người dùng (đã mã hóa)
   */
  async storeBackupCodes(userId: string, codes: string[]): Promise<void> {
    const encryptedCodes = encryptionService.encrypt(JSON.stringify(codes));

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

    logger.info('Backup codes stored for user', { userId, count: codes.length });
  }

  /**
   * EN: Get backup codes for user (decrypted)
   * VI: Lấy mã backup cho người dùng (đã giải mã)
   */
  async getBackupCodes(userId: string): Promise<string[]> {
    const user = await this.prisma.user.findUnique({
      where: { id: userId },
      select: { mfaBackupCodes: true },
    });

    if (!user?.mfaBackupCodes) {
      return [];
    }

    try {
      const decryptedCodes = encryptionService.decrypt(user.mfaBackupCodes);
      return JSON.parse(decryptedCodes);
    } catch (error) {
      logger.error('Failed to decrypt backup codes', { userId, error });
      return [];
    }
  }

  /**
   * EN: Validate and consume backup code
   * VI: Xác thực và tiêu thụ mã backup
   */
  async validateBackupCode(userId: string, code: string): Promise<boolean> {
    const codes = await this.getBackupCodes(userId);

    if (!codes.includes(code)) {
      return false;
    }

    // Remove the used code
    const remainingCodes = codes.filter(c => c !== code);

    if (remainingCodes.length === 0) {
      // All backup codes used, disable MFA backup
      await this.prisma.user.update({
        where: { id: userId },
        data: { mfaBackupCodes: null },
      });
    } else {
      // Update with remaining codes
      await this.storeBackupCodes(userId, remainingCodes);
    }

    logger.info('Backup code validated and consumed', {
      userId,
      remainingCodes: remainingCodes.length,
    });

    return true;
  }

  /**
   * EN: Regenerate backup codes (invalidates all existing ones)
   * VI: Tái tạo mã backup (hủy tất cả mã hiện tại)
   */
  async regenerateBackupCodes(userId: string): Promise<string[]> {
    const newCodes = this.generateBackupCodes();
    await this.storeBackupCodes(userId, newCodes);

    logger.info('Backup codes regenerated', { userId });
    return newCodes;
  }

  /**
   * EN: Check if user has backup codes available
   * VI: Kiểm tra người dùng có mã backup không
   */
  async hasBackupCodes(userId: string): Promise<boolean> {
    const codes = await this.getBackupCodes(userId);
    return codes.length > 0;
  }
}

export const mfaService = new MFAService();
