"use client";

import { CartItem } from "@/types";
import {
  createContext,
  use,
  useReducer,
  useEffect,
  useCallback,
  useMemo,
  type ReactNode,
} from "react";

type StopInfo = {
  id: string;
  city: string;
  state: string;
  date: string;
  time: string;
  location: string;
  brand_id: string;
};

export type { StopInfo };

type CartContextType = {
  cart: CartItem[];
  subtotal: number;
  selectedStop: StopInfo | null;
  cartBrandId: string | null;
  cartBrandSlug: string | null;
  justAdded: CartItem | null;
  cartRestored: boolean;  // true when server cart was loaded (for toast)
  setSelectedStop: (stop: StopInfo | null) => void;
  addToCart: (item: Omit<CartItem, "quantity">, fulfillment?: "pickup" | "ship") => void;
  /**
   * Replaces the cart with a single item (clears other items + selected stop)
   * and returns the resulting item so the caller can navigate to /checkout
   * for a true 1-tap "Buy Now" flow.
   */
  buyNow: (item: Omit<CartItem, "quantity">, fulfillment?: "pickup" | "ship") => CartItem;
  increaseQuantity: (id: string) => void;
  decreaseQuantity: (id: string) => void;
  removeFromCart: (id: string) => void;
  clearCart: () => void;
  dismissToast: () => void;
  /** Returns raw local cart — used by login page to merge before redirecting */
  getLocalCart: () => CartItem[];
  /** Loads server cart for a logged-in user, replaces local cart + localStorage.
   *  Call once on admin page mount for logged-in users.
   *  Returns true if a server cart was loaded (shows the restored toast). */
  loadServerCart: (userId: string) => Promise<boolean>;
  /** Dismiss the cart-restored toast */
  dismissRestoredToast: () => void;
};

const CART_KEY = "route_commerce_cart";
const STOP_KEY = "route_commerce_stop";

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

function parsePrice(price: string): number {
  const cleaned = String(price).replace("$", "").replace(",", "");
  return Number(cleaned) || 0;
}

/**
 * Read the cart from localStorage and validate it. Validation rules:
 *   - drop legacy carts missing brand_id (would pollute cross-brand state)
 *   - non-array JSON is treated as empty
 *
 * Side effect: clears the stored cart (and the stored stop) when validation
 * rejects the data. Runs only on the client.
 */
function readValidatedCart(): CartItem[] {
  if (typeof window === "undefined") return [];
  try {
    const raw = localStorage.getItem(CART_KEY);
    if (!raw) return [];
    const parsed = JSON.parse(raw);
    if (!Array.isArray(parsed)) return [];
    if (parsed.length > 0 && !parsed[0]?.brand_id) {
      // Legacy cart without brand_id — nuke it to avoid cross-brand pollution.
      try {
        localStorage.removeItem(CART_KEY);
        localStorage.removeItem(STOP_KEY);
      } catch {
        // ignore
      }
      return [];
    }
    return parsed;
  } catch {
    return [];
  }
}

/**
 * Read the selected stop from localStorage and validate it. Validation rules:
 *   - drop the stop when the cart is empty (stale state)
 *   - drop the stop when its brand doesn't match the cart's brand
 *
 * Side effect: clears the stored stop when validation rejects it. Runs only
 * on the client.
 */
function readValidatedStop(cart: CartItem[]): StopInfo | null {
  if (typeof window === "undefined") return null;
  if (cart.length === 0) {
    // Empty cart — any stop is stale.
    try {
      localStorage.removeItem(STOP_KEY);
    } catch {
      // ignore
    }
    return null;
  }
  try {
    const raw = localStorage.getItem(STOP_KEY);
    if (!raw) return null;
    const parsed = JSON.parse(raw);
    if (!parsed || typeof parsed !== "object" || !parsed.id) return null;
    const cartBrandId = cart[0]?.brand_id;
    if (cartBrandId && parsed.brand_id && parsed.brand_id !== cartBrandId) {
      // Stop belongs to a different brand — drop it.
      try {
        localStorage.removeItem(STOP_KEY);
      } catch {
        // ignore
      }
      return null;
    }
    return parsed as StopInfo;
  } catch {
    return null;
  }
}

type State = {
  cart: CartItem[];
  selectedStop: StopInfo | null;
  justAdded: CartItem | null;
  cartRestored: boolean;       // shown once per session
  restoredDismissed: boolean;
};

type Action =
  | { type: "SET_CART"; cart: CartItem[] }
  | { type: "REMOTE_CART"; cart: CartItem[]; localJson: string }
  | { type: "ADD_ITEM"; item: Omit<CartItem, "quantity">; fulfillment: "pickup" | "ship" }
  | { type: "ADD_ITEM_CROSS_BRAND"; item: CartItem }
  | { type: "BUY_NOW"; item: CartItem }
  | { type: "INCREASE_QUANTITY"; id: string }
  | { type: "DECREASE_QUANTITY"; id: string }
  | { type: "REMOVE_FROM_CART"; id: string }
  | { type: "CLEAR_CART" }
  | { type: "SET_SELECTED_STOP"; stop: StopInfo | null }
  | { type: "DISMISS_TOAST" }
  | { type: "SET_CART_RESTORED" }
  | { type: "SET_RESTORED_DISMISSED"; value: boolean };

