import React, { createContext, useContext, useEffect, useState, useRef } from 'react';
import type { User, Session } from '@supabase/supabase-js';
import { supabase } from '@/lib/supabaseClient';
import type { Profile } from '@/types/database';
import { toast } from '@/hooks/use-toast';
import { authLog, authWarn, authError } from '@/lib/authLogger';
import type { AALLevel, CheckAalResult } from '@/types/auth';
import { getSessionAal, checkAalStepUp as checkAalStepUpService, signOutUser } from '@/lib/authService';
import { clearAllAuthFlags } from '@/lib/sessionFlags';
import { logger } from '@/lib/logger';

interface AuthContextType {
  user: User | null;
  session: Session | null;
  aal: AALLevel | null;
  loading: boolean;
  pendingEmail: string | null;
  sessionError: string | null;
  signOut: () => Promise<void>;
  verifySession: () => Promise<boolean>;
  clearPendingEmail: () => void;
  checkAalStepUp: () => Promise<CheckAalResult>;
}

const AuthContext = createContext<AuthContextType | undefined>(undefined);

function AuthProviderComponent({ children }: { children: React.ReactNode }) {
  const [user, setUser] = useState<User | null>(null);
  const [session, setSession] = useState<Session | null>(null);
  const [aal, setAal] = useState<AALLevel | null>(null);
  const [loading, setLoading] = useState(true);
  const [pendingEmail, setPendingEmail] = useState<string | null>(null);
  const [sessionError, setSessionError] = useState<string | null>(null);
  
  // Refs for lifecycle and cleanup management
  const novuUpdateTimeoutRef = useRef<NodeJS.Timeout | null>(null);
  const previousEmailRef = useRef<string | null>(null);
  const orphanedPasswordToastShownRef = useRef(false);

  // Verify session is still valid - simplified
  const verifySession = async () => {
    try {
      const { data: { session }, error } = await supabase.auth.getSession();
      
      if (error) {
        authError('[Auth] Session verification failed:', error);
        setSessionError(error.message);
        return false;
      }
      
      if (!session) {
        authLog('[Auth] No active session found');
        return false;
      }
      
      authLog('[Auth] Session verified:', session.user.email);
      
      // Update state if session was found but not set
      if (!user) {
        setSession(session);
        setUser(session.user);
      }
      
      return true;
    } catch (error: unknown) {
      authError('[Auth] Session verification error:', error);
      return false;
    }
  };

  useEffect(() => {
    authLog('[Auth] Initializing auth provider');
    
    // CRITICAL: Set up listener FIRST to catch all events
    const {
      data: { subscription },
    } = supabase.auth.onAuthStateChange((event, session) => {
      authLog('[Auth] State change:', event, 'User:', session?.user?.email || 'none', 'Has session:', !!session);
      
      // Extract email info early for cleanup
      const currentEmail = session?.user?.email;
      const newEmailPending = session?.user?.new_email;
      
      // CRITICAL: Always clear/update pending email state BEFORE any early returns
      setPendingEmail(newEmailPending ?? null);
      
      // Clear any error
      setSessionError(null);
      
      // Synchronous state updates only
      setSession(session);
      setUser(session?.user ?? null);
      
      // Handle loading state
      if (event === 'SIGNED_IN' || event === 'INITIAL_SESSION') {
        setLoading(false);
      } else if (event === 'SIGNED_OUT') {
        authLog('[Auth] SIGNED_OUT - clearing state');
        setSession(null);
        setUser(null);
        setAal(null);
        setLoading(false);
        orphanedPasswordToastShownRef.current = false;
        return;
      }
      
      // Defer async operations to avoid blocking the auth state change callback
      setTimeout(async () => {
        // Get AAL level from Supabase API (ground truth, not cached session data)
        if (session) {
          const currentAal = await getSessionAal(session);
          setAal(currentAal);
          authLog('[Auth] Current AAL:', currentAal);
          
          // Check if user is banned
          const { data: profile } = await supabase
            .from('profiles')
            .select('banned')
            .eq('user_id', session.user.id)
            .maybeSingle();

          if (profile?.banned) {
            authWarn('[Auth] Banned user detected, signing out');
            await supabase.auth.signOut();
            return;
          }
        } else {
          setAal(null);
        }
      }, 0);
      
      // Detect confirmed email change: email changed AND no longer pending
      if (
        session?.user &&
        previousEmailRef.current &&
        currentEmail &&
        currentEmail !== previousEmailRef.current &&
        !newEmailPending
      ) {
        // Clear any existing Novu update timeout
        if (novuUpdateTimeoutRef.current) {
          clearTimeout(novuUpdateTimeoutRef.current);
        }
        
        // Defer Novu update and notifications to avoid blocking auth
        const oldEmail = previousEmailRef.current;
        novuUpdateTimeoutRef.current = setTimeout(async () => {
          try {
            // Update Novu subscriber with confirmed email
            const { notificationService } = await import('@/lib/notificationService');
            if (notificationService.isEnabled()) {
              await notificationService.updateSubscriber({
                subscriberId: session.user.id,
                email: currentEmail,
              });
            }

            // Log the confirmed email change
            await supabase.from('admin_audit_log').insert({
              admin_user_id: session.user.id,
              target_user_id: session.user.id,
              action: 'email_change_completed',
              details: {
                old_email: oldEmail,
                new_email: currentEmail,
                timestamp: new Date().toISOString(),
              },
            });

            // Send final security notification
            if (notificationService.isEnabled()) {
              await notificationService.trigger({
                workflowId: 'email-changed',
                subscriberId: session.user.id,
                payload: {
                  oldEmail: oldEmail,
                  newEmail: currentEmail,
                  timestamp: new Date().toISOString(),
                },
              });
            }
          } catch (error: unknown) {
            authError('Error updating Novu after email confirmation:', error);
          } finally {
            novuUpdateTimeoutRef.current = null;
          }
        }, 0);
      }
      
      // Update tracked email
      if (currentEmail) {
        previousEmailRef.current = currentEmail;
      }
    });

    // THEN get initial session (this may trigger INITIAL_SESSION event)
    supabase.auth.getSession().then(({ data: { session }, error }) => {
      if (error) {
        authError('[Auth] Initial session fetch error:', error);
        setSessionError(error.message);
        setLoading(false);
        return;
      }
      
      // Note: onAuthStateChange will handle the INITIAL_SESSION event
      // This is just a backup in case the event doesn't fire
      authLog('[Auth] getSession completed, session exists:', !!session);
    });

    return () => {
      authLog('[Auth] Cleaning up auth provider');
      subscription.unsubscribe();
      
      // Clear any pending timeouts
      if (novuUpdateTimeoutRef.current) {
        clearTimeout(novuUpdateTimeoutRef.current);
        novuUpdateTimeoutRef.current = null;
      }
    };
  }, []);

  const signOut = async () => {
    authLog('[Auth] Signing out...');
    const result = await signOutUser();
    if (!result.success) {
      authError('Error signing out:', result.error);
      throw new Error(result.error);
    }
  };

  const clearPendingEmail = () => {
    setPendingEmail(null);
  };

  const checkAalStepUp = async (): Promise<CheckAalResult> => {
    return checkAalStepUpService(session);
  };

  const value = {
    user,
    session,
    aal,
    loading,
    pendingEmail,
    sessionError,
    signOut,
    verifySession,
    clearPendingEmail,
    checkAalStepUp,
  };

  return <AuthContext.Provider value={value}>{children}</AuthContext.Provider>;
}

export const AuthProvider = AuthProviderComponent;

export function useAuth() {
  const context = useContext(AuthContext);
  if (context === undefined) {
    logger.error('AuthContext is undefined - component may be rendering outside AuthProvider', { component: 'useAuth' });
    throw new Error('useAuth must be used within an AuthProvider');
  }
  return context;
}
