// Replace the existing AuthContext.tsx with these enhancements

import React, {
  createContext,
  useContext,
  useState,
  useCallback,
  useEffect,
  useRef,
  JSX,
} from 'react';
import * as Types from '../types/types';
import { ApiClient, CreateEmployerRequest, GuestConversionRequest } from 'services/api-client';
import { formatApiRequest, toCamelCase } from 'types/conversion';

// ============================
// Enhanced Types and Interfaces
// ============================

interface AuthState {
  user: Types.User | null;
  guest: Types.Guest | null;
  isAuthenticated: boolean;
  isGuest: boolean;
  isLoading: boolean;
  isInitializing: boolean;
  error: string | null;
  mfaResponse: Types.MFARequestResponse | null;
}

interface LoginRequest {
  login: string; // email or username
  password: string;
}

interface EmailVerificationRequest {
  token: string;
}

interface ResendVerificationRequest {
  email: string;
}

interface PasswordResetRequest {
  email: string;
}

// ============================
// Enhanced Token Storage Constants
// ============================

const TOKEN_STORAGE = {
  ACCESS_TOKEN: 'accessToken',
  REFRESH_TOKEN: 'refreshToken',
  USER_DATA: 'userData',
  TOKEN_EXPIRY: 'tokenExpiry',
  USER_TYPE: 'userType',
  IS_GUEST: 'isGuest',
  PENDING_VERIFICATION_EMAIL: 'pendingVerificationEmail',
} as const;

// ============================
// JWT Utilities
// ============================

type JwtPayload = {
  exp?: number; // Expiry time in seconds
};

function parseJwtPayload(token: string): JwtPayload | null {
  try {
    const base64Url = token.split('.')[1];
    const base64 = base64Url.replace(/-/g, '+').replace(/_/g, '/');
    const jsonPayload = decodeURIComponent(
      atob(base64)
        .split('')
        .map(c => '%' + ('00' + c.charCodeAt(0).toString(16)).slice(-2))
        .join('')
    );
    return JSON.parse(jsonPayload);
  } catch (error) {
    console.error('Failed to parse JWT token:', error);
    return null;
  }
}

function isTokenExpired(token: string): boolean {
  const payload = parseJwtPayload(token);
  if (!payload || !payload.exp) {
    return true;
  }

  // Check if token expires within the next 5 minutes (buffer time)
  const expiryTime = payload.exp * 1000; // Convert to milliseconds
  const bufferTime = 5 * 60 * 1000; // 5 minutes
  const currentTime = Date.now();

  return currentTime >= expiryTime - bufferTime;
}

// ============================
// Enhanced Storage Utilities
// ============================

function clearStoredAuth(): void {
  localStorage.removeItem(TOKEN_STORAGE.ACCESS_TOKEN);
  localStorage.removeItem(TOKEN_STORAGE.REFRESH_TOKEN);
  localStorage.removeItem(TOKEN_STORAGE.USER_DATA);
  localStorage.removeItem(TOKEN_STORAGE.TOKEN_EXPIRY);
  localStorage.removeItem(TOKEN_STORAGE.USER_TYPE);
  localStorage.removeItem(TOKEN_STORAGE.IS_GUEST);
}

function prepareUserDataForStorage(user: Types.User): string {
  try {
    // Convert dates to ISO strings for storage
    const userForStorage = formatApiRequest(user);
    return JSON.stringify(userForStorage);
  } catch (error) {
    console.error('Failed to prepare user data for storage:', error);
    return JSON.stringify(user); // Fallback to direct serialization
  }
}

function parseStoredUserData(userDataStr: string): Types.User | null {
  try {
    const rawUserData = JSON.parse(userDataStr);

    // Convert the data using toCamelCase which handles date conversion
    const convertedData = toCamelCase<Types.User>(rawUserData);

    return convertedData;
  } catch (error) {
    console.error('Failed to parse stored user data:', error);
    return null;
  }
}

function updateStoredUserData(user: Types.User): void {
  try {
    localStorage.setItem(TOKEN_STORAGE.USER_DATA, prepareUserDataForStorage(user));
  } catch (error) {
    console.error('Failed to update stored user data:', error);
  }
}

