/**
 * Accounts Context
 *
 * Manages user authentication and profile operations using generated SWR hooks
 *
 * Features:
 * - OTP-based authentication
 * - User profile management
 * - Avatar upload
 * - Profile updates
 * - localStorage cache with TTL (1 hour)
 */

"use client";

import {
    createContext, ReactNode, useCallback, useContext, useEffect, useRef, useState
} from 'react';

// Import API instance and fetcher
import { api as apiAccounts, getAccountsProfileRetrieve } from '../../';
// Import schema
import { PatchedUserProfileUpdateRequestSchema } from '../../generated/cfg_accounts';
// Import hooks from generated API hooks
import {
    useCreateAccountsOtpRequestCreate, useCreateAccountsOtpVerifyCreate,
    useCreateAccountsProfileAvatarCreate, useCreateAccountsTokenRefreshCreate,
    usePartialUpdateAccountsProfilePartialUpdate, useUpdateAccountsProfileUpdateUpdate
} from '../../generated/cfg_accounts/_utils/hooks';
import { clearProfileCache, getCachedProfile, setCachedProfile } from '../hooks/useProfileCache';
import { authLogger } from '../utils/logger';

// Import types from generated API
import type {
  User,
  UserProfileUpdateRequest,
  PatchedUserProfileUpdateRequest,
  OTPRequestRequest,
  OTPVerifyRequest,
  OTPRequestResponse,
  OTPVerifyResponse,
  TokenRefresh,
  API,
} from '../../generated/cfg_accounts';
// Re-export schemas for external use
export { PatchedUserProfileUpdateRequestSchema };
export type { PatchedUserProfileUpdateRequest };

// ─────────────────────────────────────────────────────────────────────────
// Context Type
// ─────────────────────────────────────────────────────────────────────────

export interface AccountsContextValue {
  // Current user profile
  profile?: User;
  isLoadingProfile: boolean;
  profileError: Error | null;

  // Profile operations
  updateProfile: (data: UserProfileUpdateRequest) => Promise<User>;
  partialUpdateProfile: (data: PatchedUserProfileUpdateRequest) => Promise<User>;
  uploadAvatar: (avatar: File | Blob) => Promise<User>;
  refreshProfile: (options?: { callerId?: string; force?: boolean }) => Promise<User | undefined>;

  // Authentication
  requestOTP: (data: OTPRequestRequest) => Promise<OTPRequestResponse>;
  verifyOTP: (data: OTPVerifyRequest) => Promise<OTPVerifyResponse>;
  refreshToken: (refresh: string) => Promise<TokenRefresh>;
  logout: () => void;
}

// ─────────────────────────────────────────────────────────────────────────
// Context
// ─────────────────────────────────────────────────────────────────────────

const AccountsContext = createContext<AccountsContextValue | undefined>(undefined);

// ─────────────────────────────────────────────────────────────────────────
// Provider Component
// ─────────────────────────────────────────────────────────────────────────

interface AccountsProviderProps {
  children: ReactNode;
}

