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

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


interface AuthState {
  user: Types.User | null;
  guest: Types.Guest | null;
  isAuthenticated: 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;
}

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

const TOKEN_STORAGE = {
  ACCESS_TOKEN: 'accessToken',
  REFRESH_TOKEN: 'refreshToken',
  USER_DATA: 'userData',
  TOKEN_EXPIRY: 'tokenExpiry',
  GUEST_DATA: 'guestData',
  PENDING_VERIFICATION_EMAIL: 'pendingVerificationEmail'
} as const;

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

function parseJwtPayload(token: string): any {
  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);
}

// ============================
// Storage Utilities with Date Conversion
// ============================

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);
}

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 storeAuthData(authResponse: Types.AuthResponse): 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());
}

function getStoredAuthData(): {
  accessToken: string | null;
  refreshToken: string | null;
  userData: Types.User | null;
  expiresAt: number | null;
} {
  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);
  
  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 };
}

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);
  }
}

// ============================
// Guest Session Utilities
// ============================

function createGuestSession(): Types.Guest {
  const sessionId = `guest_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
  const guest: Types.Guest = {
    sessionId,
    createdAt: new Date(),
    lastActivity: new Date(),
    ipAddress: 'unknown',
    userAgent: navigator.userAgent
  };
  
  try {
    localStorage.setItem(TOKEN_STORAGE.GUEST_DATA, JSON.stringify(formatApiRequest(guest)));
  } catch (error) {
    console.error('Failed to store guest session:', error);
  }
  
  return guest;
}

function getStoredGuestData(): Types.Guest | null {
  try {
    const guestDataStr = localStorage.getItem(TOKEN_STORAGE.GUEST_DATA);
    if (guestDataStr) {
      return toCamelCase<Types.Guest>(JSON.parse(guestDataStr));
    }
  } catch (error) {
    console.error('Failed to parse stored guest data:', error);
    localStorage.removeItem(TOKEN_STORAGE.GUEST_DATA);
  }
  return null;
}

// ============================
// Main Authentication Hook
// ============================

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

  const [apiClient] = useState(() => new ApiClient());
  const initializationCompleted = 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]);

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

    try {
      // Initialize guest session first
      let guest = getStoredGuestData();
      if (!guest) {
        guest = createGuestSession();
      }

      const stored = getStoredAuthData();
      
      // If no stored tokens, user is not authenticated but has guest session
      if (!stored.accessToken || !stored.refreshToken || !stored.userData) {
        setAuthState({
          user: null,
          guest,
          isAuthenticated: false,
          isLoading: false,
          isInitializing: false,
          error: null,
          mfaResponse: null,
        });
        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) {
          storeAuthData(refreshResult);
          apiClient.setAuthToken(refreshResult.accessToken);
          
          setAuthState({
            user: refreshResult.user,
            guest,
            isAuthenticated: true,
            isLoading: false,
            isInitializing: false,
            error: null,
            mfaResponse: null
          });
          
          console.log('Token refreshed successfully');
        } else {
          console.log('Token refresh failed, clearing stored auth');
          clearStoredAuth();
          apiClient.clearAuthToken();
          
          setAuthState({
            user: null,
            guest,
            isAuthenticated: false,
            isLoading: false,
            isInitializing: false,
            error: null,
            mfaResponse: null
          });
        }
      } else {
        // Access token is still valid
        apiClient.setAuthToken(stored.accessToken);
        
        setAuthState({
          user: stored.userData,
          guest,
          isAuthenticated: true,
          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();
      
      const guest = createGuestSession();
      setAuthState({
        user: null,
        guest,
        isAuthenticated: false,
        isLoading: false,
        isInitializing: false,
        error: null,
        mfaResponse: null
      });
    } finally {
      initializationCompleted.current = true;
    }
  }, [apiClient, refreshAccessToken]);

  // 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(() => {
      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, mfaData: 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
        const authResponse: Types.AuthResponse = result;
        storeAuthData(authResponse);
        apiClient.setAuthToken(authResponse.accessToken);

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

        console.log('Login successful');
        return true;
      }
    } catch (error: any) {
      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]);

  // 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);
        apiClient.setAuthToken(authResponse.accessToken);

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

        console.log('MFA verification successful');
        return true;
      }

      return false;
    } catch (error) {
      const errorMessage = error instanceof Error ? error.message : 'MFA verification failed';
      console.log(errorMessage);
      setAuthState(prev => ({
        ...prev,
        isLoading: false,
        error: errorMessage
      }));
      return false;
    }
  }, [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
    }));
  }, []);

  // Email verification
  const verifyEmail = useCallback(async (verificationData: EmailVerificationRequest): Promise<{ message: string; userType: string } | 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]);

  // Resend email verification
  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]);

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

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

  const logout = useCallback(() => {
    clearStoredAuth();
    apiClient.clearAuthToken();
    
    // Create new guest session after logout
    const guest = createGuestSession();
    
    setAuthState(prev => ({
      ...prev,
      user: null,
      guest,
      isAuthenticated: false,
      isLoading: false,
      error: null,
      mfaResponse: null,
    }));
    
    console.log('User logged out');
  }, [apiClient]);

  const updateUserData = useCallback((updatedUser: Types.User) => {
    updateStoredUserData(updatedUser);
    setAuthState(prev => ({
      ...prev,
      user: updatedUser
    }));
    console.log('User data updated', updatedUser);
  }, []);

  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);
      
      // Store email for potential verification resend
      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) {
      storeAuthData(refreshResult);
      apiClient.setAuthToken(refreshResult.accessToken);
      
      setAuthState(prev => ({
        ...prev,
        user: refreshResult.user,
        isAuthenticated: true,
        isLoading: false,
        error: null
      }));
      
      return true;
    } else {
      logout();
      return false;
    }
  }, [refreshAccessToken, logout]);

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

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

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

function AuthProvider({ children }: { children: React.ReactNode }) {
  const auth = useAuthenticationLogic();
  
  return (
    <AuthContext.Provider value={auth}>
      {children}
    </AuthContext.Provider>
  );
}

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

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

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

function ProtectedRoute({ 
  children, 
  fallback = <div>Please log in to access this page.</div>,
  requiredUserType 
}: ProtectedRouteProps) {
  const { isAuthenticated, isInitializing, user } = useAuth();
  
  // Show loading while checking stored tokens
  if (isInitializing) {
    return <div>Loading...</div>;
  }
  
  // Not authenticated
  if (!isAuthenticated) {
    return <>{fallback}</>;
  }
  
  // Check user type if required
  if (requiredUserType && user?.userType !== requiredUserType) {
    return <div>Access denied. Required user type: {requiredUserType}</div>;
  }
  
  return <>{children}</>;
}

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

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

export {
  useAuthenticationLogic, AuthProvider, useAuth, ProtectedRoute
}