function storeAuthData(authResponse: Types.AuthResponse, isGuest = false): void {
  localStorage.setItem(TOKEN_STORAGE.ACCESS_TOKEN, authResponse.accessToken);
  localStorage.setItem(TOKEN_STORAGE.REFRESH_TOKEN, authResponse.refreshToken);
  localStorage.setItem(TOKEN_STORAGE.USER_DATA, prepareUserDataForStorage(authResponse.user));
  localStorage.setItem(TOKEN_STORAGE.TOKEN_EXPIRY, authResponse.expiresAt.toString());
  localStorage.setItem(TOKEN_STORAGE.USER_TYPE, authResponse.user.userType);
  localStorage.setItem(TOKEN_STORAGE.IS_GUEST, isGuest.toString());
}

function getStoredAuthData(): {
  accessToken: string | null;
  refreshToken: string | null;
  userData: Types.User | null;
  expiresAt: number | null;
  userType: string | null;
  isGuest: boolean;
} {
  const accessToken = localStorage.getItem(TOKEN_STORAGE.ACCESS_TOKEN);
  const refreshToken = localStorage.getItem(TOKEN_STORAGE.REFRESH_TOKEN);
  const userDataStr = localStorage.getItem(TOKEN_STORAGE.USER_DATA);
  const expiryStr = localStorage.getItem(TOKEN_STORAGE.TOKEN_EXPIRY);
  const userType = localStorage.getItem(TOKEN_STORAGE.USER_TYPE);
  const isGuestStr = localStorage.getItem(TOKEN_STORAGE.IS_GUEST);

  let userData: Types.User | null = null;
  let expiresAt: number | null = null;

  try {
    if (userDataStr) {
      userData = parseStoredUserData(userDataStr);
    }
    if (expiryStr) {
      expiresAt = parseInt(expiryStr, 10);
    }
  } catch (error) {
    console.error('Failed to parse stored auth data:', error);
    clearStoredAuth();
  }

  return {
    accessToken,
    refreshToken,
    userData,
    expiresAt,
    userType,
    isGuest: isGuestStr === 'true',
  };
}

type EmailVerificationResponse = {
  message: string;
  userType: string;
};

interface AuthenticationLogic extends AuthState {
  apiClient: ApiClient;
  login: (loginData: LoginRequest) => Promise<boolean>;
  logout: () => Promise<void>;
  verifyMFA: (mfaData: Types.MFAVerifyRequest) => Promise<boolean>;
  resendMFACode: (email: string, deviceId: string, deviceName: string) => Promise<boolean>;
  clearMFA: () => void;
  verifyEmail: (
    verificationData: EmailVerificationRequest
  ) => Promise<EmailVerificationResponse | null>;
  resendEmailVerification: (email: string) => Promise<boolean>;
  setPendingVerificationEmail: (email: string) => void;
  getPendingVerificationEmail: () => string | null;
  createEmployerAccount: (employerData: CreateEmployerRequest) => Promise<boolean>;
  requestPasswordReset: (email: string) => Promise<boolean>;
  refreshAuth: () => Promise<boolean>;
  updateUserData: (updatedUser: Types.User) => void;
  convertGuestToUser: (registrationData: GuestConversionRequest) => Promise<boolean>;
  createGuestSession: () => Promise<boolean>;
}

