"use client"; import { create } from "zustand"; import { persist } from "zustand/middleware"; import { authService } from "../services/authService"; import api from "../services/api"; import { useCartStore } from "./cartStore"; import { usePetStore } from "./usePetStore"; export type UserRole = "User_Guest" | "User_PetOwner" | "User_Partner"; export interface Address { id: string; title: string; receptorName: string; phone: string; province: string; city: string; detail: string; zipCode: string; isDefault: boolean; } export interface Transaction { id: string; type: 'top_up' | 'purchase'; amount: number; date: string; status: 'success' | 'failed' | 'pending'; } interface UserProfile { firstName: string; lastName: string; email: string; mobile: string; walletBalance: number; charityDonationTotal: number; addresses: Address[]; transactions: Transaction[]; } interface UserStore { role: UserRole; isLoggedIn: boolean; isAuthModalOpen: boolean; profile: UserProfile; setRole: (role: UserRole) => void; setLoggedIn: (isLoggedIn: boolean) => void; setAuthModalOpen: (open: boolean) => void; updateProfile: (profile: Partial) => Promise; addAddress: (address: Address) => Promise; updateAddress: (id: string, address: Address) => Promise; deleteAddress: (id: string) => Promise; setDefaultAddress: (id: string) => Promise; topUpWallet: (amount: number) => Promise; logout: () => void; fetchProfile: () => Promise; } export const useUserStore = create()( persist( (set) => ({ role: "User_Guest", isLoggedIn: false, isAuthModalOpen: false, profile: { firstName: "", lastName: "", email: "", mobile: "", walletBalance: 0, charityDonationTotal: 0, addresses: [], transactions: [], }, setRole: (role) => set({ role }), setLoggedIn: (isLoggedIn) => set({ isLoggedIn }), setAuthModalOpen: (open) => set({ isAuthModalOpen: open }), updateProfile: async (updates) => { try { const profileData = await authService.updateProfile(updates); set((state) => ({ profile: { ...state.profile, firstName: profileData.firstName, lastName: profileData.lastName, email: profileData.email, mobile: profileData.mobile || "", } })); } catch (error) { console.error("Failed to update profile:", error); throw error; } }, addAddress: async (address) => { try { // Remove ID so backend generates UUID const addressData = { title: address.title, receptorName: address.receptorName, phone: address.phone, province: address.province, city: address.city, detail: address.detail, zipCode: address.zipCode, isDefault: address.isDefault, }; await api.post('/users/addresses', addressData); await useUserStore.getState().fetchProfile(); } catch (error) { console.error("Failed to add address:", error); throw error; } }, updateAddress: async (id, updated) => { try { const addressData = { title: updated.title, receptorName: updated.receptorName, phone: updated.phone, province: updated.province, city: updated.city, detail: updated.detail, zipCode: updated.zipCode, isDefault: updated.isDefault, }; await api.patch(`/users/addresses/${id}`, addressData); await useUserStore.getState().fetchProfile(); } catch (error) { console.error("Failed to update address:", error); throw error; } }, deleteAddress: async (id) => { try { await api.delete(`/users/addresses/${id}`); await useUserStore.getState().fetchProfile(); } catch (error) { console.error("Failed to delete address:", error); throw error; } }, setDefaultAddress: async (id) => { try { await api.patch(`/users/addresses/${id}/default`); await useUserStore.getState().fetchProfile(); } catch (error) { console.error("Failed to set default address:", error); throw error; } }, topUpWallet: async (amount) => { try { await api.post('/users/wallet/top-up', { amount }); await useUserStore.getState().fetchProfile(); } catch (error) { console.error("Failed to top-up wallet:", error); throw error; } }, logout: () => { authService.logout(); try { usePetStore.getState().reset(); } catch {} try { useCartStore.getState().clearCart(); } catch {} try { localStorage.removeItem("canina-pets"); } catch {} try { localStorage.removeItem("canina-cart-storage"); } catch {} set({ role: "User_Guest", isLoggedIn: false, profile: { firstName: "", lastName: "", email: "", mobile: "", walletBalance: 0, charityDonationTotal: 0, addresses: [], transactions: [] } }); }, fetchProfile: async () => { try { const token = typeof window !== 'undefined' && window.localStorage ? localStorage.getItem('accessToken') : (globalThis as unknown as { _testToken?: string })._testToken; if (!token) { try { usePetStore.getState().reset(); } catch {} try { localStorage.removeItem("canina-pets"); } catch {} set({ isLoggedIn: false, role: "User_Guest", profile: { firstName: "", lastName: "", email: "", mobile: "", walletBalance: 0, charityDonationTotal: 0, addresses: [], transactions: [] } }); return; } const profileData = await authService.getProfile(); if (!profileData) { authService.logout(); set({ isLoggedIn: false, role: "User_Guest", profile: { firstName: "", lastName: "", email: "", mobile: "", walletBalance: 0, charityDonationTotal: 0, addresses: [], transactions: [] } }); return; } set((state) => { const pd = profileData as unknown as Record; const backendWallet = Number(profileData.walletBalance || 0); const backendCharity = Number(profileData.charityDonationTotal || 0); const backendTransactions = (pd.walletTransactions as Array<{ id: string; type: string; amount: number; createdAt: string; status: string }>)?.map((t) => ({ id: t.id, type: (t.type === 'deposit' ? 'top_up' : 'purchase') as Transaction['type'], amount: Number(t.amount), date: t.createdAt, status: (t.status === 'completed' ? 'success' : t.status === 'failed' ? 'failed' : 'pending') as Transaction['status'] })) || []; return { isLoggedIn: true, role: (profileData.role as UserRole) || "User_PetOwner", profile: { firstName: profileData.firstName, lastName: profileData.lastName, email: profileData.email, mobile: profileData.mobile || "", walletBalance: backendWallet, charityDonationTotal: backendCharity, addresses: Array.isArray(pd.addresses) && pd.addresses.length > 0 ? (pd.addresses as Address[]) : state.profile.addresses, transactions: backendTransactions.length > 0 ? backendTransactions : state.profile.transactions } }; }); // Sync orders list to CartStore while preserving local petId & address mappings if (profileData.orders) { const currentLocalOrders = useCartStore.getState().orders; const mergedOrders = (profileData.orders as Record[]).map((bo) => { const localMatch = currentLocalOrders.find(lo => lo.id === bo.id); return { ...bo, petId: bo.petId || (bo.pet as { id?: string })?.id || localMatch?.petId, shippingAddress: bo.shippingAddress || localMatch?.shippingAddress }; }); useCartStore.getState().setOrders(mergedOrders); } // Sync pets list to PetStore if (profileData.pets) { usePetStore.getState().setPets(profileData.pets as Record[]); } } catch (error) { console.error("Failed to fetch user profile:", error); authService.logout(); set({ isLoggedIn: false, role: "User_Guest", profile: { firstName: "", lastName: "", email: "", mobile: "", walletBalance: 0, charityDonationTotal: 0, addresses: [], transactions: [] } }); } }, }), { name: "canina-user" } ) );