import React, { createContext, useContext, useEffect, useMemo, useState } from 'react'; import type { ReactNode } from 'react'; import type { Session, User } from '@supabase/supabase-js'; import { mobileSupabase } from '@/lib/supabase'; import { clearMobileSessionStorage } from '@/lib/secureSessionStorage'; import { tableNameUsers } from '@/lib/tables'; import { mobileTelemetry, trackMobileError } from '@/lib/telemetry'; export interface MobileUserProfile { user_id: string; first_name?: string; last_name?: string; email?: string; role?: string; trade_enable?: boolean; } interface MobileAuthContextValue { session: Session | null; user: User | null; profile: MobileUserProfile | null; loading: boolean; error: string | null; signIn: (email: string, password: string) => Promise<{ error?: string }>; signOut: () => Promise; invalidateSession: (reason: string) => Promise; refreshProfile: () => Promise; accessToken: string | null; } const MobileAuthContext = createContext(null); export function MobileAuthProvider({ children }: { children: ReactNode }) { const [session, setSession] = useState(null); const [user, setUser] = useState(null); const [profile, setProfile] = useState(null); const [loading, setLoading] = useState(true); const [error, setError] = useState(null); useEffect(() => { let active = true; async function bootstrap() { try { const { data } = await mobileSupabase.auth.getSession(); if (!active) { return; } setSession(data.session ?? null); setUser(data.session?.user ?? null); if (data.session?.user) { mobileTelemetry.trackEvent('info', 'auth', 'session_restored', { userId: data.session.user.id, }); await fetchProfile(data.session.user.id); } else { setLoading(false); } } catch (bootstrapError) { trackMobileError('auth', 'session_restore_failed', bootstrapError); setError(bootstrapError instanceof Error ? bootstrapError.message : 'Failed to restore session'); setLoading(false); } } void bootstrap(); const { data: authListener } = mobileSupabase.auth.onAuthStateChange((event, nextSession) => { setSession(nextSession); setUser(nextSession?.user ?? null); if (nextSession?.user) { mobileTelemetry.trackEvent('info', 'auth', 'session_changed', { userId: nextSession.user.id, }); void fetchProfile(nextSession.user.id); } else { if (event === 'SIGNED_OUT') { mobileTelemetry.trackEvent('info', 'auth', 'session_signed_out'); } setProfile(null); setLoading(false); } }); return () => { active = false; authListener.subscription.unsubscribe(); }; }, []); async function fetchProfile(userId: string) { try { const { data, error: profileError } = await mobileSupabase .from(tableNameUsers) .select('user_id,first_name,last_name,email,role,trade_enable') .eq('user_id', userId) .single(); if (profileError) { setError(profileError.message); trackMobileError('auth', 'profile_load_failed', profileError, { userId }); } else { setProfile((data || null) as MobileUserProfile | null); setError(null); } } catch (fetchError) { setError(fetchError instanceof Error ? fetchError.message : 'Failed to load profile'); trackMobileError('auth', 'profile_load_failed', fetchError, { userId }); } finally { setLoading(false); } } async function signIn(email: string, password: string) { setLoading(true); setError(null); const { error: authError } = await mobileSupabase.auth.signInWithPassword({ email, password }); if (authError) { setError(authError.message); trackMobileError('auth', 'sign_in_failed', authError, { email }); setLoading(false); return { error: authError.message }; } mobileTelemetry.trackEvent('info', 'auth', 'sign_in_succeeded', { message: email, }); return {}; } async function signOut() { setLoading(true); try { await mobileSupabase.auth.signOut(); mobileTelemetry.trackEvent('info', 'auth', 'sign_out_succeeded', { userId: user?.id, }); } finally { await clearMobileSessionStorage(); setProfile(null); setSession(null); setUser(null); setLoading(false); } } async function invalidateSession(reason: string) { setLoading(true); mobileTelemetry.trackEvent('warn', 'auth', 'session_invalidated', { message: reason, userId: user?.id, }); try { await mobileSupabase.auth.signOut({ scope: 'local' }); } catch (invalidateError) { trackMobileError('auth', 'session_invalidation_signout_failed', invalidateError); } finally { await clearMobileSessionStorage(); setProfile(null); setSession(null); setUser(null); setError(reason); setLoading(false); } } async function refreshProfile() { if (!user) { return; } setLoading(true); await fetchProfile(user.id); } const value = useMemo( () => ({ session, user, profile, loading, error, signIn, signOut, invalidateSession, refreshProfile, accessToken: session?.access_token ?? null, }), [session, user, profile, loading, error] ); return {children}; } export function useMobileAuth() { const context = useContext(MobileAuthContext); if (!context) { throw new Error('useMobileAuth must be used within a MobileAuthProvider'); } return context; }