function useAuthenticationLogic(): AuthenticationLogic {
  const [authState, setAuthState] = useState<AuthState>({
    user: null,
    guest: null,
    isAuthenticated: false,
    isGuest: false,
    isLoading: false,
    isInitializing: true,
    error: null,
    mfaResponse: null,
  });

  const [apiClient] = useState(() => new ApiClient());
  const initializationCompleted = useRef(false);
  const guestCreationAttempted = useRef(false);

  // Token refresh function
  const refreshAccessToken = useCallback(
    async (refreshToken: string): Promise<Types.AuthResponse | null> => {
      try {
        const response = await apiClient.refreshToken(refreshToken);
        return response;
      } catch (error) {
        console.error('Token refresh failed:', error);
        return null;
      }
    },
    [apiClient]
  );

  // Create guest session
  const createGuestSession = useCallback(async (): Promise<boolean> => {
    if (guestCreationAttempted.current) {
      return false;
    }

    guestCreationAttempted.current = true;

    try {
      console.log('🔄 Creating guest session...');
      const guestAuth = await apiClient.createGuestSession();

      if (guestAuth && guestAuth.user && guestAuth.user.userType === 'guest') {
        storeAuthData(guestAuth, true);
        apiClient.setAuthToken(guestAuth.accessToken);

        setAuthState({
          user: null,
          guest: guestAuth.user as Types.Guest,
          isAuthenticated: true,
          isGuest: true,
          isLoading: false,
          isInitializing: false,
          error: null,
          mfaResponse: null,
        });

        console.log('👤 Guest session created successfully:', guestAuth.user);
        return true;
      }

      return false;
    } catch (error) {
      console.error('❌ Failed to create guest session:', error);
      guestCreationAttempted.current = false;

      // Set to unauthenticated state if guest creation fails
      setAuthState(prev => ({
        ...prev,
        user: null,
        guest: null,
        isAuthenticated: false,
        isGuest: false,
        isLoading: false,
        isInitializing: false,
        error: 'Failed to create guest session',
      }));

      return false;
    }
  }, [apiClient]);

  // Initialize authentication state
  const initializeAuth = useCallback(async () => {
    if (initializationCompleted.current) {
      return;
    }

    try {
      const stored = getStoredAuthData();

      // If no stored tokens, create guest session
      if (!stored.accessToken || !stored.refreshToken || !stored.userData) {
        console.log('🔄 No stored auth found, creating guest session...');
        await createGuestSession();
        return;
      }

      // For guests, always verify the session exists on server
      if (stored.userType === 'guest' && stored.userData) {
        console.log(stored.userData);
        try {
          // Make a quick API call to verify guest still exists
          const response = await fetch(`${apiClient.getBaseUrl()}/users/${stored.userData.id}`, {
            headers: { Authorization: `Bearer ${stored.accessToken}` },
          });

          if (!response.ok) {
            console.log('🔄 Guest session invalid, creating new guest session...');
            clearStoredAuth();
            await createGuestSession();
            return;
          }
        } catch (error) {
          console.log('🔄 Guest verification failed, creating new guest session...');
          clearStoredAuth();
          await createGuestSession();
          return;
        }
      }

      // Check if access token is expired
      if (isTokenExpired(stored.accessToken)) {
        console.log('🔄 Access token expired, attempting refresh...');

        const refreshResult = await refreshAccessToken(stored.refreshToken);

        if (refreshResult) {
          const isGuest = stored.userType === 'guest';
          storeAuthData(refreshResult, isGuest);
          apiClient.setAuthToken(refreshResult.accessToken);

          setAuthState({
            user: isGuest ? null : refreshResult.user,
            guest: isGuest ? (refreshResult.user as Types.Guest) : null,
            isAuthenticated: true,
            isGuest,
            isLoading: false,
            isInitializing: false,
            error: null,
            mfaResponse: null,
          });

          console.log('✅ Token refreshed successfully');
        } else {
          console.log('❌ Token refresh failed, creating new guest session...');
          clearStoredAuth();
          apiClient.clearAuthToken();
          await createGuestSession();
        }
      } else {
        // Access token is still valid
        apiClient.setAuthToken(stored.accessToken);
        const isGuest = stored.userType === 'guest';

        setAuthState({
          user: isGuest ? null : stored.userData,
          guest: isGuest ? (stored.userData as Types.Guest) : null,
          isAuthenticated: true,
          isGuest,
          isLoading: false,
          isInitializing: false,
          error: null,
          mfaResponse: null,
        });

        console.log('✅ Restored authentication from stored tokens');
      }
    } catch (error) {
      console.error('❌ Error initializing auth:', error);
      clearStoredAuth();
      apiClient.clearAuthToken();
      await createGuestSession();
    } finally {
      initializationCompleted.current = true;
    }
  }, [apiClient, refreshAccessToken, createGuestSession]);

  // Run initialization on mount
  useEffect(() => {
    initializeAuth();
  }, [initializeAuth]);

  // Set up automatic token refresh
  useEffect(() => {
    if (!authState.isAuthenticated) {
      return;
    }

    const stored = getStoredAuthData();
    if (!stored.expiresAt) {
      return;
    }

    const expiryTime = stored.expiresAt * 1000;
    const currentTime = Date.now();
    const timeUntilExpiry = expiryTime - currentTime - 5 * 60 * 1000; // 5 minute buffer

    if (timeUntilExpiry <= 0) {
      initializeAuth();
      return;
    }

    const refreshTimer = setTimeout((): void => {
      console.log('🔄 Auto-refreshing token before expiry...');
      initializeAuth();
    }, timeUntilExpiry);

    return () => clearTimeout(refreshTimer);
  }, [authState.isAuthenticated, initializeAuth]);

  // Enhanced login with MFA support
  const login = useCallback(
    async (loginData: LoginRequest): Promise<boolean> => {
      setAuthState(prev => ({
        ...prev,
        isLoading: true,
        error: null,
        mfaResponse: null,
      }));

      try {
        const result = await apiClient.login({
          login: loginData.login,
          password: loginData.password,
        });

        if ('mfaRequired' in result) {
          // MFA required for new device
          setAuthState(prev => ({
            ...prev,
            isLoading: false,
            mfaResponse: result,
          }));
          return false; // Login not complete yet
        } else {
          // Normal login success - convert from guest to authenticated user
          const authResponse: Types.AuthResponse = result;
          storeAuthData(authResponse, false);
          apiClient.setAuthToken(authResponse.accessToken);

          setAuthState(prev => ({
            ...prev,
            user: authResponse.user,
            guest: null,
            isAuthenticated: true,
            isGuest: false,
            isLoading: false,
            error: null,
            mfaResponse: null,
          }));

          console.log('✅ Login successful, converted from guest to authenticated user');
          return true;
        }
      } catch (error) {
        const errorMessage =
          error instanceof Error ? error.message : 'Network error occurred. Please try again.';
        setAuthState(prev => ({
          ...prev,
          isLoading: false,
          error: errorMessage,
          mfaResponse: null,
        }));
        return false;
      }
    },
    [apiClient]
  );

  // Convert guest to permanent user
  const convertGuestToUser = useCallback(
    async (registrationData: GuestConversionRequest): Promise<boolean> => {
      if (!authState.isGuest || !authState.guest) {
        throw new Error('Not currently a guest user');
      }

      setAuthState(prev => ({ ...prev, isLoading: true, error: null }));

      try {
        const result = await apiClient.convertGuestToUser(registrationData);

        // Store new authentication
        storeAuthData(result.auth, false);
        apiClient.setAuthToken(result.auth.accessToken);

        setAuthState(prev => ({
          ...prev,
          user: result.auth.user,
          guest: null,
          isAuthenticated: true,
          isGuest: false,
          isLoading: false,
          error: null,
        }));

        console.log('✅ Guest successfully converted to permanent user');
        return true;
      } catch (error) {
        const errorMessage =
          error instanceof Error ? error.message : 'Failed to convert guest account';
        setAuthState(prev => ({
          ...prev,
          isLoading: false,
          error: errorMessage,
        }));
        return false;
      }
    },
    [apiClient, authState.isGuest, authState.guest]
  );

  // MFA verification
  const verifyMFA = useCallback(
    async (mfaData: Types.MFAVerifyRequest): Promise<boolean> => {
      setAuthState(prev => ({ ...prev, isLoading: true, error: null }));

      try {
        const result = await apiClient.verifyMFA(mfaData);

        if (result.accessToken) {
          const authResponse: Types.AuthResponse = result;
          storeAuthData(authResponse, false);
          apiClient.setAuthToken(authResponse.accessToken);

          setAuthState(prev => ({
            ...prev,
            user: authResponse.user,
            guest: null,
            isAuthenticated: true,
            isGuest: false,
            isLoading: false,
            error: null,
            mfaResponse: null,
          }));

          console.log('✅ MFA verification successful, converted from guest');
          return true;
        }

        return false;
      } catch (error) {
        const errorMessage = error instanceof Error ? error.message : 'MFA verification failed';
        setAuthState(prev => ({
          ...prev,
          isLoading: false,
          error: errorMessage,
        }));
        return false;
      }
    },
    [apiClient]
  );

  // Logout - returns to guest session
  const logout = useCallback(async () => {
    try {
      // If authenticated, try to logout gracefully
      if (authState.isAuthenticated && !authState.isGuest) {
        const stored = getStoredAuthData();
        if (stored.accessToken && stored.refreshToken) {
          try {
            await apiClient.logout(stored.accessToken, stored.refreshToken);
          } catch (error) {
            console.warn('Logout request failed, proceeding with local cleanup');
          }
        }
      }
    } catch (error) {
      console.warn('Error during logout:', error);
    } finally {
      // Always clear stored auth and create new guest session
      clearStoredAuth();
      apiClient.clearAuthToken();
      guestCreationAttempted.current = false;

      // Create new guest session
      await createGuestSession();

      console.log('🔄 Logged out, created new guest session');
    }
  }, [apiClient, authState.isAuthenticated, authState.isGuest, createGuestSession]);

  // Update user data
  const updateUserData = useCallback(
    (updatedUser: Types.User) => {
      updateStoredUserData(updatedUser);
      setAuthState(prev => ({
        ...prev,
        user: authState.isGuest ? null : updatedUser,
        guest: authState.isGuest ? (updatedUser as Types.Guest) : prev.guest,
      }));
      console.log('✅ User data updated');
    },
    [authState.isGuest]
  );

  // Email verification functions (unchanged)
  const verifyEmail = useCallback(
    async (
      verificationData: EmailVerificationRequest
    ): Promise<EmailVerificationResponse | null> => {
      setAuthState(prev => ({ ...prev, isLoading: true, error: null }));

      try {
        const result = await apiClient.verifyEmail(verificationData);
        setAuthState(prev => ({ ...prev, isLoading: false }));
        return {
          message: result.message || 'Email verified successfully',
          userType: result.userType || 'user',
        };
      } catch (error) {
        const errorMessage = error instanceof Error ? error.message : 'Email verification failed';
        setAuthState(prev => ({
          ...prev,
          isLoading: false,
          error: errorMessage,
        }));
        return null;
      }
    },
    [apiClient]
  );

  // Other existing methods remain the same...
  const resendEmailVerification = useCallback(
    async (email: string): Promise<boolean> => {
      setAuthState(prev => ({ ...prev, isLoading: true, error: null }));

      try {
        await apiClient.resendVerificationEmail({ email });
        setAuthState(prev => ({ ...prev, isLoading: false }));
        return true;
      } catch (error) {
        const errorMessage =
          error instanceof Error ? error.message : 'Failed to resend verification email';
        setAuthState(prev => ({
          ...prev,
          isLoading: false,
          error: errorMessage,
        }));
        return false;
      }
    },
    [apiClient]
  );

  const setPendingVerificationEmail = useCallback((email: string) => {
    localStorage.setItem(TOKEN_STORAGE.PENDING_VERIFICATION_EMAIL, email);
  }, []);

  const getPendingVerificationEmail = useCallback((): string | null => {
    return localStorage.getItem(TOKEN_STORAGE.PENDING_VERIFICATION_EMAIL);
  }, []);

  const createEmployerAccount = useCallback(
    async (employerData: CreateEmployerRequest): Promise<boolean> => {
      setAuthState(prev => ({ ...prev, isLoading: true, error: null }));

      try {
        const employer = await apiClient.createEmployer(employerData);
        console.log('✅ Employer created:', employer);

        setPendingVerificationEmail(employerData.email);

        setAuthState(prev => ({ ...prev, isLoading: false }));
        return true;
      } catch (error) {
        const errorMessage = error instanceof Error ? error.message : 'Account creation failed';
        setAuthState(prev => ({
          ...prev,
          isLoading: false,
          error: errorMessage,
        }));
        return false;
      }
    },
    [apiClient, setPendingVerificationEmail]
  );

  const requestPasswordReset = useCallback(
    async (email: string): Promise<boolean> => {
      setAuthState(prev => ({ ...prev, isLoading: true, error: null }));

      try {
        await apiClient.requestPasswordReset({ email });
        setAuthState(prev => ({ ...prev, isLoading: false }));
        return true;
      } catch (error) {
        const errorMessage =
          error instanceof Error ? error.message : 'Password reset request failed';
        setAuthState(prev => ({
          ...prev,
          isLoading: false,
          error: errorMessage,
        }));
        return false;
      }
    },
    [apiClient]
  );

  const refreshAuth = useCallback(async (): Promise<boolean> => {
    const stored = getStoredAuthData();
    if (!stored.refreshToken) {
      return false;
    }

    setAuthState(prev => ({ ...prev, isLoading: true, error: null }));

    const refreshResult = await refreshAccessToken(stored.refreshToken);

    if (refreshResult) {
      const isGuest = stored.userType === 'guest';
      storeAuthData(refreshResult, isGuest);
      apiClient.setAuthToken(refreshResult.accessToken);

      setAuthState(prev => ({
        ...prev,
        user: isGuest ? null : refreshResult.user,
        guest: isGuest ? (refreshResult.user as Types.Guest) : null,
        isAuthenticated: true,
        isGuest,
        isLoading: false,
        error: null,
      }));

      return true;
    } else {
      await logout();
      return false;
    }
  }, [refreshAccessToken, logout, apiClient]);

  // Resend MFA code
  const resendMFACode = useCallback(
    async (email: string, deviceId: string, deviceName: string): Promise<boolean> => {
      setAuthState(prev => ({ ...prev, isLoading: true, error: null }));

      try {
        await apiClient.requestMFA({
          email,
          password: '', // This would need to be stored securely or re-entered
          deviceId,
          deviceName,
        });

        setAuthState(prev => ({ ...prev, isLoading: false }));
        return true;
      } catch (error) {
        const errorMessage = error instanceof Error ? error.message : 'Failed to resend MFA code';
        setAuthState(prev => ({
          ...prev,
          isLoading: false,
          error: errorMessage,
        }));
        return false;
      }
    },
    [apiClient]
  );

  // Clear MFA state
  const clearMFA = useCallback(() => {
    setAuthState(prev => ({
      ...prev,
      mfaResponse: null,
      error: null,
    }));
  }, []);

  return {
    ...authState,
    apiClient,
    login,
    logout,
    verifyMFA,
    resendMFACode,
    clearMFA,
    verifyEmail,
    resendEmailVerification,
    setPendingVerificationEmail,
    getPendingVerificationEmail,
    createEmployerAccount,
    requestPasswordReset,
    refreshAuth,
    updateUserData,
    convertGuestToUser,
    createGuestSession,
  };
}

