"use client";

import Link from "next/link";
import { createContext, useContext, useEffect, useMemo, useRef, useState } from "react";
import { usePathname } from "next/navigation";
import { CART_STORAGE_KEY, calculateCart } from "@/lib/cart";
import type { CartItem, Dish } from "@/lib/types";
import { reachGoal } from "@/lib/analytics";

type CartContextValue = {
  ordersEnabled: boolean;
  items: CartItem[];
  count: number;
  orderComment: string;
  hydrated: boolean;
  addItem: (dishId: string) => void;
  updateQuantity: (dishId: string, quantity: number) => void;
  replaceItems: (items: CartItem[]) => void;
  setOrderComment: (comment: string) => void;
  updatePriceSnapshots: (actual: { sbis_id: string; price?: number | null; available?: boolean; enough?: boolean; stock_left?: number | null }[]) => void;
  removeItem: (dishId: string) => void;
  undoRemove: () => void;
  pendingRemoval: { item: CartItem; index: number; expiresAt: number } | null;
  clear: () => void;
  getDish: (dishId: string) => Dish | undefined;
};

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

export function CartProvider({ children, menuItems, ordersEnabled }: { children: React.ReactNode; menuItems: Dish[]; ordersEnabled: boolean }) {
  const pathname = usePathname();
  const [items, setItems] = useState<CartItem[]>([]);
  const [orderComment, setOrderCommentState] = useState("");
  const [hydrated, setHydrated] = useState(false);
  const [toast, setToast] = useState<string | null>(null);
  const [priceOverrides, setPriceOverrides] = useState<Record<string, number>>({});
  const [availabilityOverrides, setAvailabilityOverrides] = useState<Record<string, boolean>>({});
  const [stockOverrides, setStockOverrides] = useState<Record<string, number | null>>({});
  const [pendingRemoval, setPendingRemoval] = useState<{ item: CartItem; index: number; token: number } | null>(null);
  const removalTimerRef = useRef<number | null>(null);

  useEffect(() => {
    const raw = window.localStorage.getItem(CART_STORAGE_KEY);
    if (raw) setItems(JSON.parse(raw) as CartItem[]);
    setOrderCommentState(window.localStorage.getItem("kiln-cart-comment-v1") || "");
    setHydrated(true);
  }, []);

  useEffect(() => {
    if (!hydrated) return;
    window.localStorage.setItem(CART_STORAGE_KEY, JSON.stringify(items));
  }, [hydrated, items]);

  useEffect(() => {
    if (hydrated) window.localStorage.setItem("kiln-cart-comment-v1", orderComment);
  }, [hydrated, orderComment]);

  useEffect(() => () => {
    if (removalTimerRef.current) window.clearTimeout(removalTimerRef.current);
  }, []);

  useEffect(() => {
    if (!hydrated || !items.length || !["/cart", "/checkout"].includes(pathname)) return;
    const controller = new AbortController();
    const check = async () => {
      try {
        const response = await fetch("/api/cart/availability", { method: "POST", headers: { "content-type": "application/json" }, body: JSON.stringify({ items: items.map((item) => ({ dishId: item.dishId, quantity: item.quantity })) }), cache: "no-store", signal: controller.signal });
        if (!response.ok) return;
        const body = await response.json() as { items?: { dishId: string; available: boolean; enough: boolean; stockLeft: number | null }[] };
        const nextAvailability: Record<string, boolean> = {};
        const nextStock: Record<string, number | null> = {};
        for (const item of body.items || []) { nextAvailability[item.dishId] = item.available && item.enough; nextStock[item.dishId] = item.stockLeft; }
        setAvailabilityOverrides((current) => ({ ...current, ...nextAvailability }));
        setStockOverrides((current) => ({ ...current, ...nextStock }));
      } catch { /* last-good menu projection remains visible */ }
    };
    void check();
    const timer = window.setInterval(() => { if (document.visibilityState === "visible") void check(); }, 30_000);
    return () => { controller.abort(); window.clearInterval(timer); };
  }, [hydrated, items, pathname]);

  const value = useMemo<CartContextValue>(
    () => ({
      ordersEnabled,
      items,
      count: items.filter((item) => menuItems.some((dish) => dish.id === item.dishId)).length,
      orderComment,
      hydrated,
      addItem: (dishId) => {
        setItems((current) => {
          const existing = current.find((item) => item.dishId === dishId);
          const dish = menuItems.find((candidate) => candidate.id === dishId);
          const unavailable = availabilityOverrides[dishId] === false || dish?.temporarilyUnavailable;
          const stockLeft = stockOverrides[dishId] !== undefined ? stockOverrides[dishId] : dish?.stockLeft;
          if (!dish || !ordersEnabled || unavailable || dish.alcohol || (stockLeft !== null && stockLeft !== undefined && (existing?.quantity || 0) >= stockLeft)) {
            setToast(dish?.unavailableText || "Позиция сейчас недоступна");
            window.setTimeout(() => setToast(null), 2400);
            return current;
          }
          if (existing) {
            return current.map((item) => (item.dishId === dishId ? { ...item, quantity: item.quantity + 1 } : item));
          }
          return [...current, { dishId, quantity: 1, priceSnapshot: dish?.price }];
        });
        reachGoal("add_to_cart", { dishId });
        setToast("Добавлено в корзину");
        window.setTimeout(() => setToast(null), 2400);
      },
      updateQuantity: (dishId, quantity) => {
        const dish = menuItems.find((candidate) => candidate.id === dishId);
        const stockLeft = stockOverrides[dishId] !== undefined ? stockOverrides[dishId] : dish?.stockLeft;
        const cappedQuantity = stockLeft !== null && stockLeft !== undefined ? Math.min(quantity, stockLeft) : quantity;
        setItems((current) => current.flatMap((item) => (item.dishId === dishId ? (cappedQuantity > 0 ? [{ ...item, quantity: cappedQuantity }] : []) : [item])));
      },
      replaceItems: (nextItems) => setItems(nextItems.filter((item) =>
        menuItems.some((dish) => dish.id === item.dishId) && item.quantity > 0)),
      setOrderComment: (comment) => setOrderCommentState(comment.slice(0, 160)),
      updatePriceSnapshots: (actual) => {
        const bySbisId = new Map(actual.map((item) => [String(item.sbis_id), item]));
        const nextOverrides: Record<string, number> = {};
        const nextAvailability: Record<string, boolean> = {};
        menuItems.forEach((dish) => {
          const currentActual = dish.sbisId ? bySbisId.get(dish.sbisId) : undefined;
          if (currentActual) {
            if (typeof currentActual.price === "number") nextOverrides[dish.id] = currentActual.price;
            if (currentActual.available !== undefined) nextAvailability[dish.id] = currentActual.available && currentActual.enough !== false;
          }
        });
        setItems((current) => current.map((item) => {
          const dish = menuItems.find((candidate) => candidate.id === item.dishId);
          const currentActual = dish?.sbisId ? bySbisId.get(dish.sbisId) : undefined;
          return currentActual && typeof currentActual.price === "number" ? { ...item, priceSnapshot: currentActual.price } : item;
        }));
        setPriceOverrides((current) => ({ ...current, ...nextOverrides }));
        setAvailabilityOverrides((current) => ({ ...current, ...nextAvailability }));
        const nextStock = Object.fromEntries(menuItems.flatMap((dish) => {
          const currentActual = dish.sbisId ? bySbisId.get(dish.sbisId) : undefined;
          return currentActual && "stock_left" in currentActual ? [[dish.id, currentActual.stock_left ?? null]] : [];
        }));
        setStockOverrides((current) => ({ ...current, ...nextStock }));
      },
      removeItem: (dishId) => {
        setToast(null);
        setItems((current) => {
          const index = current.findIndex((item) => item.dishId === dishId);
          if (index < 0) return current;
          if (removalTimerRef.current) window.clearTimeout(removalTimerRef.current);
          const token = Date.now();
          setPendingRemoval({ item: current[index], index, token });
          removalTimerRef.current = window.setTimeout(() => {
            setPendingRemoval((pending) => pending?.token === token ? null : pending);
            removalTimerRef.current = null;
          }, 3000);
          return current.filter((item) => item.dishId !== dishId);
        });
        reachGoal("remove_from_cart", { dishId });
      },
      undoRemove: () => {
        if (!pendingRemoval) return;
        if (removalTimerRef.current) window.clearTimeout(removalTimerRef.current);
        const { item, index } = pendingRemoval;
        setItems((current) => {
          const existing = current.find((currentItem) => currentItem.dishId === item.dishId);
          if (existing) {
            return current.map((currentItem) => currentItem.dishId === item.dishId
              ? { ...currentItem, quantity: currentItem.quantity + item.quantity }
              : currentItem);
          }
          const restored = [...current];
          restored.splice(Math.min(index, restored.length), 0, item);
          return restored;
        });
        setPendingRemoval(null);
        removalTimerRef.current = null;
      },
      pendingRemoval: pendingRemoval ? { item: pendingRemoval.item, index: pendingRemoval.index, expiresAt: pendingRemoval.token + 3000 } : null,
      clear: () => { setItems([]); setOrderCommentState(""); }
      ,getDish: (dishId) => {
        const dish = menuItems.find((candidate) => candidate.id === dishId);
        const override = priceOverrides[dishId];
        const availability = availabilityOverrides[dishId];
        const stockLeft = stockOverrides[dishId];
        return dish && (override !== undefined || availability !== undefined || stockLeft !== undefined)
          ? { ...dish, ...(override !== undefined ? { price: override } : {}), ...(availability !== undefined ? { temporarilyUnavailable: !availability } : {}), ...(stockLeft !== undefined ? { stockLeft } : {}) }
          : dish;
      }
    }),
    [hydrated, items, orderComment, pendingRemoval, menuItems, ordersEnabled, priceOverrides, availabilityOverrides, stockOverrides]
  );

  return (
    <CartContext.Provider value={value}>
      {children}
      {toast ? (
        <div className="fixed bottom-24 left-1/2 z-50 w-[calc(100%-32px)] max-w-sm -translate-x-1/2 rounded-lg border border-white/12 bg-kiln-graphite/96 p-3 shadow-ember backdrop-blur md:bottom-6 md:right-6 md:left-auto md:translate-x-0">
          <div className="flex items-center justify-between gap-3">
            <span className="text-sm">{toast}</span>
            {ordersEnabled ? <Link href="/cart" className="text-sm text-kiln-flame">
              Открыть
            </Link> : null}
          </div>
        </div>
      ) : null}
    </CartContext.Provider>
  );
}

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

export function useCartTotals() {
  const context = useCart();
  return calculateCart(context.items, context.items.map((item) => context.getDish(item.dishId)).filter((dish): dish is Dish => Boolean(dish)));
}
