import { create } from 'zustand' import { persist } from 'zustand/middleware' import type { User, Token, UserCreate, UserLogin, Account, SubscriptionDetails } from '@/types' import { authApi } from '@/api/auth' import { apiClient } from '@/api/client' import { clearCachedQuota } from '@/hooks/useCachedQuota' interface AuthState { user: User | null token: Token | null account: Account | null subscription: SubscriptionDetails | null isAuthenticated: boolean isLoading: boolean error: string | null // Actions login: (credentials: UserLogin) => Promise register: (data: UserCreate) => Promise logout: () => Promise fetchUser: () => Promise setTokens: (token: Token) => void clearError: () => void setLoading: (loading: boolean) => void } export const useAuthStore = create()( persist( (set, get) => ({ user: null, token: null, account: null, subscription: null, isAuthenticated: false, isLoading: false, error: null, login: async (credentials: UserLogin) => { set({ isLoading: true, error: null }) try { const token = await authApi.login(credentials) // Store tokens localStorage.setItem('access_token', token.access_token) localStorage.setItem('refresh_token', token.refresh_token) set({ token, isAuthenticated: true }) // Fetch user info await get().fetchUser() } catch (error: unknown) { const axiosErr = error as { response?: { data?: { detail?: unknown } } } const rawDetail = axiosErr.response?.data?.detail const message = (typeof rawDetail === 'string' ? rawDetail : null) || (error instanceof Error ? error.message : 'Login failed') set({ error: message, isLoading: false }) throw error } }, register: async (data: UserCreate) => { set({ isLoading: true, error: null }) try { await authApi.register(data) // After registration, log the user in await get().login({ email: data.email, password: data.password }) } catch (error: unknown) { const axiosErr = error as { response?: { data?: { detail?: unknown } } } const rawDetail = axiosErr.response?.data?.detail const message = (typeof rawDetail === 'string' ? rawDetail : null) || (error instanceof Error ? error.message : 'Registration failed') set({ error: message, isLoading: false }) throw error } }, logout: async () => { try { await authApi.logout() } catch { // Ignore logout errors } finally { localStorage.removeItem('access_token') localStorage.removeItem('refresh_token') clearCachedQuota() set({ user: null, token: null, account: null, subscription: null, isAuthenticated: false, error: null }) } }, fetchUser: async () => { set({ isLoading: true }) try { const [userResult, accountResult, subscriptionResult] = await Promise.allSettled([ authApi.me(), apiClient.get('/accounts/me').then(r => r.data), apiClient.get('/accounts/me/subscription').then(r => r.data), ]) const user = userResult.status === 'fulfilled' ? userResult.value : null const account = accountResult.status === 'fulfilled' ? accountResult.value : null const subscription = subscriptionResult.status === 'fulfilled' ? subscriptionResult.value : null if (!user) { // User fetch failed — propagate the error const reason = userResult.status === 'rejected' ? userResult.reason : new Error('Failed to fetch user') throw reason } set({ user, account, subscription, isLoading: false }) } catch (error: unknown) { const message = error instanceof Error ? error.message : 'Failed to fetch user' set({ error: message, isLoading: false }) throw error } }, setTokens: (token: Token) => set({ token }), clearError: () => set({ error: null }), setLoading: (loading: boolean) => set({ isLoading: loading }), }), { name: 'auth-storage', partialize: (state) => ({ token: state.token, isAuthenticated: state.isAuthenticated, account: state.account, subscription: state.subscription, }), } ) ) export default useAuthStore