import crypto from 'crypto';
import { AuthService } from './auth.service';

export interface MFAChallenge {
  challengeId: string;
  userId: string;
  type: 'totp' | 'sms' | 'email';
  code: string;
  expiresAt: Date;
  attempts: number;
  maxAttempts: number;
  isUsed: boolean;
}

export interface MFASetup {
  userId: string;
  type: 'totp' | 'sms' | 'email';
  secret?: string; // For TOTP
  phoneNumber?: string; // For SMS
  email?: string; // For email
  isEnabled: boolean;
  backupCodes: string[];
}

// In-memory storage for MFA challenges and setups (in production, use database)
const mfaChallenges = new Map<string, MFAChallenge>();
const mfaSetups = new Map<string, MFASetup>();

export class MFAService {
  
  /**
   * Generate TOTP secret for user (using hex instead of base32)
   */
  static generateTOTPSecret(): string {
    return crypto.randomBytes(20).toString('hex');
  }
  
  /**
   * Generate 6-digit verification code
   */
  static generateVerificationCode(): string {
    return Math.floor(100000 + Math.random() * 900000).toString();
  }
  
  /**
   * Generate backup codes
   */
  static generateBackupCodes(count: number = 10): string[] {
    const codes: string[] = [];
    for (let i = 0; i < count; i++) {
      codes.push(crypto.randomBytes(4).toString('hex').toUpperCase());
    }
    return codes;
  }
  
  /**
   * Setup MFA for user
   */
  static setupMFA(userId: string, type: 'totp' | 'sms' | 'email', options: {
    phoneNumber?: string;
    email?: string;
  } = {}): MFASetup {
    // Remove existing setup if any
    mfaSetups.delete(userId);
    
    const setup: MFASetup = {
      userId,
      type,
      isEnabled: false,
      backupCodes: this.generateBackupCodes()
    };
    
    switch (type) {
      case 'totp':
        setup.secret = this.generateTOTPSecret();
        break;
      case 'sms':
        setup.phoneNumber = options.phoneNumber;
        break;
      case 'email':
        setup.email = options.email;
        break;
    }
    
    mfaSetups.set(userId, setup);
    return setup;
  }
  
  /**
   * Enable MFA after verification
   */
  static enableMFA(userId: string): boolean {
    const setup = mfaSetups.get(userId);
    if (!setup) {
      return false; // Cannot enable if not setup
    }
    
    setup.isEnabled = true;
    return true;
  }
  
  /**
   * Disable MFA
   */
  static disableMFA(userId: string): boolean {
    const setup = mfaSetups.get(userId);
    if (!setup) {
      return false; // Cannot disable if not setup
    }
    
    setup.isEnabled = false;
    return true;
  }
  
  /**
   * Check if user has MFA enabled
   */
  static isMFAEnabled(userId: string): boolean {
    const setup = mfaSetups.get(userId);
    return setup ? setup.isEnabled : false;
  }
  
  /**
   * Create MFA challenge
   */
  static createChallenge(userId: string): MFAChallenge {
    const setup = mfaSetups.get(userId);
    if (!setup || !setup.isEnabled) {
      throw new Error('MFA not enabled for user');
    }
    
    const challengeId = crypto.randomUUID();
    const code = this.generateVerificationCode();
    const expiresAt = new Date(Date.now() + 5 * 60 * 1000); // 5 minutes
    
    const challenge: MFAChallenge = {
      challengeId,
      userId,
      type: setup.type,
      code,
      expiresAt,
      attempts: 0,
      maxAttempts: 3,
      isUsed: false
    };
    
    mfaChallenges.set(challengeId, challenge);
    
    // In a real implementation, send the code via SMS/email
    this.sendVerificationCode(challenge);
    
    return challenge;
  }
  
  /**
   * Verify MFA challenge
   */
  static verifyChallenge(challengeId: string, code: string): boolean {
    const challenge = mfaChallenges.get(challengeId);
    
    if (!challenge) {
      return false;
    }
    
    // Check if challenge is expired
    if (challenge.expiresAt < new Date()) {
      mfaChallenges.delete(challengeId);
      return false;
    }
    
    // Check if challenge is already used
    if (challenge.isUsed) {
      return false;
    }
    
    // Check if max attempts exceeded
    if (challenge.attempts >= challenge.maxAttempts) {
      mfaChallenges.delete(challengeId);
      return false;
    }
    
    // Increment attempts
    challenge.attempts++;
    
    // Verify code
    if (challenge.code === code) {
      challenge.isUsed = true;
      return true;
    }
    
    return false;
  }
  
  /**
   * Verify backup code
   */
  static verifyBackupCode(userId: string, code: string): boolean {
    const setup = mfaSetups.get(userId);
    if (!setup || !setup.isEnabled) {
      return false;
    }
    
    const codeIndex = setup.backupCodes.indexOf(code.toUpperCase());
    if (codeIndex === -1) {
      return false;
    }
    
    // Remove used backup code
    setup.backupCodes.splice(codeIndex, 1);
    return true;
  }
  
  /**
   * Get MFA setup for user
   */
  static getMFASetup(userId: string): MFASetup | null {
    return mfaSetups.get(userId) || null;
  }
  
  /**
   * Clean up expired challenges
   */
  static cleanupExpiredChallenges(): void {
    const now = new Date();
    for (const [challengeId, challenge] of mfaChallenges.entries()) {
      if (challenge.expiresAt < now || challenge.isUsed) {
        mfaChallenges.delete(challengeId);
      }
    }
  }
  
  /**
   * Send verification code (mock implementation)
   */
  private static sendVerificationCode(challenge: MFAChallenge): void {
    // Mock implementation - in production, integrate with SMS/email services
    console.log(`[MFA] Sending ${challenge.type} code ${challenge.code} to user ${challenge.userId}`);
    
    switch (challenge.type) {
      case 'sms':
        // Send SMS via Twilio, AWS SNS, etc.
        break;
      case 'email':
        // Send email via SendGrid, AWS SES, etc.
        break;
      case 'totp':
        // TOTP doesn't require sending - user generates code from authenticator app
        break;
    }
  }
  
  /**
   * Validate MFA setup parameters
   */
  static validateMFASetup(type: 'totp' | 'sms' | 'email', options: {
    phoneNumber?: string;
    email?: string;
  }): boolean {
    switch (type) {
      case 'totp':
        return true; // No additional validation needed
      case 'sms':
        return !!options.phoneNumber && /^\+[1-9]\d{1,14}$/.test(options.phoneNumber);
      case 'email':
        return !!options.email && /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(options.email);
      default:
        return false;
    }
  }
  
  /**
   * Get challenge status (for debugging)
   */
  static getChallengeStatus(challengeId: string): {
    exists: boolean;
    expired: boolean;
    used: boolean;
    attemptsRemaining: number;
  } {
    const challenge = mfaChallenges.get(challengeId);
    
    if (!challenge) {
      return {
        exists: false,
        expired: false,
        used: false,
        attemptsRemaining: 0
      };
    }
    
    return {
      exists: true,
      expired: challenge.expiresAt < new Date(),
      used: challenge.isUsed,
      attemptsRemaining: challenge.maxAttempts - challenge.attempts
    };
  }
}