import React, { createContext, useContext, useState, useEffect, useCallback, ReactNode } from 'react'; import { authApi, User } from '../services/api'; interface AuthContextType { user: User | null; token: string | null; isLoading: boolean; isAuthenticated: boolean; setupUser: (username: string) => Promise; updateUsername: (username: string) => Promise; logout: () => void; refreshUser: () => Promise; } const AuthContext = createContext(undefined); const TOKEN_KEY = 'acestep_token'; const USER_KEY = 'acestep_user'; export function AuthProvider({ children }: { children: ReactNode }): React.ReactElement { // Start with null - we'll auto-login from database on mount const [user, setUser] = useState(null); const [token, setToken] = useState(null); const [isLoading, setIsLoading] = useState(true); const isAuthenticated = !!user && !!token; // Auto-login on mount: Try to get existing user from database useEffect(() => { async function initAuth(): Promise { try { // First, try auto-login from database (for local single-user app) const { user: userData, token: newToken } = await authApi.auto(); setUser(userData); setToken(newToken); localStorage.setItem(TOKEN_KEY, newToken); localStorage.setItem(USER_KEY, JSON.stringify(userData)); } catch (error: unknown) { // No user in database (404) or server error - that's okay // Clear any stale localStorage data const err = error as { message?: string }; if (err.message?.startsWith('404:')) { // No user exists yet - frontend will show username setup console.log('No user in database, need to set up username'); } else { console.warn('Auto-login failed:', error); } // Clear stale data setToken(null); setUser(null); localStorage.removeItem(TOKEN_KEY); localStorage.removeItem(USER_KEY); } finally { setIsLoading(false); } } initAuth(); }, []); const setupUser = useCallback(async (username: string): Promise => { const { user: userData, token: newToken } = await authApi.setup(username); setUser(userData); setToken(newToken); localStorage.setItem(TOKEN_KEY, newToken); localStorage.setItem(USER_KEY, JSON.stringify(userData)); }, []); const updateUsername = useCallback(async (username: string): Promise => { if (!token) throw new Error('Not authenticated'); const { user: userData, token: newToken } = await authApi.updateUsername(username, token); setUser(userData); setToken(newToken); localStorage.setItem(TOKEN_KEY, newToken); localStorage.setItem(USER_KEY, JSON.stringify(userData)); }, [token]); const logout = useCallback((): void => { authApi.logout().catch(() => {}); setUser(null); setToken(null); localStorage.removeItem(TOKEN_KEY); localStorage.removeItem(USER_KEY); }, []); const refreshUser = useCallback(async (): Promise => { if (!token) return; try { const { user: userData } = await authApi.me(token); setUser(userData); localStorage.setItem(USER_KEY, JSON.stringify(userData)); } catch (error) { console.error('Failed to refresh user:', error); } }, [token]); const value: AuthContextType = { user, token, isLoading, isAuthenticated, setupUser, updateUsername, logout, refreshUser, }; return {children}; } export function useAuth(): AuthContextType { const context = useContext(AuthContext); if (context === undefined) { throw new Error('useAuth must be used within an AuthProvider'); } return context; }