"use client"; import { createContext, useCallback, useContext, useEffect, useMemo, useState, } from "react"; import { addCartItem, getCart, removeCartItem, updateCartItem, } from "@/lib/meshkee/api"; import { cartFromGuest, emptyCart, normalizeCartResponse, } from "@/lib/meshkee/cart"; import { clearGuestCart, readGuestCart, writeGuestCart, } from "@/lib/meshkee/guest-cart"; import type { CartState, GuestCartLine } from "@/lib/meshkee/types"; import { useAuth } from "@/app/providers/auth-provider"; import { useToast } from "@/app/providers/toast-provider"; type CartContextValue = { cart: CartState; itemCount: number; ready: boolean; loading: boolean; open: boolean; setOpen: (open: boolean) => void; refreshCart: () => Promise; addItem: (item: { storeItemVariantId: string; name: string; price: number; quantity?: number; image?: string | null; slug?: string; }) => Promise; updateQuantity: (itemId: string, quantity: number) => Promise; removeItem: (itemId: string) => Promise; guestItems: GuestCartLine[]; }; const CartContext = createContext(null); export function CartProvider({ children }: { children: React.ReactNode }) { const { ready: authReady, isAuthenticated, businessId, getAccessToken } = useAuth(); const { showToast } = useToast(); const [cart, setCart] = useState(emptyCart()); const [ready, setReady] = useState(false); const [loading, setLoading] = useState(false); const [open, setOpen] = useState(false); const [guestItems, setGuestItems] = useState([]); const refreshCart = useCallback(async () => { if (!authReady) return; const token = getAccessToken(); if (isAuthenticated && token && businessId) { setLoading(true); try { const localGuest = readGuestCart(); if (localGuest.length > 0) { for (const line of localGuest) { await addCartItem(businessId, token, { storeItemVariantId: line.id, quantity: line.quantity, }); } clearGuestCart(); setGuestItems([]); } const payload = await getCart(businessId, token); setCart(normalizeCartResponse(payload)); } catch { setCart(emptyCart()); } finally { setLoading(false); setReady(true); } return; } const guest = readGuestCart(); setGuestItems(guest); setCart(cartFromGuest(guest)); setReady(true); }, [authReady, businessId, getAccessToken, isAuthenticated]); useEffect(() => { void refreshCart().catch(() => { setCart(emptyCart()); setReady(true); }); }, [refreshCart]); const addItem = useCallback( async (item: { storeItemVariantId: string; name: string; price: number; quantity?: number; image?: string | null; slug?: string; }) => { const quantity = Math.max(1, Math.floor(item.quantity ?? 1)); const token = getAccessToken(); if (isAuthenticated && token && businessId) { setLoading(true); try { const payload = await addCartItem(businessId, token, { storeItemVariantId: item.storeItemVariantId, quantity, }); setCart(normalizeCartResponse(payload)); showToast("به سبد خرید اضافه شد"); setOpen(true); } catch (error) { showToast( error instanceof Error ? error.message : "افزودن به سبد خرید ناموفق بود", ); } finally { setLoading(false); } return; } const current = readGuestCart(); const existing = current.find((line) => line.id === item.storeItemVariantId); const next = existing ? current.map((line) => line.id === item.storeItemVariantId ? { ...line, quantity: line.quantity + quantity } : line, ) : [ ...current, { id: item.storeItemVariantId, name: item.name, price: item.price, image: item.image ?? null, slug: item.slug, quantity, }, ]; writeGuestCart(next); setGuestItems(next); setCart(cartFromGuest(next)); showToast("به سبد خرید اضافه شد"); setOpen(true); }, [businessId, getAccessToken, isAuthenticated, showToast], ); const updateQuantity = useCallback( async (itemId: string, quantity: number) => { const token = getAccessToken(); if (isAuthenticated && token && businessId) { setLoading(true); try { const payload = quantity < 1 ? await removeCartItem(businessId, token, itemId) : await updateCartItem(businessId, token, itemId, quantity); setCart(normalizeCartResponse(payload)); } finally { setLoading(false); } return; } const current = readGuestCart(); const next = quantity < 1 ? current.filter((line) => line.id !== itemId) : current.map((line) => line.id === itemId ? { ...line, quantity } : line, ); writeGuestCart(next); setGuestItems(next); setCart(cartFromGuest(next)); }, [businessId, getAccessToken, isAuthenticated], ); const removeItem = useCallback( async (itemId: string) => { await updateQuantity(itemId, 0); }, [updateQuantity], ); const itemCount = useMemo( () => cart.items.reduce((sum, line) => sum + line.quantity, 0), [cart.items], ); const value = useMemo( () => ({ cart, itemCount, ready, loading, open, setOpen, refreshCart, addItem, updateQuantity, removeItem, guestItems, }), [ cart, itemCount, ready, loading, open, refreshCart, addItem, updateQuantity, removeItem, guestItems, ], ); return {children}; } export function useCart() { const ctx = useContext(CartContext); if (!ctx) throw new Error("useCart must be used within CartProvider"); return ctx; }