'use client';

/**
 * Authentication Context Provider
 * 
 * Provides authentication state and methods to the entire application.
 * Handles token refresh, session management, and user state.
 */

import React, { createContext, useContext, useEffect, useState, useCallback, useRef } from 'react';
import {
  User,
  AuthContextType,
  LoginCredentials,
  RegisterData,
  UpdateProfileData,
  ChangePasswordData,
} from '@/lib/types/auth';
import {
  authService,
  oauthService,
  mfaService,
  tokenStorage,
  getTimeUntilExpiry,
  isRefreshTokenExpired,
  OAuthProvider,
} from '@/lib/services/auth';

// Create the context
const AuthContext = createContext<AuthContextType | undefined>(undefined);

// Token refresh interval (check every minute)
const TOKEN_CHECK_INTERVAL = 60 * 1000;

// Refresh tokens 5 minutes before expiry
const REFRESH_BUFFER = 5 * 60 * 1000;

interface AuthProviderProps {
  children: React.ReactNode;
}

export function AuthProvider({ children }: AuthProviderProps) {
  const [user, setUser] = useState<User | null>(null);
  const [isLoading, setIsLoading] = useState(true);
  const [isInitialized, setIsInitialized] = useState(false);
  const [error, setError] = useState<string | null>(null);
  
  const refreshIntervalRef = useRef<NodeJS.Timeout | null>(null);
  const isRefreshingRef = useRef(false);

  /**
   * Check if we need to refresh the access token
   */
  const shouldRefreshToken = useCallback((): boolean => {
    const timeUntilExpiry = getTimeUntilExpiry();
    
    if (timeUntilExpiry === null) {
      return false;
    }
    
    // Refresh if token expires in less than 5 minutes
    return timeUntilExpiry < REFRESH_BUFFER;
  }, []);

  /**
   * Refresh the access token
   */
  const refreshToken = useCallback(async () => {
    // Prevent multiple simultaneous refresh attempts
    if (isRefreshingRef.current) {
      return;
    }

    // Check if refresh token is still valid
    if (isRefreshTokenExpired()) {
      console.log('Refresh token expired, logging out');
      await logout();
      return;
    }

    try {
      isRefreshingRef.current = true;
      await authService.refreshAccessToken();
      console.log('Access token refreshed successfully');
    } catch (error) {
      console.error('Failed to refresh token:', error);
      // If refresh fails, log out the user
      await logout();
    } finally {
      isRefreshingRef.current = false;
    }
  }, []);

  /**
   * Start the token refresh interval
   */
  const startTokenRefreshInterval = useCallback(() => {
    // Clear existing interval
    if (refreshIntervalRef.current) {
      clearInterval(refreshIntervalRef.current);
    }

    // Check token every minute
    refreshIntervalRef.current = setInterval(() => {
      if (shouldRefreshToken()) {
        refreshToken();
      }
    }, TOKEN_CHECK_INTERVAL);
  }, [shouldRefreshToken, refreshToken]);

  /**
   * Stop the token refresh interval
   */
  const stopTokenRefreshInterval = useCallback(() => {
    if (refreshIntervalRef.current) {
      clearInterval(refreshIntervalRef.current);
      refreshIntervalRef.current = null;
    }
  }, []);

  /**
   * Check authentication status and load user data
   */
  const checkAuth = useCallback(async () => {
    try {
      setIsLoading(true);
      setError(null);

      // Check if we have a valid access token
      if (!tokenStorage.hasValidAccessToken()) {
        // Try to refresh if we have a refresh token
        if (!isRefreshTokenExpired()) {
          await refreshToken();
        } else {
          // No valid tokens, user is not authenticated
          setUser(null);
          setIsLoading(false);
          setIsInitialized(true);
          return;
        }
      }

      // Fetch current user data
      const userData = await authService.getCurrentUser();
      setUser(userData);
      
      // Start token refresh interval
      startTokenRefreshInterval();
    } catch (error) {
      console.error('Auth check failed:', error);
      setUser(null);
      setError('Failed to verify authentication');
    } finally {
      setIsLoading(false);
      setIsInitialized(true);
    }
  }, [refreshToken, startTokenRefreshInterval]);

  /**
   * Login with email and password
   */
  const login = useCallback(async (credentials: LoginCredentials) => {
    try {
      setIsLoading(true);
      setError(null);

      await authService.login(credentials);
      
      // Fetch user data after successful login
      const userData = await authService.getCurrentUser();
      setUser(userData);
      
      // Start token refresh interval
      startTokenRefreshInterval();
    } catch (error: any) {
      const errorMessage = error.response?.data?.detail || error.message || 'Login failed';
      setError(errorMessage);
      throw error;
    } finally {
      setIsLoading(false);
    }
  }, [startTokenRefreshInterval]);

  /**
   * Register a new user
   */
  const register = useCallback(async (data: RegisterData) => {
    try {
      setIsLoading(true);
      setError(null);

      await authService.register(data);
      
      // Note: Django doesn't auto-login on registration
      // User needs to login after registration
    } catch (error: any) {
      const errorMessage = error.response?.data?.detail || error.message || 'Registration failed';
      setError(errorMessage);
      throw error;
    } finally {
      setIsLoading(false);
    }
  }, []);

  /**
   * Logout
   */
  const logout = useCallback(async () => {
    try {
      setIsLoading(true);
      await authService.logout();
    } catch (error) {
      console.error('Logout error:', error);
    } finally {
      setUser(null);
      stopTokenRefreshInterval();
      setIsLoading(false);
    }
  }, [stopTokenRefreshInterval]);

  /**
   * Update user profile
   */
  const updateProfile = useCallback(async (data: UpdateProfileData) => {
    try {
      setIsLoading(true);
      setError(null);

      const updatedUser = await authService.updateProfile(data);
      setUser(updatedUser);
    } catch (error: any) {
      const errorMessage = error.response?.data?.detail || error.message || 'Profile update failed';
      setError(errorMessage);
      throw error;
    } finally {
      setIsLoading(false);
    }
  }, []);

  /**
   * Change password
   */
  const changePassword = useCallback(async (data: ChangePasswordData) => {
    try {
      setIsLoading(true);
      setError(null);

      await authService.changePassword(data);
    } catch (error: any) {
      const errorMessage = error.response?.data?.detail || error.message || 'Password change failed';
      setError(errorMessage);
      throw error;
    } finally {
      setIsLoading(false);
    }
  }, []);

  /**
   * Request password reset
   */
  const requestPasswordReset = useCallback(async (email: string) => {
    try {
      setIsLoading(true);
      setError(null);

      await authService.requestPasswordReset(email);
    } catch (error: any) {
      const errorMessage = error.response?.data?.detail || error.message || 'Password reset request failed';
      setError(errorMessage);
      throw error;
    } finally {
      setIsLoading(false);
    }
  }, []);

  // Initialize authentication on mount
  useEffect(() => {
    checkAuth();

    // Cleanup on unmount
    return () => {
      stopTokenRefreshInterval();
    };
  }, [checkAuth, stopTokenRefreshInterval]);

  const value: AuthContextType = {
    user,
    isAuthenticated: !!user,
    isLoading,
    isInitialized,
    error,
    login,
    register,
    logout,
    refreshToken,
    updateProfile,
    changePassword,
    requestPasswordReset,
    checkAuth,
  };

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

/**
 * Hook to use auth context
 */
export function useAuth() {
  const context = useContext(AuthContext);
  
  if (context === undefined) {
    throw new Error('useAuth must be used within an AuthProvider');
  }
  
  return context;
}