// ============================
// Enhanced Context Provider
// ============================

const AuthContext = createContext<ReturnType<typeof useAuthenticationLogic> | null>(null);

function AuthProvider({ children }: { children: React.ReactNode }): JSX.Element {
  const auth = useAuthenticationLogic();

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

function useAuth(): ReturnType<typeof useAuthenticationLogic> {
  const context = useContext(AuthContext);
  if (!context) {
    throw new Error('useAuth must be used within an AuthProvider');
  }
  return context;
}

// ============================
// Enhanced Protected Route Component
// ============================

interface ProtectedRouteProps {
  children: React.ReactNode;
  fallback?: React.ReactNode;
  requiredUserType?: Types.UserType;
  allowGuests?: boolean;
}

function ProtectedRoute({
  children,
  fallback = <div>Please log in to access this page.</div>,
  requiredUserType,
  allowGuests = false,
}: ProtectedRouteProps): JSX.Element {
  const { isAuthenticated, isInitializing, user, isGuest } = useAuth();

  // Show loading while checking stored tokens
  if (isInitializing) {
    return <div>Loading...</div>;
  }

  // Not authenticated at all (shouldn't happen with guest sessions)
  if (!isAuthenticated) {
    return <>{fallback}</>;
  }

  // Guest access control
  if (isGuest && !allowGuests) {
    return <div>Please create an account or log in to access this page.</div>;
  }

  // Check user type if required (only for non-guests)
  if (requiredUserType && !isGuest && user?.userType !== requiredUserType) {
    return <div>Access denied. Required user type: {requiredUserType}</div>;
  }

  return <>{children}</>;
}

export type {
  AuthState,
  LoginRequest,
  EmailVerificationRequest,
  ResendVerificationRequest,
  PasswordResetRequest,
  GuestConversionRequest,
};

export type { CreateCandidateRequest, CreateEmployerRequest } from '../services/api-client';

export { useAuthenticationLogic, AuthProvider, useAuth, ProtectedRoute };
