import { useAllProducts } from '@/src/hooks/prominent-api-hooks'; import { useCentralSlotStore } from '@/src/store/centralSlotStore'; import { Alert } from 'react-native'; import { useQuery, useMutation, useQueryClient, UseQueryResult, UseMutationResult } from '@tanstack/react-query'; import { StorageServiceCasual } from 'common-ui/src/services/StorageServiceCasual'; // Cart type definition export type CartType = "regular" | "flash"; const getCartStorageKey = (cartType: CartType = "regular"): string => { return cartType === "flash" ? "flash_cart_items" : "cart_items"; }; interface LocalCartItem { id: number; productId: number; quantity: number; slotId: number; addedAt: string; } interface ProductSummary { id: number; price: string; incrementStep: number; isOutOfStock: boolean; isFlashAvailable: boolean; name?: string; flashPrice?: string | null; images?: string[]; productQuantity?: number; unitNotation?: string; marketPrice?: string | null; } export interface CartItem { id: number; productId: number; quantity: number; addedAt: string; subtotal: number; slotId: number; } interface CartData { items: CartItem[]; totalItems: number; totalAmount: number; } interface UseGetCartOptions { refetchOnWindowFocus?: boolean; enabled?: boolean; } interface UseGetCartReturn { data: CartData | undefined; isLoading: boolean; error: Error | null; refetch: () => Promise>; cartItems: CartItem[]; totalItems: number; totalPrice: number; isEmpty: boolean; hasItems: boolean; } interface AddToCartVariables { productId: number; quantity: number; slotId: number; } interface UpdateCartVariables { itemId: number; quantity: number; } interface RemoveCartVariables { itemId: number; } interface MutationOptions { onSuccess?: (data: TData, variables: TVariables) => void; onError?: (error: Error) => void; showSuccessAlert?: boolean; showErrorAlert?: boolean; refetchCart?: boolean; } interface UseAddToCartReturn { mutate: UseMutationResult['mutate']; mutateAsync: UseMutationResult['mutateAsync']; isLoading: boolean; error: Error | null; data: LocalCartItem[] | undefined; addToCart: (productId: number, quantity?: number, slotId?: number, onSettled?: (data: LocalCartItem[] | undefined, error: Error | null) => void) => void; addToCartAsync: (productId: number, quantity?: number, slotId?: number) => Promise; } interface UseUpdateCartItemReturn { mutate: UseMutationResult['mutate']; mutateAsync: UseMutationResult['mutateAsync']; isLoading: boolean; error: Error | null; data: LocalCartItem[] | undefined; updateCartItem: (itemId: number, quantity: number) => void; updateCartItemAsync: (itemId: number, quantity: number) => Promise; } interface UseRemoveFromCartReturn { mutate: UseMutationResult['mutate']; mutateAsync: UseMutationResult['mutateAsync']; isLoading: boolean; error: Error | null; data: LocalCartItem[] | undefined; removeFromCart: (itemId: number) => void; removeFromCartAsync: (itemId: number) => Promise; } const getLocalCart = async (cartType: CartType = "regular"): Promise => { const key = getCartStorageKey(cartType); const data = await StorageServiceCasual.getItem(key); return data ? JSON.parse(data) : []; }; const saveLocalCart = async (items: LocalCartItem[], cartType: CartType = "regular"): Promise => { const key = getCartStorageKey(cartType); await StorageServiceCasual.setItem(key, JSON.stringify(items)); await getLocalCart(cartType); }; const getNextCartItemId = (items: LocalCartItem[]): number => { const maxId = items.length > 0 ? Math.max(...items.map(item => item.id)) : 0; return maxId + 1; }; const addToLocalCart = async (productId: number, quantity: number, slotId: number | undefined, cartType: CartType = "regular"): Promise => { const items = await getLocalCart(cartType); const existingIndex = items.findIndex(item => item.productId === productId); if (existingIndex >= 0) { items[existingIndex].quantity += quantity; if (slotId !== undefined) { items[existingIndex].slotId = slotId; } } else { const newId = getNextCartItemId(items); const cartItem: LocalCartItem = { id: newId, productId, quantity, slotId: slotId ?? 0, addedAt: new Date().toISOString(), }; items.push(cartItem); } await saveLocalCart(items, cartType); return items; }; const updateLocalCartItem = async (itemId: number, quantity: number, cartType: CartType = "regular"): Promise => { const items = await getLocalCart(cartType); const item = items.find(i => i.id === itemId); if (item) { item.quantity = quantity; await saveLocalCart(items, cartType); } return items; }; const removeFromLocalCart = async (itemId: number, cartType: CartType = "regular"): Promise => { const items = await getLocalCart(cartType); const filtered = items.filter(i => i.id !== itemId); await saveLocalCart(filtered, cartType); return filtered; }; const clearLocalCart = async (cartType: CartType = "regular"): Promise => { const key = getCartStorageKey(cartType); await StorageServiceCasual.setItem(key, JSON.stringify([])); }; export function useGetCart(options: UseGetCartOptions = {}, cartType: CartType = "regular"): UseGetCartReturn { const { data: products } = useAllProducts(); const productSlotsMap = useCentralSlotStore((state) => state.productSlotsMap); const query: UseQueryResult = useQuery({ queryKey: [`local-cart-${cartType}`], queryFn: async (): Promise => { const cartItems = await getLocalCart(cartType); const productMap: Record> = Object.fromEntries( products?.products?.map((p) => [ p.id, { id: p.id, price: String(p.price), incrementStep: p.incrementStep, marketPrice: p.marketPrice === null || p.marketPrice === undefined ? null : String(p.marketPrice), name: p.name, flashPrice: p.flashPrice, images: p.images, productQuantity: p.productQuantity, unitNotation: p.unitNotation, }, ]) ?? [] ); const items: CartItem[] = cartItems .map((cartItem): CartItem | null => { const productBasic = productMap[cartItem.productId]; const productAvailability = productSlotsMap[cartItem.productId]; if (!productBasic || !productAvailability) return null; return { id: cartItem.id, productId: cartItem.productId, quantity: cartItem.quantity, addedAt: cartItem.addedAt, subtotal: Number(productBasic.price) * cartItem.quantity, slotId: cartItem.slotId, }; }) .filter((item): item is CartItem => item !== null); const totalAmount = items.reduce((sum, item) => sum + item.subtotal, 0); return { items, totalItems: items.length, totalAmount, }; }, refetchOnWindowFocus: options?.refetchOnWindowFocus ?? true, enabled: (options?.enabled ?? true) && !!products, }); return { data: query.data, isLoading: query.isLoading, error: query.error, refetch: query.refetch, cartItems: query.data?.items ?? [], totalItems: query.data?.totalItems ?? 0, totalPrice: query.data?.totalAmount ?? 0, isEmpty: !(query.data?.items?.length ?? 0), hasItems: Boolean(query.data?.items?.length), }; } export function useAddToCart(options: MutationOptions = {}, cartType: CartType = "regular"): UseAddToCartReturn { const queryClient = useQueryClient(); const mutation: UseMutationResult = useMutation({ mutationFn: async ({ productId, quantity, slotId }: AddToCartVariables): Promise => { return await addToLocalCart(productId, quantity, slotId, cartType); }, onSuccess: (data: LocalCartItem[], variables: AddToCartVariables) => { queryClient.invalidateQueries({ queryKey: [`local-cart-${cartType}`] }); if (options?.showSuccessAlert !== false) { Alert.alert("Success", "Item added to cart!"); } options?.onSuccess?.(data, variables); }, onError: (error: Error) => { if (options?.showErrorAlert !== false) { Alert.alert("Error", error.message || "Failed to add item to cart"); } options?.onError?.(error); }, }); const addToCart = (productId: number, quantity = 1, slotId?: number, onSettled?: (data: LocalCartItem[] | undefined, error: Error | null) => void): void => { if (slotId == null) { throw new Error('slotId is required for adding to cart'); } mutation.mutate({ productId, quantity, slotId }, { onSettled: (data: LocalCartItem[] | undefined, error: Error | null) => { onSettled?.(data, error); } }); }; return { mutate: mutation.mutate, mutateAsync: mutation.mutateAsync, isLoading: mutation.isPending, error: mutation.error, data: mutation.data, addToCart, addToCartAsync: (productId: number, quantity = 1, slotId?: number): Promise => { if (slotId == null) { throw new Error('slotId is required for adding to cart'); } return mutation.mutateAsync({ productId, quantity, slotId }); }, }; } export function useUpdateCartItem(options: MutationOptions = {}, cartType: CartType = "regular"): UseUpdateCartItemReturn { const queryClient = useQueryClient(); const mutation: UseMutationResult = useMutation({ mutationFn: async ({ itemId, quantity }: UpdateCartVariables): Promise => { return await updateLocalCartItem(itemId, quantity, cartType); }, onSuccess: (data: LocalCartItem[], variables: UpdateCartVariables) => { queryClient.invalidateQueries({ queryKey: [`local-cart-${cartType}`] }); if (options?.showSuccessAlert !== false) { Alert.alert("Success", "Cart item updated!"); } options?.onSuccess?.(data, variables); }, onError: (error: Error) => { if (options?.showErrorAlert !== false) { Alert.alert("Error", error.message || "Failed to update cart item"); } options?.onError?.(error); }, }); return { mutate: mutation.mutate, mutateAsync: mutation.mutateAsync, isLoading: mutation.isPending, error: mutation.error, data: mutation.data, updateCartItem: (itemId: number, quantity: number): void => mutation.mutate({ itemId, quantity }), updateCartItemAsync: (itemId: number, quantity: number): Promise => mutation.mutateAsync({ itemId, quantity }), }; } export function useRemoveFromCart(options: MutationOptions = {}, cartType: CartType = "regular"): UseRemoveFromCartReturn { const queryClient = useQueryClient(); const mutation: UseMutationResult = useMutation({ mutationFn: async ({ itemId }: RemoveCartVariables): Promise => { return await removeFromLocalCart(itemId, cartType); }, onSuccess: (data: LocalCartItem[], variables: RemoveCartVariables) => { queryClient.invalidateQueries({ queryKey: [`local-cart-${cartType}`] }); if (options?.showSuccessAlert !== false) { Alert.alert("Success", "Item removed from cart!"); } options?.onSuccess?.(data, variables); }, onError: (error: Error) => { if (options?.showErrorAlert !== false) { Alert.alert("Error", error.message || "Failed to remove item from cart"); } options?.onError?.(error); }, }); return { mutate: mutation.mutate, mutateAsync: mutation.mutateAsync, isLoading: mutation.isPending, error: mutation.error, data: mutation.data, removeFromCart: (itemId: number): void => mutation.mutate({ itemId }), removeFromCartAsync: (itemId: number): Promise => mutation.mutateAsync({ itemId }), }; } // Export clear cart function for direct use export { clearLocalCart };