/**
 * OTP Service - Enterprise Implementation
 *
 * Centralized OTP management with clean architecture principles.
 * Implements business logic for OTP generation, verification, and delivery.
 *
 * Features:
 * - Type-safe interfaces
 * - Comprehensive error handling
 * - Audit logging integration
 * - Rate limiting awareness
 * - Personalized email delivery
 *
 * @module lib/services/otp.service
 */

import { generateOTP, sendOTP, verifyOTP as verifyOTPStorage, getRemainingAttempts as getAttemptsFromStorage } from '@/lib/otp-storage';
import { prisma } from '@/lib/db';
import type {
  IOTPService,
  OTPUserContext,
  OTPGenerationResult,
  OTPVerificationResult,
  OTP_CONFIG,
} from '@/lib/types/otp.types';

/**
 * Production-ready OTP Service Implementation
 *
 * @class OTPService
 * @implements {IOTPService}
 */
class OTPService implements IOTPService {
  /**
   * Generate and send OTP to user with personalization
   *
   * @param context - User context for OTP generation
   * @returns Promise resolving to generation result
   *
   * @example
   * ```typescript
   * const result = await otpService.generateAndSend({
   *   email: 'user@example.com',
   *   name: 'John Doe',
   *   language: 'en',
   *   purpose: 'login',
   *   ipAddress: '192.168.1.1'
   * });
   *
   * if (result.success) {
   *   console.log('OTP sent successfully');
   * }
   * ```
   */
  async generateAndSend(context: OTPUserContext): Promise<OTPGenerationResult> {
    try {
      // Generate cryptographically secure OTP
      const code = generateOTP();

      // Send OTP via email with personalization
      const sent = await sendOTP(
        context.email,
        code,
        context.language,
        context.name,
        context.purpose
      );

      if (!sent) {
        return {
          success: false,
          error: 'Failed to send verification code. Please try again.',
        };
      }

      // Calculate expiry time
      const expiresAt = new Date(Date.now() + 10 * 60 * 1000); // 10 minutes

      // Log generation event (if user exists)
      await this.logOTPEvent(context.email, 'otp_generated', true, {
        purpose: context.purpose,
        ipAddress: context.ipAddress,
      });

      return {
        success: true,
        expiresAt,
        // Only include code in development for debugging
        ...(process.env.NODE_ENV === 'development' && { code }),
      };
    } catch (error) {
      console.error('[OTPService] Generation error:', error);

      return {
        success: false,
        error: 'Failed to generate verification code. Please try again later.',
      };
    }
  }

  /**
   * Verify OTP code with comprehensive validation
   *
   * @param email - User's email address
   * @param code - OTP code to verify
   * @param purpose - Expected purpose of the OTP
   * @returns Promise resolving to verification result
   *
   * @example
   * ```typescript
   * const result = await otpService.verify(
   *   'user@example.com',
   *   '123456',
   *   'login'
   * );
   *
   * if (result.success) {
   *   // OTP verified - proceed with login
   * }
   * ```
   */
  async verify(
    email: string,
    code: string,
    purpose: OTPPurpose
  ): Promise<OTPVerificationResult> {
    try {
      // Check remaining attempts before verification
      const remaining = await this.getRemainingAttempts(email);

      if (remaining <= 0) {
        await this.logOTPEvent(email, 'otp_max_attempts_exceeded', false);

        return {
          success: false,
          error: 'Maximum verification attempts exceeded. Please request a new code.',
          remainingAttempts: 0,
        };
      }

      // Verify OTP code
      const isValid = await verifyOTPStorage(email, code);

      if (!isValid) {
        const newRemaining = await this.getRemainingAttempts(email);

        await this.logOTPEvent(email, 'otp_verification_failed', false, {
          remainingAttempts: newRemaining,
          purpose,
        });

        return {
          success: false,
          error: 'Invalid or expired verification code',
          remainingAttempts: newRemaining,
        };
      }

      // Verification successful
      await this.logOTPEvent(email, 'otp_verification_success', true, {
        purpose,
      });

      return {
        success: true,
      };
    } catch (error) {
      console.error('[OTPService] Verification error:', error);

      return {
        success: false,
        error: 'Verification failed. Please try again.',
      };
    }
  }