function initState(): State {
  // Initial values are read directly from localStorage via lazy initializers
  // so the first render reflects the persisted cart. SSR is handled by the
  // `typeof window === "undefined"` guards in readValidated* above (they
  // return the empty defaults on the server, matching the empty initial
  // value the client used to set from a mount-only useEffect).
  const cart = readValidatedCart();
  return {
    cart,
    selectedStop: readValidatedStop(cart),
    justAdded: null,
    cartRestored: false,
    restoredDismissed: false,
  };
}

function reducer(state: State, action: Action): State {
  switch (action.type) {
    case "SET_CART":
      return { ...state, cart: action.cart };
    case "REMOTE_CART":
      // Only accept remote if it differs from our current cart
      // (avoids overwriting local with same value after our own write)
      if (JSON.stringify(state.cart) === action.localJson) {
        return state;
      }
      return { ...state, cart: action.cart };
    case "ADD_ITEM": {
      const existing = state.cart.find((p) => p.id === action.item.id);
      if (existing) {
        return {
          ...state,
          cart: state.cart.map((p) =>
            p.id === action.item.id ? { ...p, quantity: p.quantity + 1 } : p,
          ),
          justAdded: { ...existing, quantity: existing.quantity + 1 },
        };
      }
      // Cross-brand guard: clear selectedStop if items exist from a different brand
      if (state.cart.length > 0 && state.cart[0].brand_id !== action.item.brand_id) {
        return {
          ...state,
          cart: [{ ...action.item, quantity: 1, fulfillment: action.fulfillment }],
          selectedStop: null,
          justAdded: { ...action.item, quantity: 1, fulfillment: action.fulfillment },
        };
      }
      const newItem: CartItem = {
        ...action.item,
        quantity: 1,
        fulfillment: action.fulfillment,
      };
      return {
        ...state,
        cart: [...state.cart, newItem],
        justAdded: newItem,
      };
    }
    case "ADD_ITEM_CROSS_BRAND": {
      // Already constructed newItem + cleared stop as side effects upstream.
      // For consistency with ADD_ITEM cross-brand branch, return fresh cart.
      return {
        ...state,
        cart: [action.item],
        justAdded: action.item,
      };
    }
    case "BUY_NOW":
      return {
        ...state,
        cart: [action.item],
        selectedStop: null,
        justAdded: action.item,
      };
    case "INCREASE_QUANTITY":
      return {
        ...state,
        cart: state.cart.map((item) =>
          item.id === action.id ? { ...item, quantity: item.quantity + 1 } : item,
        ),
      };
    case "DECREASE_QUANTITY": {
      const next: CartItem[] = [];
      for (const item of state.cart) {
        const updated = item.id === action.id ? { ...item, quantity: item.quantity - 1 } : item;
        if (updated.quantity > 0) next.push(updated);
      }
      return { ...state, cart: next };
    }
    case "REMOVE_FROM_CART": {
      const next = state.cart.filter((item) => item.id !== action.id);
      // Clear stop if cart is now empty
      if (next.length === 0) {
        return { ...state, cart: next, selectedStop: null };
      }
      return { ...state, cart: next };
    }
    case "CLEAR_CART":
      return { ...state, cart: [] };
    case "SET_SELECTED_STOP":
      return { ...state, selectedStop: action.stop };
    case "DISMISS_TOAST":
      return { ...state, justAdded: null };
    case "SET_CART_RESTORED":
      return { ...state, cartRestored: true, restoredDismissed: false };
    case "SET_RESTORED_DISMISSED":
      return { ...state, restoredDismissed: action.value };
    default:
      return state;
  }
}

