/**
 * Secure OTP Storage using Convex
 *
 * Security improvements:
 * - Convex storage with automatic expiration tracking
 * - bcrypt hashing of OTP codes (defense in depth)
 * - Attempt tracking to prevent brute force (via Convex mutations)
 * - Secure logging (no sensitive data exposed)
 * - One-time use enforcement
 *
 * Migrated from Redis to Convex (T13)
 */

import bcrypt from 'bcryptjs';
import { convexMutation } from './convex-client';
import { sendOTPEmail } from './email';

const OTP_EXPIRY_SECONDS = 10 * 60; // 10 minutes
const MAX_OTP_ATTEMPTS = 5;

/**
 * Generate a random 6-digit OTP code
 *
 * Note: Always generates random code, even in development
 * Development codes are logged to console for testing
 */
export function generateOTP(): string {
  const code = Math.floor(100000 + Math.random() * 900000).toString();

  // Log for development testing (sanitized logging)
  if (process.env.NODE_ENV !== 'production') {
    console.log(`[DEV] Generated OTP: ${code}`);
  }

  return code;
}

/**
 * Map external purpose string to Convex OTP purpose enum
 */
function mapPurpose(purpose: 'login' | 'signup' | 'reset'): 'SIGNUP' | 'LOGIN' | 'PASSWORD_RESET' {
  switch (purpose) {
    case 'login': return 'LOGIN';
    case 'signup': return 'SIGNUP';
    case 'reset': return 'PASSWORD_RESET';
    default: return 'LOGIN';
  }
}

/**
 * Store OTP in Convex with bcrypt hashing and send via email
 *
 * @param identifier - User email
 * @param code - The OTP code to store and send
 * @param language - User's preferred language ('ar' or 'en')
 * @param name - User's name for personalization (optional)
 * @param purpose - Purpose of the OTP (login, signup, or reset)
 * @returns Promise<boolean> - Success status
 */
export async function sendOTP(
  identifier: string,
  code: string,
  language: 'ar' | 'en' = 'en',
  name?: string,
  purpose: 'login' | 'signup' | 'reset' = 'login'
): Promise<boolean> {
  try {
    console.log(`[OTP] Starting OTP storage for ${identifier} (${purpose})`);

    // Hash the OTP code before storage (defense in depth)
    const hashedCode = await bcrypt.hash(code, 10);
    console.log(`[OTP] Code hashed successfully`);

    // Store hashed OTP in Convex with automatic expiration tracking
    const expiresAt = Date.now() + OTP_EXPIRY_SECONDS * 1000;
    try {
      await convexMutation("auth/mutations:createOtp", {
        email: identifier,
        otpCode: hashedCode,
        purpose: mapPurpose(purpose),
        expiresAt,
      });
      console.log(`[OTP] Hashed code stored in Convex`);
    } catch (convexError) {
      console.error(`[OTP] Failed to store hashed code in Convex:`, convexError);
      throw convexError;
    }

    // In development, log the plaintext OTP for testing
    if (process.env.NODE_ENV !== 'production') {
      console.log(`[OTP] OTP Code: ${code} for ${name || identifier} (${purpose}) - expires in ${OTP_EXPIRY_SECONDS / 60} minutes`);
    }

    // Send personalized OTP via email
    const emailSent = await sendOTPEmail(identifier, code, language, name, purpose);
    if (!emailSent) {
      console.error('[OTP] Failed to send email');
      // In development, still return true as OTP is stored in Convex
      if (process.env.NODE_ENV !== 'production') {
        console.log('[OTP] Development mode: Email not required, OTP stored in Convex');
      }

      // Log audit event
      try {
        await convexMutation("auth/mutations:createAuditLog", {
          type: 'otp_email_failed',
          action: `OTP email send failed for identifier`,
          success: false,
          ip: '127.0.0.1',
          metadata: {
            identifier: identifier.replace(/(.{2})(.*)(@.*)/, '$1***$3'),
          },
        });
      } catch (err) {
        console.error('[OTP] Audit log error:', err);
      }
    } else {
      console.log(`[OTP] OTP email sent to ${identifier}`);
    }

    // Log security event (sanitized - no OTP code)
    try {
      await convexMutation("auth/mutations:createAuditLog", {
        type: 'otp_request',
        action: `OTP requested for identifier`,
        success: true,
        ip: '127.0.0.1',
        metadata: {
          identifier: identifier.replace(/(.{2})(.*)(@.*)/, '$1***$3'),
          emailSent,
        },
      });
    } catch (err) {
      console.error('[OTP] Audit log error:', err);
    }

    console.log(`[OTP] Sent successfully (expires in ${OTP_EXPIRY_SECONDS / 60} min)`);
    return true;
  } catch (error) {
    console.error('[OTP] Storage error:', error);

    // Development mode fallback
    if (process.env.NODE_ENV !== 'production') {
      console.warn('[OTP] Convex storage failed in development mode');
      console.log(`[OTP] [DEV FALLBACK] Use OTP: ${code} for ${identifier}`);
      return true;
    }

    // In production, failure is critical - return false
    return false;
  }
}

