import crypto from 'crypto';

/**
 * Admin 2FA Service
 * Handles TOTP-based two-factor authentication for admin accounts
 */

export interface TwoFactorSecret {
  secret: string;
  qrCode: string;
  backupCodes: string[];
}

export interface TwoFactorVerification {
  verified: boolean;
  message: string;
}

class Admin2FAService {
  private readonly appName = 'PlayCoinKrazy';
  private readonly issuer = 'PlayCoinKrazy Admin';
  private readonly window = 1; // Allow 1 time window before/after

  /**
   * Generate TOTP secret and QR code
   */
  generateSecret(adminEmail: string): TwoFactorSecret {
    // Generate random secret (32 bytes = 256 bits)
    const secret = crypto.randomBytes(32).toString('base64');

    // Generate backup codes (10 codes, 8 characters each)
    const backupCodes = Array.from({ length: 10 }, () =>
      crypto.randomBytes(4).toString('hex').toUpperCase()
    );

    // Generate QR code URL (using otpauth:// scheme)
    const otpauthUrl = this.generateOtpauthUrl(adminEmail, secret);

    return {
      secret,
      qrCode: otpauthUrl,
      backupCodes,
    };
  }

  /**
   * Generate otpauth:// URL for QR code
   */
  private generateOtpauthUrl(email: string, secret: string): string {
    const encoded = encodeURIComponent(`${this.appName} (${email})`);
    return `otpauth://totp/${encoded}?secret=${secret}&issuer=${encodeURIComponent(this.issuer)}`;
  }

  /**
   * Verify TOTP token
   */
  verifyToken(secret: string, token: string): boolean {
    // Remove spaces from token
    const cleanToken = token.replace(/\s/g, '');

    // Verify token is 6 digits
    if (!/^\d{6}$/.test(cleanToken)) {
      return false;
    }

    // Get current time counter (30-second intervals)
    const now = Math.floor(Date.now() / 1000);
    const timeCounter = Math.floor(now / 30);

    // Check current and adjacent time windows
    for (let i = -this.window; i <= this.window; i++) {
      const counter = timeCounter + i;
      const expectedToken = this.generateToken(secret, counter);

      if (expectedToken === cleanToken) {
        return true;
      }
    }

    return false;
  }

  /**
   * Generate TOTP token for a given counter value
   */
  private generateToken(secret: string, counter: number): string {
    // Decode secret from base64
    const decodedSecret = Buffer.from(secret, 'base64');

    // Create HMAC-SHA1 hash
    const hmac = crypto.createHmac('sha1', decodedSecret);
    const counterBuffer = Buffer.alloc(8);
    counterBuffer.writeBigInt64BE(BigInt(counter), 0);
    hmac.update(counterBuffer);
    const hash = hmac.digest();

    // Extract 4-byte dynamic binary code
    const offset = hash[hash.length - 1] & 0xf;
    const code = (hash[offset] & 0x7f) << 24 |
      (hash[offset + 1] & 0xff) << 16 |
      (hash[offset + 2] & 0xff) << 8 |
      (hash[offset + 3] & 0xff);

    // Return 6-digit code
    return String(code % 1000000).padStart(6, '0');
  }

  /**
   * Verify backup code
   */
  verifyBackupCode(code: string, backupCodes: string[]): boolean {
    const cleanCode = code.replace(/\s/g, '').toUpperCase();
    return backupCodes.includes(cleanCode);
  }

  /**
   * Remove backup code after use
   */
  removeBackupCode(code: string, backupCodes: string[]): string[] {
    const cleanCode = code.replace(/\s/g, '').toUpperCase();
    return backupCodes.filter((c) => c !== cleanCode);
  }

  /**
   * Generate new backup codes
   */
  generateBackupCodes(): string[] {
    return Array.from({ length: 10 }, () =>
      crypto.randomBytes(4).toString('hex').toUpperCase()
    );
  }

  /**
   * Get current TOTP token (for testing/debugging only)
   */
  getCurrentToken(secret: string): string {
    const now = Math.floor(Date.now() / 1000);
    const timeCounter = Math.floor(now / 30);
    return this.generateToken(secret, timeCounter);
  }

  /**
   * Get time remaining for current token (in seconds)
   */
  getTimeRemaining(): number {
    const now = Math.floor(Date.now() / 1000);
    return 30 - (now % 30);
  }
}

export const admin2FAService = new Admin2FAService();
