// src/providers/AuthProvider.tsx
import React, { useState, useEffect, useCallback, ReactNode, useMemo } from 'react';
import { AuthContext, AuthContextType } from '../contexts/AuthContext';
import type { UserProfile } from '../types';
import * as apiClient from '../services/apiClient';
import { useApi } from '../hooks/useApi';
import { getToken, setToken, removeToken } from '../services/tokenStorage';
import { logger } from '../services/logger.client';

export const AuthProvider: React.FC<{ children: ReactNode }> = ({ children }) => {
  const [userProfile, setUserProfile] = useState<UserProfile | null>(null);
  const [authStatus, setAuthStatus] = useState<AuthContextType['authStatus']>('Determining...');
  const [isLoading, setIsLoading] = useState(true);

  // FIX: Stabilize the apiFunction passed to useApi.
  // By wrapping this in useCallback, we ensure the same function instance is passed to
  // useApi on every render. This prevents the `execute` function returned by `useApi`
  // from being recreated, which in turn breaks the infinite re-render loop in the useEffect below.
  const getProfileCallback = useCallback(() => apiClient.getAuthenticatedUserProfile(), []);

  const { execute: checkTokenApi } = useApi<UserProfile, []>(getProfileCallback);
  const { execute: fetchProfileApi } = useApi<UserProfile, []>(getProfileCallback);

  useEffect(() => {
    // This flag prevents state updates if the component unmounts or if another
    // auth operation (like login/logout) occurs before this initial check completes.
    let isMounted = true;
    logger.info('[AuthProvider-Effect] Starting initial authentication check.');

    const checkAuthToken = async () => {
      const token = getToken();
      if (token) {
        logger.info('[AuthProvider-Effect] Found auth token. Validating...');
        try {
          const fetchedProfile = await checkTokenApi();

          if (isMounted && fetchedProfile) {
            logger.info('[AuthProvider-Effect] Profile received, setting state to AUTHENTICATED.');
            setUserProfile(fetchedProfile);
            setAuthStatus('AUTHENTICATED');
          } else if (isMounted) {
            logger.warn(
              '[AuthProvider-Effect] Token was present but validation returned no profile. Signing out.',
            );
            removeToken();
            setUserProfile(null);
            setAuthStatus('SIGNED_OUT');
          }
        } catch (e: unknown) {
          // This catch block is now primarily for unexpected errors, as useApi handles API errors.
          logger.warn('Auth token validation failed. Clearing token.', { error: e });
          if (isMounted) {
            removeToken();
            setUserProfile(null);
            setAuthStatus('SIGNED_OUT');
          }
        }
      } else {
        logger.info('[AuthProvider-Effect] No auth token found. Setting state to SIGNED_OUT.');
        if (isMounted) {
          setAuthStatus('SIGNED_OUT');
        }
      }

      if (isMounted) {
        logger.info(
          '[AuthProvider-Effect] Initial auth check finished. Setting isLoading to false.',
        );
        setIsLoading(false);
      }
    };

    checkAuthToken();

    return () => {
      logger.info('[AuthProvider-Effect] Component unmounting, cleaning up.');
      isMounted = false;
    };
  }, [checkTokenApi]);

  const logout = useCallback(() => {
    logger.info('[AuthProvider-Logout] Clearing user data and auth token.');
    removeToken();
    setUserProfile(null);
    setAuthStatus('SIGNED_OUT');
  }, []);

  const login = useCallback(
    async (token: string, profileData?: UserProfile) => {
      logger.info(`[AuthProvider-Login] Attempting login.`);
      setToken(token);

      if (profileData) {
        // If profile is provided (e.g., from credential login), use it directly.
        logger.info('[AuthProvider-Login] Profile data received directly.');
        setUserProfile(profileData);
        setAuthStatus('AUTHENTICATED');
        logger.info('[AuthProvider-Login] Login successful. State set to AUTHENTICATED.', {
          user: profileData.user,
        });
      } else {
        // If no profile is provided (e.g., from OAuth or token refresh), fetch it.
        logger.info('[AuthProvider-Login] Auth token set in storage. Fetching profile...');
        try {
          const fetchedProfile = await fetchProfileApi();
          if (!fetchedProfile) {
            throw new Error('Received null or undefined profile from API.');
          }
          setUserProfile(fetchedProfile);
          setAuthStatus('AUTHENTICATED');
          logger.info('[AuthProvider-Login] Profile fetch successful. State set to AUTHENTICATED.');
        } catch (e) {
          const errorMessage = e instanceof Error ? e.message : String(e);
          logger.error('Failed to fetch user data after login. Rolling back auth state.', {
            error: errorMessage,
          });
          logout(); // Log the user out to prevent an inconsistent state.
          // Re-throw the error so the calling component can handle it (e.g., show a notification)
          throw new Error(`Login succeeded, but failed to fetch your data: ${errorMessage}`);
        }
      }
    },
    [fetchProfileApi, logout],
  );

  const updateProfile = useCallback((updatedProfileData: Partial<UserProfile>) => {
    logger.info('[AuthProvider-UpdateProfile] Updating profile state.', { updatedProfileData });
    setUserProfile((prevProfile) => {
      if (!prevProfile) return null;
      return { ...prevProfile, ...updatedProfileData };
    });
  }, []);

  const value = useMemo(
    () => ({
      userProfile,
      authStatus,
      isLoading,
      login,
      logout,
      updateProfile,
    }),
    [userProfile, authStatus, isLoading, login, logout, updateProfile],
  );

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