export function AccountsProvider({ children }: AccountsProviderProps) {
  // State management with localStorage cache
  const [profile, setProfile] = useState<User | undefined>(() => {
    // Initialize from cache on mount
    const cached = getCachedProfile();
    return cached || undefined;
  });
  const [isLoadingProfile, setIsLoadingProfile] = useState(false);
  const [profileError, setProfileError] = useState<Error | null>(null);

  // Use refs to access current state without adding to dependencies
  const profileRef = useRef<User | undefined>(profile);
  const isLoadingRef = useRef(false);

  // Keep refs in sync with state
  useEffect(() => {
    profileRef.current = profile;
  }, [profile]);

  useEffect(() => {
    isLoadingRef.current = isLoadingProfile;
  }, [isLoadingProfile]);

  // Mutation hooks
  const updateMutation = useUpdateAccountsProfileUpdateUpdate();
  const partialUpdateMutation = usePartialUpdateAccountsProfilePartialUpdate();
  const avatarMutation = useCreateAccountsProfileAvatarCreate();
  const otpRequestMutation = useCreateAccountsOtpRequestCreate();
  const otpVerifyMutation = useCreateAccountsOtpVerifyCreate();
  const tokenRefreshMutation = useCreateAccountsTokenRefreshCreate();

  // Refresh profile - fetch from API and update cache
  const refreshProfile = useCallback(async (options?: { callerId?: string; force?: boolean }): Promise<User | undefined> => {
    const { callerId, force } = options || {};
    const currentLoading = isLoadingRef.current;

    // Prevent duplicate concurrent calls
    if (currentLoading) {
      authLogger.debug(`Profile loading in progress, skipping (caller: ${callerId})`);
      return profileRef.current;
    }

    setIsLoadingProfile(true);
    isLoadingRef.current = true;
    setProfileError(null);
    try {
      authLogger.debug(`Fetching profile from API (caller: ${callerId}, force: ${force})`);
      const result = await getAccountsProfileRetrieve(apiAccounts);
      setProfile(result);
      profileRef.current = result;
      setCachedProfile(result);
      return result;
    } catch (error) {
      const err = error instanceof Error ? error : new Error('Failed to fetch profile');
      setProfileError(err);
      throw err;
    } finally {
      setIsLoadingProfile(false);
      isLoadingRef.current = false;
    }
  }, []);

  // Update profile (full) - then reload from API
  const updateProfile = async (data: UserProfileUpdateRequest): Promise<User> => {
    await updateMutation(data, apiAccounts);
    const user = await refreshProfile({ callerId: 'updateProfile', force: true });
    return user as User;
  };

  // Partial update profile - then reload from API
  const partialUpdateProfile = async (data: PatchedUserProfileUpdateRequest): Promise<User> => {
    await partialUpdateMutation(data, apiAccounts);
    const user = await refreshProfile({ callerId: 'partialUpdateProfile', force: true });
    return user as User;
  };

  // Upload avatar - then reload from API
  const uploadAvatar = async (avatar: File | Blob): Promise<User> => {
    await avatarMutation({ avatar }, apiAccounts);
    const user = await refreshProfile({ callerId: 'uploadAvatar', force: true });
    return user as User;
  };

  // Request OTP
  const requestOTP = async (data: OTPRequestRequest): Promise<OTPRequestResponse> => {
    const result = await otpRequestMutation(data, apiAccounts);
    return result as OTPRequestResponse;
  };

  // Verify OTP
  const verifyOTP = async (data: OTPVerifyRequest): Promise<OTPVerifyResponse> => {
    const result = await otpVerifyMutation(data, apiAccounts);

    // Check if 2FA is required - don't save tokens yet
    if (result.requires_2fa && result.session_id) {
      authLogger.info('2FA required, session:', result.session_id);
      // Return result without saving tokens - caller handles 2FA step
      return result as OTPVerifyResponse;
    }

    // Normal flow - save tokens after successful verification
    if (result.access && result.refresh) {
      apiAccounts.setToken(result.access, result.refresh);
      // Refresh profile to load user data with new token
      await refreshProfile({ callerId: 'verifyOTP', force: true });
    }

    return result as OTPVerifyResponse;
  };

  // Refresh token
  const refreshToken = async (refresh: string): Promise<TokenRefresh> => {
    const result = await tokenRefreshMutation({ refresh }, apiAccounts);

    // Automatically save new access token
    if (result.access) {
      apiAccounts.setToken(result.access, refresh);
    }

    return result as TokenRefresh;
  };

  // Logout - clear tokens, profile state, and cache
  const logout = useCallback(() => {
    apiAccounts.clearTokens();
    setProfile(undefined);
    setProfileError(null);
    clearProfileCache();
  }, []);

  const value: AccountsContextValue = {
    profile,
    isLoadingProfile,
    profileError,
    updateProfile,
    partialUpdateProfile,
    uploadAvatar,
    refreshProfile,
    requestOTP,
    verifyOTP,
    refreshToken,
    logout,
  };

  return (
    <AccountsContext.Provider value={value}>
      {children}
    </AccountsContext.Provider>
  );
}

// ─────────────────────────────────────────────────────────────────────────
// Hook
// ─────────────────────────────────────────────────────────────────────────

export function useAccountsContext(): AccountsContextValue {
  const context = useContext(AccountsContext);
  if (!context) {
    throw new Error('useAccountsContext must be used within AccountsProvider');
  }
  return context;
}
