// @ts-nocheck
'use client';

import { usePathname } from 'next/navigation';
import React, {
    createContext, ReactNode, useCallback, useContext, useEffect, useMemo, useRef, useState
} from 'react';

import { useCfgRouter, useLocalStorage, useQueryParams } from '../hooks';

import { api as apiAccounts, Enums } from '../../';
import { clearProfileCache, getCachedProfile } from '../hooks/useProfileCache';
import { useAuthRedirectManager } from '../hooks/useAuthRedirect';
import { useTokenRefresh } from '../hooks/useTokenRefresh';
import { Analytics, AnalyticsCategory, AnalyticsEvent } from '../utils/analytics';
import { authLogger } from '../utils/logger';
import { AccountsProvider, useAccountsContext } from './AccountsContext';

import type { AuthConfig, AuthContextType, AuthProviderProps, UserProfile } from './types';

// Default routes
const defaultRoutes = {
  auth: '/auth',
  defaultCallback: '/dashboard',
  defaultAuthCallback: '/auth',
};

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

// Constants
const EMAIL_STORAGE_KEY = 'auth_email';
const PHONE_STORAGE_KEY = 'auth_phone';

const hasValidTokens = (): boolean => {
  if (typeof window === 'undefined') return false;
  return apiAccounts.isAuthenticated();
};