/**
 * Verify OTP code with brute force protection
 *
 * @param identifier - User email or phone number
 * @param code - The OTP code to verify
 * @returns Promise<boolean> - Verification success
 */
export async function verifyOTP(identifier: string, code: string): Promise<boolean> {
  try {
    // Development bypass: Allow fixed code "000000" in development for testing
    if (process.env.NODE_ENV !== 'production' && code === '000000') {
      console.log(`[OTP] Development bypass code used (000000)`);
      return true;
    }

    // Use Convex verifyOtp mutation — returns { valid, storedHash?, otpId?, reason? }
    const result = await convexMutation<{
      valid: boolean;
      storedHash?: string;
      otpId?: string;
      reason?: string;
    }>("auth/mutations:verifyOtp", {
      email: identifier,
      otpCode: code, // the mutation returns storedHash for bcrypt comparison
    });

    if (!result) {
      console.log(`[OTP] Convex client not configured`);
      return false;
    }

    if (!result.valid) {
      console.log(`[OTP] Verification failed: ${result.reason}`);

      // Log security event for expired/not found
      if (result.reason === 'expired' || result.reason === 'not_found') {
        try {
          await convexMutation("auth/mutations:createAuditLog", {
            type: 'otp_verify_failed',
            action: `OTP verification failed: ${result.reason}`,
            success: false,
            ip: '127.0.0.1',
            metadata: { reason: result.reason },
          });
        } catch (err) {
          console.error('[OTP] Audit log error:', err);
        }
      }

      return false;
    }

    // The Convex mutation returned the stored hash — compare with bcrypt
    const isValid = await bcrypt.compare(code, result.storedHash!);

    if (isValid && result.otpId) {
      // Mark as verified and delete (one-time use)
      await convexMutation("auth/mutations:markOtpVerified", {
        otpId: result.otpId,
      });
      // Delete OTP after successful verification
      await convexMutation("auth/mutations:deleteOtp", {
        email: identifier,
      });

      // Log success
      try {
        await convexMutation("auth/mutations:createAuditLog", {
          type: 'otp_verify_success',
          action: `OTP verified successfully`,
          success: true,
          ip: '127.0.0.1',
        });
      } catch (err) {
        console.error('[OTP] Audit log error:', err);
      }

      console.log(`[OTP] Successfully verified`);
    } else {
      console.log(`[OTP] Verification failed (bcrypt mismatch)`);
    }

    return isValid;
  } catch (error) {
    console.error('[OTP] Verification error:', error);
    return false;
  }
}

/**
 * Clear OTP for a given identifier (admin function)
 *
 * @param identifier - User email or phone number
 */
export async function clearOTP(identifier: string): Promise<void> {
  try {
    await convexMutation("auth/mutations:deleteOtp", {
      email: identifier,
    });
    console.log(`[OTP] Cleared for identifier`);
  } catch (error) {
    console.error('[OTP] Clear error:', error);
  }
}

/**
 * Get remaining attempts for an identifier
 * Note: Attempt tracking is handled within Convex verifyOtp mutation.
 * This returns a default value since Convex handles brute-force internally.
 *
 * @param identifier - User email or phone number
 * @returns Promise<number> - Remaining attempts
 */
// eslint-disable-next-line @typescript-eslint/no-unused-vars
export async function getRemainingAttempts(identifier: string): Promise<number> {
  // Convex OTP system handles attempt tracking via verify flow
  // Return max attempts as default since we can't query attempt count externally
  return MAX_OTP_ATTEMPTS;
}

/**
 * Store OTP in Convex (without sending email)
 * Use this when you want to handle email sending separately
 *
 * @param identifier - User email or phone number
 * @param code - The OTP code to store
 * @returns Promise<boolean> - Success status
 */
export async function storeOTP(identifier: string, code: string): Promise<boolean> {
  try {
    // Hash the OTP code before storage (defense in depth)
    const hashedCode = await bcrypt.hash(code, 10);

    // Store with expiration tracking
    const expiresAt = Date.now() + OTP_EXPIRY_SECONDS * 1000;
    await convexMutation("auth/mutations:createOtp", {
      email: identifier,
      otpCode: hashedCode,
      purpose: 'LOGIN' as const,
      expiresAt,
    });

    // In development, log plaintext OTP for testing
    if (process.env.NODE_ENV !== 'production') {
      console.log(`[OTP] OTP Stored: ${code} for ${identifier} (expires in ${OTP_EXPIRY_SECONDS / 60} minutes)`);
    }

    console.log(`[OTP] Stored successfully for identifier`);
    return true;
  } catch (error) {
    console.error('[OTP] Storage error:', error);
    return false;
  }
}