  /**
   * Get remaining verification attempts for an email
   *
   * @param email - User's email address
   * @returns Promise resolving to remaining attempts count
   */
  async getRemainingAttempts(email: string): Promise<number> {
    try {
      return await getAttemptsFromStorage(email);
    } catch (error) {
      console.error('[OTPService] Get attempts error:', error);
      return 0;
    }
  }

  /**
   * Invalidate all OTPs for an email address
   *
   * @param email - User's email address
   * @returns Promise that resolves when invalidation is complete
   */
  async invalidate(email: string): Promise<void> {
    try {
      const { clearOTP } = await import('@/lib/otp-storage');
      await clearOTP(email);

      await this.logOTPEvent(email, 'otp_invalidated', true);
    } catch (error) {
      console.error('[OTPService] Invalidation error:', error);
    }
  }

  /**
   * Log OTP-related events for audit trail
   *
   * @private
   * @param email - User's email (will be partially masked in logs)
   * @param eventType - Type of OTP event
   * @param success - Whether the operation succeeded
   * @param metadata - Additional event metadata
   */
  private async logOTPEvent(
    email: string,
    eventType: string,
    success: boolean,
    metadata?: Record<string, any>
  ): Promise<void> {
    try {
      // Find user (may not exist for signup)
      const user = await prisma.users.findUnique({
        where: { email: email.toLowerCase() },
        select: { id: true },
      });

      await prisma.audit_logs.create({
        data: {
          userId: user?.id,
          type: eventType,
          action: this.getActionDescription(eventType),
          success,
          ip: '127.0.0.1', // Will be overridden in route handler
          metadata: {
            ...metadata,
            emailMasked: this.maskEmail(email),
          },
        },
      });
    } catch (error) {
      // Log errors should not break the main flow
      console.error('[OTPService] Audit log error:', error);
    }
  }

  /**
   * Get human-readable action description for audit log
   *
   * @private
   * @param eventType - Event type identifier
   * @returns Human-readable description
   */
  private getActionDescription(eventType: string): string {
    const descriptions: Record<string, string> = {
      otp_generated: 'OTP code generated and sent',
      otp_verification_success: 'OTP verified successfully',
      otp_verification_failed: 'OTP verification failed',
      otp_max_attempts_exceeded: 'Maximum OTP verification attempts exceeded',
      otp_invalidated: 'OTP codes invalidated',
    };

    return descriptions[eventType] || eventType;
  }

  /**
   * Mask email address for security logging
   *
   * @private
   * @param email - Email address to mask
   * @returns Partially masked email (e.g., "jo***@example.com")
   */
  private maskEmail(email: string): string {
    const [localPart, domain] = email.split('@');

    if (localPart.length <= 2) {
      return `**@${domain}`;
    }

    return `${localPart.slice(0, 2)}***@${domain}`;
  }
}

/**
 * Singleton instance of OTP Service
 * Use this throughout the application for OTP operations
 *
 * @example
 * ```typescript
 * import { otpService } from '@/lib/services/otp.service';
 *
 * const result = await otpService.generateAndSend({
 *   email: 'user@example.com',
 *   name: 'John Doe',
 *   language: 'en',
 *   purpose: 'login'
 * });
 * ```
 */
export const otpService = new OTPService();

/**
 * Type guard to check if result is successful
 *
 * @param result - OTP operation result
 * @returns True if operation succeeded
 */
export function isOTPSuccess(
  result: OTPGenerationResult | OTPVerificationResult
): result is { success: true } {
  return result.success === true;
}

/**
 * Type guard to check if result is an error
 *
 * @param result - OTP operation result
 * @returns True if operation failed
 */
export function isOTPError(
  result: OTPGenerationResult | OTPVerificationResult
): result is { success: false; error: string } {
  return result.success === false && 'error' in result;
}