// Internal provider that uses AccountsContext
const AuthProviderInternal: React.FC<AuthProviderProps> = ({ children, config }) => {
  const accounts = useAccountsContext();

  // Redirect URL manager for saving URL before auth redirect
  const redirectManager = useAuthRedirectManager({
    fallbackUrl: config?.routes?.defaultCallback || defaultRoutes.defaultCallback,
    clearOnUse: true,
  });

  // Smart initial loading state: only true if we don't have tokens yet
  const [isLoading, setIsLoading] = useState(() => {
    // If we already have tokens and profile, don't show loading
    if (typeof window !== 'undefined') {
      const hasTokens = hasValidTokens();
      // Only show loading on initial mount if no tokens
      return !hasTokens;
    }
    return true;
  });

  const [initialized, setInitialized] = useState(false);
  const router = useCfgRouter();
  const pathname = usePathname();
  const queryParams = useQueryParams();

  // Use localStorage hooks for email and phone
  const [storedEmail, setStoredEmail, clearStoredEmail] = useLocalStorage<string | null>(EMAIL_STORAGE_KEY, null);
  const [storedPhone, setStoredPhone, clearStoredPhone] = useLocalStorage<string | null>(PHONE_STORAGE_KEY, null);

  // Automatic token refresh - refreshes token before expiry, on focus, and on network reconnect
  useTokenRefresh({
    enabled: true,
    onRefresh: (newToken) => {
      authLogger.info('Token auto-refreshed successfully');
    },
    onRefreshError: (error) => {
      authLogger.warn('Token auto-refresh failed:', error.message);
      // Don't logout on refresh error - user might still have valid session
    },
  });

  // Map AccountsContext profile to UserProfile
  const user = accounts.profile as UserProfile | null;

  // Use refs to avoid dependency issues
  const userRef = useRef(user);
  const configRef = useRef(config);
  const isLoadingProfileRef = useRef(false);

  // Update refs when values change
  useEffect(() => {
    userRef.current = user;
  }, [user]);

  useEffect(() => {
    configRef.current = config;
  }, [config]);

  // Note: API URL is configured in BaseClient, not at runtime

  // Common function to clear auth state
  const clearAuthState = useCallback((caller: string) => {
    authLogger.info('clearAuthState >> caller', caller);
    apiAccounts.clearTokens();
    clearProfileCache(); // Clear profile cache from localStorage
    // Note: user is now managed by AccountsContext, will auto-update
    setInitialized(true);
    setIsLoading(false);
  }, []);

  // Global error handler for auth-related errors
  // Only clears auth on actual authentication errors (401), not on any API error
  const handleGlobalAuthError = useCallback((error: any, context: string = 'API Request') => {
    // Only clear auth on actual authentication errors (401)
    // Don't logout on validation errors, server errors, network issues, etc.
    const isAuthError = error?.status === 401 ||
                        error?.statusCode === 401 ||
                        error?.code === 'token_not_valid' ||
                        error?.code === 'authentication_failed';

    if (isAuthError) {
      authLogger.warn(`Authentication error in ${context}, clearing tokens`);
      clearAuthState(`globalAuthError:${context}`);
      return true;
    }

    // Log but don't logout for other errors
    if (error?.success === false) {
      authLogger.warn(`Non-auth error in ${context} (not clearing session):`, error?.message || error);
    }

    return false;
  }, [clearAuthState]);

  // Simple profile loading without retry - now uses AccountsContext with memoization
  const loadCurrentProfile = useCallback(async (callerId?: string): Promise<void> => {
    const finalCallerId = callerId || 'AuthContext.loadCurrentProfile';

    // Check if profile loading is already in progress
    if (isLoadingProfileRef.current) {
      authLogger.debug(`Profile loading already in progress, skipping duplicate call from: ${finalCallerId}`);
      return;
    }

    authLogger.debug(`loadCurrentProfile called by: ${finalCallerId}`);

    try {
      isLoadingProfileRef.current = true;

      // Ensure API clients are properly initialized with current token
      const isAuth = apiAccounts.isAuthenticated();
      const token = apiAccounts.getToken();
      // authLogger.debug('isAuthenticated:', isAuth, 'token:', token ? token.substring(0, 20) + '...' : 'null');

      if (!isAuth) {
        authLogger.warn('No valid authentication token, throwing error');
        throw new Error('No valid authentication token');
      }

      // Check if profile is already loaded in AccountsContext to prevent duplicate API calls
      if (accounts.profile && !accounts.isLoadingProfile) {
        authLogger.debug('Profile already loaded in AccountsContext, skipping API call');
        setInitialized(true);
        return;
      }

      // Refresh profile from AccountsContext (now with memoization)
      const refreshedProfile = await accounts.refreshProfile(finalCallerId);

      if (refreshedProfile) {
        authLogger.info('Profile loaded successfully:', refreshedProfile.id);
      } else {
        authLogger.warn('Profile refresh returned undefined - but keeping tokens');
      }

      // Always mark as initialized if we have valid tokens
      // Don't clear tokens just because profile fetch failed
      setInitialized(true);
    } catch (error: any) {
      authLogger.error('Failed to load profile:', error);
      // Only clear auth state on actual authentication errors (401)
      // Don't logout on network errors, server errors, etc.
      const isAuthError = error?.status === 401 ||
                          error?.statusCode === 401 ||
                          error?.code === 'token_not_valid' ||
                          error?.code === 'authentication_failed';

      if (isAuthError) {
        authLogger.warn('Authentication error, clearing session');
        clearAuthState('loadCurrentProfile:authError');
      } else {
        // Keep tokens, mark as initialized - user can retry
        authLogger.warn('Profile load failed but keeping session (non-auth error)');
        setInitialized(true);
      }
    } finally {
      isLoadingProfileRef.current = false;
    }
  }, [clearAuthState, handleGlobalAuthError, accounts]);

  // Initialize auth state once
  useEffect(() => {
    if (initialized) return;

    const initializeAuth = async () => {
      authLogger.info('Initializing auth...');

      // Check if running in iframe (AdminLayout will handle auth via postMessage)
      const isInIframe = typeof window !== 'undefined' && window.self !== window.top;
      authLogger.info('Is in iframe:', isInIframe);

      // Debug token state
      const token = apiAccounts.getToken();
      const refreshToken = apiAccounts.getRefreshToken();
      authLogger.info('Token from API:', token ? `${token.substring(0, 20)}...` : 'null');
      authLogger.info('Refresh token from API:', refreshToken ? `${refreshToken.substring(0, 20)}...` : 'null');
      authLogger.info('localStorage keys:', Object.keys(localStorage).filter(k => k.includes('token') || k.includes('auth')));

      const hasTokens = hasValidTokens();
      authLogger.info('Has tokens:', hasTokens);

      // Check if profile is already loaded from cache (AccountsContext initialization)
      if (userRef.current) {
        authLogger.info('Profile already loaded from AccountsContext cache, skipping API request');
        setInitialized(true);
        setIsLoading(false);
        return;
      }

      // Check if cache exists in localStorage (before userRef updates)
      const cachedProfile = getCachedProfile();
      if (cachedProfile) {
        authLogger.info('Profile found in localStorage cache, skipping API request');
        setInitialized(true);
        setIsLoading(false);
        return;
      }

      // In iframe mode WITHOUT tokens yet - wait for AdminLayout to receive them via postMessage
      // Don't initialize yet - AdminLayout.handleAuthToken will call loadCurrentProfile
      if (isInIframe && !hasTokens) {
        authLogger.info('Running in iframe without tokens - waiting for parent to send via postMessage');
        authLogger.info('AdminLayout will handle auth initialization, skipping AuthContext init');
        setInitialized(true); // Mark as initialized to prevent re-initialization
        setIsLoading(false);
        return;
      }

      if (hasTokens) {
        setIsLoading(true);
        try {
          authLogger.info('No cached profile found, loading from API...');
          await loadCurrentProfile('AuthContext.initializeAuth');
        } catch (error: any) {
          authLogger.error('Failed to load profile during initialization:', error);
          // Only clear on 401 auth error, otherwise keep session
          const isAuthError = error?.status === 401 ||
                              error?.statusCode === 401 ||
                              error?.code === 'token_not_valid' ||
                              error?.code === 'authentication_failed';

          if (isAuthError) {
            clearAuthState('initializeAuth:authError');
          } else {
            authLogger.warn('Init profile load failed but keeping session');
            setInitialized(true);
          }
        }
        setIsLoading(false);
      } else {
        setInitialized(true);
        setIsLoading(false);
      }
    };

    initializeAuth();
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [initialized]);

  // Redirect logic - only for unauthenticated users on protected pages
  useEffect(() => {
    if (!initialized) return;

    // Consider authenticated if we have valid tokens, even without profile
    const isAuthenticated = apiAccounts.isAuthenticated();
    const authRoute = config?.routes?.auth || defaultRoutes.auth;
    const isAuthPage = pathname === authRoute;
    const flowParam = queryParams.get('flow');

    // Only redirect authenticated users away from auth page if they're not in a flow
    // This prevents interference with OTP verification flow
    if (isAuthenticated && isAuthPage && !flowParam) {
      const callbackUrl = config?.routes?.defaultCallback || defaultRoutes.defaultCallback;
      router.push(callbackUrl);
    }
  }, [initialized, pathname, queryParams, config?.routes]);

  const pushToDefaultCallbackUrl = useCallback(() => {
    const callbackUrl = config?.routes?.defaultCallback || defaultRoutes.defaultCallback;
    router.push(callbackUrl);
  }, [config?.routes, router]);

  const pushToDefaultAuthCallbackUrl = useCallback(() => {
    const authCallbackUrl = config?.routes?.defaultAuthCallback || defaultRoutes.defaultAuthCallback;
    router.push(authCallbackUrl);
  }, [config?.routes, router]);

  // Memoized checkAuthAndRedirect function
  const checkAuthAndRedirect = useCallback(async () => {
    try {
      setIsLoading(true);
      const isAuthenticated = apiAccounts.isAuthenticated();

      if (isAuthenticated) {
        await loadCurrentProfile();
        if (userRef.current) {
          pushToDefaultCallbackUrl();
        }
      } else {
        pushToDefaultAuthCallbackUrl();
      }
    } catch (error) {
      authLogger.error('Failed to check authentication:', error);
      // Use global error handler first
      if (!handleGlobalAuthError(error, 'checkAuthAndRedirect')) {
        clearAuthState('checkAuthAndRedirect');
      }
      pushToDefaultAuthCallbackUrl();
    } finally {
      setIsLoading(false);
    }
  }, [loadCurrentProfile, clearAuthState, pushToDefaultCallbackUrl, pushToDefaultAuthCallbackUrl, handleGlobalAuthError]);

  // OTP methods - supports both email and phone - now uses AccountsContext
  const requestOTP = useCallback(
    async (identifier: string, channel?: 'email' | 'phone', sourceUrl?: string): Promise<{ success: boolean; message: string }> => {
      // Clear tokens before requesting OTP
      apiAccounts.clearTokens();

      try {
        const channelValue = channel === 'phone' 
          ? Enums.OTPRequestRequestChannel.PHONE 
          : Enums.OTPRequestRequestChannel.EMAIL;
        const result = await accounts.requestOTP({
          identifier,
          channel: channelValue,
        });

        const channelName = channel === 'phone' ? 'phone number' : 'email address';

        // Track OTP request
        Analytics.event(AnalyticsEvent.AUTH_OTP_REQUEST, {
          category: AnalyticsCategory.AUTH,
          label: channel || 'email',
        });

        return {
          success: true,
          message: result.message || `OTP code sent to your ${channelName}`,
        };
      } catch (error) {
        authLogger.error('Request OTP error:', error);
        return {
          success: false,
          message: 'Failed to send OTP',
        };
      }
    },
    [accounts],
  );

  const verifyOTP = useCallback(
    async (identifier: string, otpCode: string, channel?: 'email' | 'phone', sourceUrl?: string, redirectUrl?: string, skipRedirect?: boolean): Promise<{
      success: boolean;
      message: string;
      user?: UserProfile;
      requires_2fa?: boolean;
      session_id?: string | null;
      should_prompt_2fa?: boolean;
    }> => {
      try {
        const channelValue = channel === 'phone'
          ? Enums.OTPVerifyRequestChannel.PHONE
          : Enums.OTPVerifyRequestChannel.EMAIL;
        // AccountsContext automatically saves tokens and refreshes profile
        const result = await accounts.verifyOTP({
          identifier,
          otp: otpCode,
          channel: channelValue,
        });

        // Check if 2FA is required - return early with session info
        if (result.requires_2fa && result.session_id) {
          authLogger.info('2FA required, session:', result.session_id);
          return {
            success: true,
            message: 'OTP verified, 2FA required',
            requires_2fa: true,
            session_id: result.session_id,
            should_prompt_2fa: result.should_prompt_2fa,
          };
        }

        // Verify that we got valid tokens (only required when 2FA is not needed)
        if (!result.access || !result.refresh) {
          authLogger.error('Verify OTP returned invalid response:', result);
          return {
            success: false,
            message: 'Invalid OTP verification response',
          };
        }

        // Save identifier based on channel and clear opposite channel
        if (channel === 'phone') {
          setStoredPhone(identifier);
          clearStoredEmail();
        } else if (identifier.includes('@')) {
          setStoredEmail(identifier);
          clearStoredPhone();
        }

        // Small delay to ensure profile state is updated
        await new Promise(resolve => setTimeout(resolve, 200));

        // Track successful login
        Analytics.event(AnalyticsEvent.AUTH_LOGIN_SUCCESS, {
          category: AnalyticsCategory.AUTH,
          label: channel || 'email',
        });

        // Set user ID for future tracking
        if (result.user?.id) {
          Analytics.setUser(String(result.user.id));
        }

        // Handle redirect logic (unless skipRedirect is true)
        if (!skipRedirect) {
          // Priority: provided redirectUrl > saved redirect > config default
          // Use hardPush for full page reload - ensures all React contexts reinitialize
          const savedRedirect = redirectManager.useAndClearRedirect();
          const finalRedirectUrl = redirectUrl || savedRedirect || config?.routes?.defaultCallback || defaultRoutes.defaultCallback;
          authLogger.info('Redirecting after auth to:', finalRedirectUrl);
          router.hardPush(finalRedirectUrl);
        }

        return {
          success: true,
          message: 'Login successful',
          user: result.user as UserProfile,
          should_prompt_2fa: result.should_prompt_2fa,
        };
      } catch (error) {
        authLogger.error('Verify OTP error:', error);

        // Track failed verification
        Analytics.event(AnalyticsEvent.AUTH_OTP_VERIFY_FAIL, {
          category: AnalyticsCategory.AUTH,
          label: channel || 'email',
        });

        return {
          success: false,
          message: 'Failed to verify OTP',
        };
      }
    },
    [setStoredEmail, setStoredPhone, clearStoredEmail, clearStoredPhone, config?.routes?.defaultCallback, accounts, router],
  );

  const refreshToken = useCallback(async (): Promise<{ success: boolean; message: string }> => {
    try {
      const refreshTokenValue = apiAccounts.getRefreshToken();
      if (!refreshTokenValue) {
        clearAuthState('refreshToken:noToken');

        // Track session expired
        Analytics.event(AnalyticsEvent.AUTH_SESSION_EXPIRED, {
          category: AnalyticsCategory.AUTH,
        });

        return {
          success: false,
          message: 'No refresh token available',
        };
      }

      await accounts.refreshToken(refreshTokenValue);

      // Track successful refresh
      Analytics.event(AnalyticsEvent.AUTH_TOKEN_REFRESH, {
        category: AnalyticsCategory.AUTH,
      });

      return {
        success: true,
        message: 'Token refreshed',
      };
    } catch (error) {
      authLogger.error('Refresh token error:', error);
      clearAuthState('refreshToken:error');

      // Track refresh failure
      Analytics.event(AnalyticsEvent.AUTH_TOKEN_REFRESH_FAIL, {
        category: AnalyticsCategory.AUTH,
      });

      return {
        success: false,
        message: 'Error refreshing token',
      };
    }
  }, [clearAuthState, accounts]);

  const logout = useCallback(async (options?: { skipConfirm?: boolean }): Promise<void> => {
    const performLogout = () => {
      // Track logout
      Analytics.event(AnalyticsEvent.AUTH_LOGOUT, {
        category: AnalyticsCategory.AUTH,
      });

      accounts.logout(); // Clear tokens and profile
      setInitialized(true);
      setIsLoading(false);

      // Use hardReplace for full page reload + replace history
      // This ensures contexts reinitialize AND back button won't return to protected page
      const authCallbackUrl = config?.routes?.defaultAuthCallback || defaultRoutes.defaultAuthCallback;
      router.hardReplace(authCallbackUrl);
    };

    // Skip confirmation if requested (e.g., after account deletion)
    if (options?.skipConfirm) {
      performLogout();
      return;
    }

    // Use config.onConfirm if provided, otherwise use a simple confirm
    if (configRef.current?.onConfirm) {
      const { confirmed } = await configRef.current.onConfirm({
        title: 'Logout',
        description: 'Are you sure you want to logout?',
        confirmationButtonText: 'Logout',
        cancellationButtonText: 'Cancel',
        color: 'error',
      });
      if (confirmed) {
        performLogout();
      }
    } else {
      // Fallback to browser confirm
      const confirmed = window.confirm('Are you sure you want to logout?');
      if (confirmed) {
        performLogout();
      }
    }
  }, [accounts, config?.routes?.defaultAuthCallback, router]);

  // Computed: Is admin user (staff or superuser)
  const isAdminUser = useMemo(() => {
    return Boolean(user?.is_staff || user?.is_superuser);
  }, [user]);

  // Profile management: Update profile data
  const updateProfile = useCallback(
    async (data: { first_name?: string; last_name?: string; username?: string }): Promise<UserProfile> => {
      const result = await accounts.partialUpdateProfile(data);
      return result as UserProfile;
    },
    [accounts]
  );

  // Profile management: Upload avatar
  const uploadAvatar = useCallback(
    async (avatar: File | Blob): Promise<UserProfile> => {
      const result = await accounts.uploadAvatar(avatar);
      return result as UserProfile;
    },
    [accounts]
  );

  // Memoized context value
  const value = useMemo<AuthContextType>(
    () => ({
      user,
      isLoading,
      // Consider authenticated if we have valid tokens, even without user profile
      isAuthenticated: apiAccounts.isAuthenticated(),
      isAdminUser,
      loadCurrentProfile,
      checkAuthAndRedirect,
      getToken: () => apiAccounts.getToken(),
      getRefreshToken: () => apiAccounts.getRefreshToken(),
      getSavedEmail: () => storedEmail,
      saveEmail: setStoredEmail,
      clearSavedEmail: clearStoredEmail,
      getSavedPhone: () => storedPhone,
      savePhone: setStoredPhone,
      clearSavedPhone: clearStoredPhone,
      requestOTP,
      verifyOTP,
      refreshToken,
      logout,
      // Redirect URL methods
      saveRedirectUrl: redirectManager.setRedirect,
      getRedirectUrl: redirectManager.getFinalRedirectUrl,
      clearRedirectUrl: redirectManager.clearRedirect,
      hasRedirectUrl: redirectManager.hasRedirect,
      // Profile management
      updateProfile,
      uploadAvatar,
    }),
    [
      user,
      isLoading,
      isAdminUser,
      loadCurrentProfile,
      checkAuthAndRedirect,
      storedEmail,
      setStoredEmail,
      clearStoredEmail,
      storedPhone,
      setStoredPhone,
      clearStoredPhone,
      requestOTP,
      verifyOTP,
      refreshToken,
      logout,
      redirectManager,
      updateProfile,
      uploadAvatar,
    ],
  );

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

// Wrapper that provides AccountsContext
export const AuthProvider: React.FC<AuthProviderProps> = ({ children, config }) => {
  return (
    <AccountsProvider>
      <AuthProviderInternal config={config}>
        {children}
      </AuthProviderInternal>
    </AccountsProvider>
  );
};

/**
 * Default auth state for SSR/outside provider
 * Returns safe defaults that indicate "not authenticated, not loading"
 */
const defaultAuthState: AuthContextType = {
  user: null,
  isLoading: false,
  isAuthenticated: false,
  isAdminUser: false,
  loadCurrentProfile: async () => {
    authLogger.warn('useAuth: loadCurrentProfile called outside AuthProvider');
  },
  checkAuthAndRedirect: async () => {
    authLogger.warn('useAuth: checkAuthAndRedirect called outside AuthProvider');
  },
  getToken: () => null,
  getRefreshToken: () => null,
  getSavedEmail: () => null,
  saveEmail: () => {
    authLogger.warn('useAuth: saveEmail called outside AuthProvider');
  },
  clearSavedEmail: () => {},
  getSavedPhone: () => null,
  savePhone: () => {
    authLogger.warn('useAuth: savePhone called outside AuthProvider');
  },
  clearSavedPhone: () => {},
  requestOTP: async () => {
    authLogger.warn('useAuth: requestOTP called outside AuthProvider');
    return { success: false, message: 'AuthProvider not available' };
  },
  verifyOTP: async () => {
    authLogger.warn('useAuth: verifyOTP called outside AuthProvider');
    return { success: false, message: 'AuthProvider not available' };
  },
  refreshToken: async () => {
    authLogger.warn('useAuth: refreshToken called outside AuthProvider');
    return { success: false, message: 'AuthProvider not available' };
  },
  logout: async (_options?: { skipConfirm?: boolean }) => {
    authLogger.warn('useAuth: logout called outside AuthProvider');
  },
  saveRedirectUrl: () => {},
  getRedirectUrl: () => null,
  clearRedirectUrl: () => {},
  hasRedirectUrl: () => false,
  updateProfile: async () => {
    authLogger.warn('useAuth: updateProfile called outside AuthProvider');
    return {} as UserProfile;
  },
  uploadAvatar: async () => {
    authLogger.warn('useAuth: uploadAvatar called outside AuthProvider');
    return {} as UserProfile;
  },
};

/**
 * Hook to access auth context
 *
 * SSR-safe: Returns default unauthenticated state when called outside AuthProvider
 * (e.g., during static page generation or server-side rendering)
 */
export const useAuth = (): AuthContextType => {
  const context = useContext(AuthContext);

  if (context === undefined) {
    // SSR or outside provider - return safe defaults
    // Log only in development to help debugging
    if (typeof window !== 'undefined' && process.env.NODE_ENV === 'development') {
      authLogger.debug('useAuth called outside AuthProvider, returning default state');
    }
    return defaultAuthState;
  }

  return context;
};

export default AuthContext; 