export function CartProvider({ children }: { children: ReactNode }) {
  const [state, dispatch] = useReducer(reducer, undefined, initState);
  const { cart, selectedStop, justAdded, cartRestored, restoredDismissed } = state;

  // Persist cart to localStorage on every change. The first run (right after
  // mount) writes back the same value we just read, which is a no-op.
  useEffect(() => {
    try {
      localStorage.setItem(CART_KEY, JSON.stringify(cart));
    } catch {
      // ignore
    }
  }, [cart]);

  // ── Cross-tab sync ─────────────────────────────────────────────────────────
  // Listen for cart changes in other tabs and reconcile. The native storage
  // event only fires for *other* tabs/windows, never the one that wrote, so
  // we don't need a guard to ignore our own writes.
  useEffect(() => {
    function handleStorageChange(e: StorageEvent) {
      if (e.key !== CART_KEY) return;
      if (e.newValue === null) return; // cleared elsewhere — let local clear handle it
      try {
        const remote = JSON.parse(e.newValue);
        if (!Array.isArray(remote)) return;
        dispatch({ type: "REMOTE_CART", cart: remote, localJson: e.newValue });
      } catch {
        // ignore malformed data
      }
    }

    window.addEventListener("storage", handleStorageChange);
    return () => window.removeEventListener("storage", handleStorageChange);
  }, []);

  const setSelectedStop = useCallback((stop: StopInfo | null) => {
    dispatch({ type: "SET_SELECTED_STOP", stop });
    try {
      if (stop) {
        localStorage.setItem(STOP_KEY, JSON.stringify(stop));
      } else {
        localStorage.removeItem(STOP_KEY);
      }
    } catch {
      // ignore
    }
  }, []);

  const addToCart = useCallback(
    (item: Omit<CartItem, "quantity">, fulfillment?: "pickup" | "ship") => {
      dispatch({
        type: "ADD_ITEM",
        item,
        fulfillment: fulfillment ?? "pickup",
      });
    },
    [],
  );

  /**
   * Replaces the entire cart with this single item. Use for "Buy Now" /
   * "Quick Buy" flows where the buyer wants to skip the cart step and go
   * straight to checkout. The returned CartItem reflects the final state
   * (existing items of the same product are merged by quantity).
   */
  const buyNow = useCallback(
    (item: Omit<CartItem, "quantity">, fulfillment?: "pickup" | "ship"): CartItem => {
      // Always clear selected stop on a buy-now — the buyer is bypassing the
      // standard stop-picker flow and will pick a stop at checkout.
      const finalItem: CartItem = {
        ...item,
        quantity: 1,
        fulfillment: fulfillment ?? "pickup",
      };
      dispatch({ type: "BUY_NOW", item: finalItem });
      return finalItem;
    },
    [],
  );

  const increaseQuantity = useCallback((id: string) => {
    dispatch({ type: "INCREASE_QUANTITY", id });
  }, []);

  const decreaseQuantity = useCallback((id: string) => {
    dispatch({ type: "DECREASE_QUANTITY", id });
  }, []);

  const removeFromCart = useCallback((id: string) => {
    dispatch({ type: "REMOVE_FROM_CART", id });
  }, []);

  const clearCart = useCallback(() => {
    dispatch({ type: "CLEAR_CART" });
    try {
      localStorage.removeItem(CART_KEY);
      localStorage.removeItem(STOP_KEY);
    } catch {
      // ignore
    }
  }, []);

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

  const dismissRestoredToast = useCallback(() => {
    dispatch({ type: "SET_RESTORED_DISMISSED", value: true });
  }, []);

  const loadServerCart = useCallback(async (userId: string): Promise<boolean> => {
    if (!userId) return false;
    try {
      const { getServerCart } = await import("@/actions/checkout");
      const serverItems = await getServerCart(userId);
      if (serverItems.length === 0) return false;

      // Replace localStorage so cross-tab stays in sync
      localStorage.setItem(CART_KEY, JSON.stringify(serverItems));
      dispatch({ type: "SET_CART", cart: serverItems });
      dispatch({ type: "SET_CART_RESTORED" });
      return true;
    } catch {
      return false;
    }
  }, []);

  const getLocalCart = useCallback((): CartItem[] => {
    try {
      const raw = localStorage.getItem(CART_KEY);
      if (!raw) return [];
      const parsed = JSON.parse(raw);
      return Array.isArray(parsed) ? parsed : [];
    } catch {
      return [];
    }
  }, []);

  const subtotal = cart.reduce((sum, item) => sum + parsePrice(item.price) * item.quantity, 0);

  const cartBrandId = cart.length > 0 ? cart[0].brand_id : null;
  const cartBrandSlug = cart.length > 0 ? cart[0].brand_slug : null;

  const showRestoredToast = cartRestored && !restoredDismissed;

  const value = useMemo<CartContextType>(
    () => ({
      cart,
      subtotal,
      selectedStop,
      cartBrandId,
      cartBrandSlug,
      justAdded,
      cartRestored: showRestoredToast,
      setSelectedStop,
      addToCart,
      buyNow,
      increaseQuantity,
      decreaseQuantity,
      removeFromCart,
      clearCart,
      dismissToast,
      getLocalCart,
      loadServerCart,
      dismissRestoredToast,
    }),
    [
      cart,
      subtotal,
      selectedStop,
      cartBrandId,
      cartBrandSlug,
      justAdded,
      showRestoredToast,
      setSelectedStop,
      addToCart,
      buyNow,
      increaseQuantity,
      decreaseQuantity,
      removeFromCart,
      clearCart,
      dismissToast,
      getLocalCart,
      loadServerCart,
      dismissRestoredToast,
    ],
  );

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

export function useCart(): CartContextType {
  const context = use(CartContext);
  if (!context) throw new Error("useCart must be used inside CartProvider");
  return context;
}