state/cart/cart.state.tstypescript
import { create } from 'zustand';
import { subscribeWithSelector, persist } from 'zustand/middleware';
import type { StateCreator } from 'zustand';
import Decimal from 'decimal.js';
import { sessionStorageManager, CART_STORAGE_KEY } from '@/core/storage';
import { createLogger } from '@/core/logger';
import type { CartState } from '@/types/global';
import { cartOperations } from './operations';
import type { CartStore, CartItemsSlice, CartUiSlice } from './cart.types';

const logger = createLogger('CartStore');

const initialCartState: CartState = {
  items: [],
  totalQuantity: 0,
  isEmpty: true,
  vouchers: [],
  subtotal: new Decimal(0),
  hasDiscounts: false,
  totalDiscount: new Decimal(0),
  totalDiscountPercentage: new Decimal(0),
  total: new Decimal(0),
  isCalculating: false,
};

// Sync setters — cart items (state container; async/business logic lives in features)
const createCartItemsSlice: StateCreator<CartStore, [], [], CartItemsSlice> = (
  set,
  get
) => ({
  reset: () => set(initialCartState),

  setLastCurrency: currency => set({ currency }),

  hasItem: packageId => get().items.some(item => item.packageId === packageId),

  getItem: packageId => get().items.find(item => item.packageId === packageId),

  getItemQuantity: packageId =>
    get().items.find(item => item.packageId === packageId)?.quantity ?? 0,

  getTotalWeight: () =>
    get().items.reduce((sum, item) => sum + item.quantity, 0),

  getTotalItemCount: () =>
    get().items.reduce((sum, item) => sum + item.quantity, 0),

  getCoupons: () => get().vouchers ?? [],

  setItemProperties: (packageId, properties) =>
    set(state => ({
      items: state.items.map(item =>
        item.packageId === packageId ? { ...item, properties } : item
      ),
    })),
});

// Sync setters — UI flags
const createCartUiSlice: StateCreator<
  CartStore,
  [],
  [],
  CartUiSlice
> = set => ({
  swapInProgress: false,
  setSwapInProgress: value => set({ swapInProgress: value }),
  isCalculating: false,
});

const cartStoreInstance = create<CartStore>()(
  persist(
    subscribeWithSelector((...a) => ({
      ...initialCartState,
      ...createCartItemsSlice(...a),
      ...createCartUiSlice(...a),
      // Async cart logic lives in `./operations` (the thin-state convention).
      // The store exposes them as delegators for backward compatibility;
      // new code should call `sdk.cart.*` / `cartOperations` instead.
      ...cartOperations,
    })),
    {
      name: CART_STORAGE_KEY,
      storage: {
        getItem: name => sessionStorageManager.get<any>(name),
        setItem: (name, value) => sessionStorageManager.set(name, value),
        removeItem: name => sessionStorageManager.remove(name),
      },
      onRehydrateStorage: () => state => {
        if (state) {
          logger.debug('Cart store rehydrated, recalculating totals...');
          if (state.shippingMethod) {
            const sm = state.shippingMethod;
            state.shippingMethod = {
              ...sm,
              price: new Decimal(sm.price),
              originalPrice: new Decimal(sm.originalPrice),
              discountAmount: new Decimal(sm.discountAmount),
              discountPercentage: new Decimal(sm.discountPercentage),
            };
          }
          state.calculateTotals();
        }
      },
      partialize: state =>
        ({
          items: state.items,
          vouchers: state.vouchers,
          shippingMethod: state.shippingMethod,
          totalQuantity: state.totalQuantity,
          isEmpty: state.isEmpty,
        }) as unknown as CartStore,
    }
  )
);

export const cartStore = cartStoreInstance;
/**
 * The cart store. Call `useCartStore.getState()` to read the current
 * {@link CartState} (items, totals, discounts, shipping), or subscribe to react
 * to changes. To *mutate* the cart, use the `sdk.cart.*` operations (add, remove,
 * update quantity, apply coupon…) rather than writing to the store directly.
 *
 * @example
 * ```ts
 * const { total, items } = useCartStore.getState();
 * useCartStore.subscribe(cart => render(cart.total));
 * ```
 * @category Cart
 */
export const useCartStore = cartStoreInstance;