/**
 * MFA Step-Up Context
 * 
 * Provides global MFA step-up functionality that automatically
 * prompts users for MFA verification when operations require AAL2
 */

import { createContext, useContext, useState, useCallback, ReactNode } from 'react';
import { useAuth } from '@/hooks/useAuth';
import { MFAStepUpModal } from '@/components/auth/MFAStepUpModal';
import { getEnrolledFactors } from '@/lib/authService';
import { isAAL2PolicyError, getAAL2ErrorMessage, MFACancelledError } from '@/lib/aalErrorDetection';
import { logger } from '@/lib/logger';

interface MFAStepUpContextType {
  /**
   * Wrap an operation that may require AAL2 verification
   * If operation fails due to AAL2, prompts user for MFA and retries
   */
  requireAAL2: <T>(operation: () => Promise<T>, errorMessage?: string) => Promise<T>;
  
  /**
   * Check if error is AAL2-related
   */
  isAAL2Error: (error: unknown) => boolean;
}

const MFAStepUpContext = createContext<MFAStepUpContextType | undefined>(undefined);

interface MFAStepUpProviderProps {
  children: ReactNode;
}

export function MFAStepUpProvider({ children }: MFAStepUpProviderProps) {
  const { session } = useAuth();
  const [modalOpen, setModalOpen] = useState(false);
  const [factorId, setFactorId] = useState<string>('');
  const [pendingOperation, setPendingOperation] = useState<(() => Promise<any>) | null>(null);
  const [pendingResolve, setPendingResolve] = useState<((value: any) => void) | null>(null);
  const [pendingReject, setPendingReject] = useState<((error: any) => void) | null>(null);

  /**
   * Handle successful MFA verification
   * Retries the pending operation with upgraded AAL2 session
   */
  const handleMFASuccess = useCallback(async () => {
    setModalOpen(false);
    
    if (!pendingOperation || !pendingResolve || !pendingReject) {
      // Invalid state - missing pending operations
      return;
    }

    try {
      logger.log('[MFAStepUp] Retrying operation after AAL2 upgrade');
      const result = await pendingOperation();
      pendingResolve(result);
    } catch (error) {
      // Operation failed after AAL2 - will be handled by caller
      pendingReject(error);
    } finally {
      // Clean up
      setPendingOperation(null);
      setPendingResolve(null);
      setPendingReject(null);
    }
  }, [pendingOperation, pendingResolve, pendingReject]);

  /**
   * Handle MFA cancellation
   */
  const handleMFACancel = useCallback(() => {
    setModalOpen(false);
    
    if (pendingReject) {
      pendingReject(new MFACancelledError());
    }
    
    // Clean up
    setPendingOperation(null);
    setPendingResolve(null);
    setPendingReject(null);
  }, [pendingReject]);

  /**
   * Wrap an operation with AAL2 requirement checking
   */
  const requireAAL2 = useCallback(
    async <T,>(operation: () => Promise<T>, customErrorMessage?: string): Promise<T> => {
      try {
        // Try the operation first
        return await operation();
      } catch (error) {
        // Check if error is AAL2-related
        if (!isAAL2PolicyError(error)) {
          // Not an AAL2 error, re-throw
          throw error;
        }

        logger.log('[MFAStepUp] AAL2 error detected, prompting for MFA', {
          error: error instanceof Error ? error.message : String(error),
        });

        // Get enrolled factors
        const factors = await getEnrolledFactors();
        
        if (factors.length === 0) {
          // No MFA set up
          throw new Error('MFA is not set up for your account');
        }

        const factor = factors[0];
        setFactorId(factor.id);

        // Return a promise that resolves when MFA is verified
        return new Promise<T>((resolve, reject) => {
          setPendingOperation(() => operation);
          setPendingResolve(() => resolve);
          setPendingReject(() => reject);
          setModalOpen(true);
        });
      }
    },
    []
  );

  const value: MFAStepUpContextType = {
    requireAAL2,
    isAAL2Error: isAAL2PolicyError,
  };

  return (
    <MFAStepUpContext.Provider value={value}>
      {children}
      <MFAStepUpModal
        open={modalOpen}
        factorId={factorId}
        onSuccess={handleMFASuccess}
        onCancel={handleMFACancel}
      />
    </MFAStepUpContext.Provider>
  );
}

/**
 * Hook to access MFA step-up functionality
 */
export function useMFAStepUp(): MFAStepUpContextType {
  const context = useContext(MFAStepUpContext);
  
  if (!context) {
    throw new Error('useMFAStepUp must be used within MFAStepUpProvider');
  }
  
  return context;
}
