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

/**
 * AuthProvider component that manages authentication state.
 *
 * Refactored to use TanStack Query (ADR-0005 Phase 7).
 */
export const AuthProvider: React.FC<{ children: ReactNode }> = ({ children }) => {
  const queryClient = useQueryClient();
  const [userProfile, setUserProfile] = useState<UserProfile | null>(null);
  const [authStatus, setAuthStatus] = useState<AuthContextType['authStatus']>('Determining...');
  const [isLoading, setIsLoading] = useState(true);

  // Use TanStack Query to fetch the authenticated user's profile
  const {
    data: fetchedProfile,
    isLoading: isQueryLoading,
    isError,
    isFetched,
  } = useAuthProfileQuery();

  // Effect to sync query result with auth state
  useEffect(() => {
    // Only process once the query has completed at least once
    if (!isFetched && isQueryLoading) {
      return;
    }

    const token = getToken();

    if (fetchedProfile) {
      logger.info('[AuthProvider] Profile received from query, setting state to AUTHENTICATED.');
      setUserProfile(fetchedProfile);
      setAuthStatus('AUTHENTICATED');
    } else if (token && isError) {
      logger.warn('[AuthProvider] Token was present but validation failed. Signing out.');
      removeToken();
      setUserProfile(null);
      setAuthStatus('SIGNED_OUT');
    } else if (token && isFetched && !fetchedProfile) {
      // Token exists, query completed, but profile is null - sign out
      logger.warn('[AuthProvider] Token was present but profile is null. Signing out.');
      removeToken();
      setUserProfile(null);
      setAuthStatus('SIGNED_OUT');
    } else if (!token) {
      logger.info('[AuthProvider] No auth token found. Setting state to SIGNED_OUT.');
      setAuthStatus('SIGNED_OUT');
    }

    setIsLoading(false);
  }, [fetchedProfile, isQueryLoading, isError, isFetched]);

  const logout = useCallback(() => {
    logger.info('[AuthProvider-Logout] Clearing user data and auth token.');
    removeToken();
    setUserProfile(null);
    setAuthStatus('SIGNED_OUT');
    // Clear the auth profile cache on logout
    queryClient.removeQueries({ queryKey: AUTH_PROFILE_QUERY_KEY });
  }, [queryClient]);

  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');
        // Update the query cache with the provided profile
        queryClient.setQueryData(AUTH_PROFILE_QUERY_KEY, profileData);
        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 {
          // Directly fetch the profile (not using the query hook since we need immediate results)
          const response = await apiClient.getAuthenticatedUserProfile();
          if (!response.ok) {
            const error = await response.json().catch(() => ({
              message: `Request failed with status ${response.status}`,
            }));
            throw new Error(error.message || 'Failed to fetch profile');
          }
          const fetchedProfileData: UserProfile = await response.json();

          if (!fetchedProfileData) {
            throw new Error('Received null or undefined profile from API.');
          }
          setUserProfile(fetchedProfileData);
          setAuthStatus('AUTHENTICATED');
          // Update the query cache with the fetched profile
          queryClient.setQueryData(AUTH_PROFILE_QUERY_KEY, fetchedProfileData);
          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}`);
        }
      }
    },
    [logout, queryClient],
  );

  const updateProfile = useCallback(
    (updatedProfileData: Partial<UserProfile>) => {
      logger.info('[AuthProvider-UpdateProfile] Updating profile state.', { updatedProfileData });
      setUserProfile((prevProfile) => {
        if (!prevProfile) return null;
        const newProfile = { ...prevProfile, ...updatedProfileData };
        // Keep the query cache in sync
        queryClient.setQueryData(AUTH_PROFILE_QUERY_KEY, newProfile);
        return newProfile;
      });
    },
    [queryClient],
  );

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

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