import { createContext, useContext, useEffect, useMemo, useReducer, ReactNode, useCallback } from "react";

/**
 * Client-side cart state. Persisted to localStorage and exposed via context.
 * Each cart line is keyed by product_id + variant_label so selecting different
 * variants of the same product produces distinct lines.
 */
export interface CartLine {
  product_id: number;
  name: string;
  slug: string;
  image_url: string;
  product_type: "physical" | "digital" | "hosting" | string;
  unit_price: number;
  currency: string;
  variant_label: string;
  quantity: number;
  stock: number; // -1 for unlimited
}

interface CartState {
  lines: CartLine[];
}

type CartAction =
  | { type: "ADD"; payload: CartLine }
  | { type: "UPDATE_QTY"; payload: { product_id: number; variant_label: string; quantity: number } }
  | { type: "REMOVE"; payload: { product_id: number; variant_label: string } }
  | { type: "CLEAR" }
  | { type: "HYDRATE"; payload: CartState };

const STORAGE_KEY = "doiteasy_cart_v1";

function lineKey(product_id: number, variant_label: string): string {
  return `${product_id}::${variant_label || ""}`;
}

function reducer(state: CartState, action: CartAction): CartState {
  switch (action.type) {
    case "ADD": {
      const key = lineKey(action.payload.product_id, action.payload.variant_label);
      const existing = state.lines.find(
        (l) => lineKey(l.product_id, l.variant_label) === key,
      );
      if (existing) {
        const newQty = existing.quantity + action.payload.quantity;
        const clamped =
          existing.stock >= 0 ? Math.min(newQty, existing.stock) : newQty;
        return {
          lines: state.lines.map((l) =>
            lineKey(l.product_id, l.variant_label) === key
              ? { ...l, quantity: clamped }
              : l,
          ),
        };
      }
      return { lines: [...state.lines, action.payload] };
    }
    case "UPDATE_QTY": {
      const key = lineKey(action.payload.product_id, action.payload.variant_label);
      return {
        lines: state.lines
          .map((l) =>
            lineKey(l.product_id, l.variant_label) === key
              ? { ...l, quantity: Math.max(1, action.payload.quantity) }
              : l,
          )
          .filter((l) => l.quantity > 0),
      };
    }
    case "REMOVE": {
      const key = lineKey(action.payload.product_id, action.payload.variant_label);
      return {
        lines: state.lines.filter(
          (l) => lineKey(l.product_id, l.variant_label) !== key,
        ),
      };
    }
    case "CLEAR":
      return { lines: [] };
    case "HYDRATE":
      return action.payload;
    default:
      return state;
  }
}

interface CartContextValue {
  lines: CartLine[];
  itemCount: number;
  subtotal: number;
  hasPhysical: boolean;
  addToCart: (line: CartLine) => void;
  updateQuantity: (product_id: number, variant_label: string, quantity: number) => void;
  removeFromCart: (product_id: number, variant_label: string) => void;
  clearCart: () => void;
}

const CartContext = createContext<CartContextValue | undefined>(undefined);

export function CartProvider({ children }: { children: ReactNode }) {
  const [state, dispatch] = useReducer(reducer, { lines: [] });

  // Hydrate from localStorage on mount (empty dep array per auth rules)
  useEffect(() => {
    try {
      const raw = localStorage.getItem(STORAGE_KEY);
      if (raw) {
        const parsed = JSON.parse(raw);
        if (parsed && Array.isArray(parsed.lines)) {
          dispatch({ type: "HYDRATE", payload: parsed });
        }
      }
    } catch (_) {
      // ignore corrupt storage
    }
  }, []);

  // Persist on any state change
  useEffect(() => {
    try {
      localStorage.setItem(STORAGE_KEY, JSON.stringify(state));
    } catch (_) {
      // ignore storage quota errors
    }
  }, [state]);

  const addToCart = useCallback((line: CartLine) => {
    dispatch({ type: "ADD", payload: line });
  }, []);

  const updateQuantity = useCallback(
    (product_id: number, variant_label: string, quantity: number) => {
      dispatch({ type: "UPDATE_QTY", payload: { product_id, variant_label, quantity } });
    },
    [],
  );

  const removeFromCart = useCallback((product_id: number, variant_label: string) => {
    dispatch({ type: "REMOVE", payload: { product_id, variant_label } });
  }, []);

  const clearCart = useCallback(() => {
    dispatch({ type: "CLEAR" });
  }, []);

  const value = useMemo<CartContextValue>(() => {
    const itemCount = state.lines.reduce((acc, l) => acc + l.quantity, 0);
    const subtotal = state.lines.reduce(
      (acc, l) => acc + l.quantity * (l.unit_price || 0),
      0,
    );
    const hasPhysical = state.lines.some((l) => l.product_type === "physical");
    return {
      lines: state.lines,
      itemCount,
      subtotal,
      hasPhysical,
      addToCart,
      updateQuantity,
      removeFromCart,
      clearCart,
    };
  }, [state, addToCart, updateQuantity, removeFromCart, clearCart]);

  return <CartContext.Provider value={value}>{children}</CartContext.Provider>;
}

export function useCart(): CartContextValue {
  const ctx = useContext(CartContext);
  if (!ctx) {
    throw new Error("useCart must be used within a CartProvider");
  }
  return ctx;
}