"use client"; import { createContext, useCallback, useContext, useEffect, useMemo, useState, type ReactNode, } from "react"; import type { GuestCartLine } from "./customer"; export type CartItem = GuestCartLine; const KEY = "meshkee-guest-cart"; const LEGACY_KEY = "salar-cart"; const CART_EVENT = "cart-updated"; function cookieDomain(): string | undefined { if (typeof window === "undefined") return undefined; const host = window.location.hostname.toLowerCase(); if (host === "localhost" || host === "127.0.0.1") return undefined; const parts = host.split(".").filter(Boolean); if (parts.length < 2) return undefined; return `.${parts.slice(-2).join(".")}`; } function parseItems(raw: string | null): CartItem[] { if (!raw) return []; try { const parsed = JSON.parse(raw) as unknown; if (!Array.isArray(parsed)) return []; return parsed .map((entry) => normalizeLine(entry)) .filter((item): item is CartItem => Boolean(item)); } catch { return []; } } function normalizeLine(entry: unknown): CartItem | null { if (!entry || typeof entry !== "object") return null; const item = entry as Record; const id = String(item.id ?? item.variantId ?? item.storeItemVariantId ?? "").trim(); if (!id) return null; const quantity = Math.max(1, Math.floor(Number(item.quantity ?? item.qty) || 1)); const price = Number(item.price) || 0; const name = String(item.name ?? "محصول"); const slug = item.slug ? String(item.slug) : undefined; const image = item.image != null && item.image !== "" ? String(item.image) : undefined; return { id, name, ...(slug ? { slug } : {}), price, quantity, ...(image ? { image } : {}) }; } function writeSharedCookie(items: CartItem[]) { if (typeof document === "undefined") return; const domain = cookieDomain(); const maxAge = 60 * 60 * 24 * 30; const encoded = encodeURIComponent( JSON.stringify( items.map(({ id, name, slug, price, quantity }) => ({ id, name, slug, price, quantity, image: null as string | null, })), ), ); if (encoded.length > 3500) return; let cookie = `${KEY}=${encoded}; path=/; max-age=${maxAge}; SameSite=Lax`; if (domain) cookie += `; domain=${domain}`; document.cookie = cookie; } function notify() { window.dispatchEvent(new Event(CART_EVENT)); } export function getCart(): CartItem[] { if (typeof window === "undefined") return []; const primary = parseItems(window.localStorage.getItem(KEY)); if (primary.length > 0) return primary; const legacy = parseItems(window.localStorage.getItem(LEGACY_KEY)); if (legacy.length > 0) { writeCart(legacy); window.localStorage.removeItem(LEGACY_KEY); return legacy; } return []; } export function writeCart(items: CartItem[]) { if (typeof window === "undefined") return; window.localStorage.setItem(KEY, JSON.stringify(items)); window.localStorage.removeItem(LEGACY_KEY); try { writeSharedCookie(items); } catch { // ignore cookie failures } notify(); } export function cartCount() { return getCart().reduce((sum, item) => sum + item.quantity, 0); } export function cartTotal() { return getCart().reduce((sum, item) => sum + item.price * item.quantity, 0); } export function addToCart(item: CartItem) { const cart = getCart(); const quantity = Math.max(1, Math.floor(item.quantity) || 1); const index = cart.findIndex((entry) => entry.id === item.id); if (index >= 0) { cart[index] = { ...cart[index], ...item, quantity: cart[index].quantity + quantity, }; } else { cart.push({ ...item, quantity }); } writeCart(cart); } export function setCartQuantity(id: string, quantity: number) { if (quantity < 1) { writeCart(getCart().filter((item) => item.id !== id)); return; } writeCart(getCart().map((item) => (item.id === id ? { ...item, quantity } : item))); } export function removeFromCart(id: string) { writeCart(getCart().filter((item) => item.id !== id)); } type CartContextValue = { items: CartItem[]; itemCount: number; total: number; addItem: (item: CartItem) => void; removeItem: (id: string) => void; setQuantity: (id: string, quantity: number) => void; }; const CartContext = createContext(null); export function CartProvider({ children }: { children: ReactNode }) { const [items, setItems] = useState([]); const sync = useCallback(() => setItems(getCart()), []); useEffect(() => { sync(); window.addEventListener(CART_EVENT, sync); window.addEventListener("storage", sync); return () => { window.removeEventListener(CART_EVENT, sync); window.removeEventListener("storage", sync); }; }, [sync]); const addItem = useCallback((item: CartItem) => addToCart(item), []); const removeItem = useCallback((id: string) => removeFromCart(id), []); const setQuantity = useCallback((id: string, quantity: number) => setCartQuantity(id, quantity), []); const itemCount = useMemo(() => items.reduce((sum, item) => sum + item.quantity, 0), [items]); const total = useMemo(() => items.reduce((sum, item) => sum + item.price * item.quantity, 0), [items]); const value = useMemo( () => ({ items, itemCount, total, addItem, removeItem, setQuantity }), [items, itemCount, total, addItem, removeItem, setQuantity], ); return {children}; } export function useCart() { const ctx = useContext(CartContext); if (!ctx) throw new Error("useCart must be used within CartProvider"); return ctx; }