"use client";

import {
  useCallback,
  useEffect,
  useMemo,
  useRef,
  useState,
} from "react";
import { toast } from "@/components/admin/Toast";
import {
  FetchError,
  apiDelete,
  apiGet,
  apiPatch,
  apiPost,
} from "@/lib/client-api";

export interface AdvanceModuleDefinition {
  advanceModuleId: string;
  folderId: string;
  slug: string;
  name: string;
  image: string | null;
  description: string;
}

export interface AdvanceFolderDefinition {
  folderId: string;
  name: string;
  description: string;
}

type FilterTab = "all" | "in-list" | "pending" | "completed";

interface CartItem {
  id: string;
  moduleSlug: string;
  data: {
    advanceModuleId?: string;
    quantity?: number;
    purchased?: boolean;
  };
}

const PAGE_SIZE_OPTIONS = [10, 20, 50, 100] as const;

export function AdvanceModulesPanel({
  eventId,
  advanceFolders,
  advanceModules,
  enabled,
}: {
  eventId: string;
  advanceFolders: AdvanceFolderDefinition[];
  advanceModules: AdvanceModuleDefinition[];
  enabled: boolean;
}) {
  const [items, setItems] = useState<CartItem[]>([]);
  const [loading, setLoading] = useState(true);
  const [pageSize, setPageSize] = useState<number>(20);
  const [page, setPage] = useState(1);
  const [tab, setTab] = useState<FilterTab>("all");
  const [query, setQuery] = useState("");
  const [showSearchResults, setShowSearchResults] = useState(false);

  const modulesById = useMemo(
    () => new Map(advanceModules.map((m) => [m.advanceModuleId, m])),
    [advanceModules],
  );
  void modulesById;

  const foldersById = useMemo(
    () => new Map(advanceFolders.map((f) => [f.folderId, f])),
    [advanceFolders],
  );
  void foldersById;

  const reload = useCallback(async () => {
    try {
      setLoading(true);
      const res = await apiGet<{ items: CartItem[] }>(
        `/api/events/${eventId}/items`,
      );
      setItems(res.items ?? []);
    } catch {
      setItems([]);
    } finally {
      setLoading(false);
    }
  }, [eventId]);

useEffect(() => {
    // eslint-disable-next-line react-hooks/set-state-in-effect
    reload();
  }, [reload]);

  // Merge items by advanceModuleId, preferring a non-purchased entry.
  const cartById = useMemo(() => {
    const map = new Map<string, CartItem>();
    for (const it of items) {
      const id = it.data.advanceModuleId;
      if (!id) continue;
      const existing = map.get(id);
      if (!existing) {
        map.set(id, it);
        continue;
      }
      if (existing.data.purchased && !it.data.purchased) {
        map.set(id, it);
      }
    }
    return map;
  }, [items]);

  const filteredModules = useMemo(() => {
    const q = query.trim().toLowerCase();
    return advanceModules.filter((m) => {
      const cart = cartById.get(m.advanceModuleId);
      const qty = cart?.data.quantity ?? 0;
      const purchased = !!cart?.data.purchased;
      const inList = qty > 0 || purchased;
      if (tab === "in-list" && !inList) return false;
      if (tab === "completed" && !purchased) return false;
      if (tab === "pending" && (!inList || purchased)) return false;
      if (q) {
        const hay = `${m.name} ${m.description} ${m.slug}`.toLowerCase();
        if (!hay.includes(q)) return false;
      }
      return true;
    });
  }, [advanceModules, cartById, query, tab]);

  const totalPages = Math.max(1, Math.ceil(filteredModules.length / pageSize));
  const currentPage = Math.min(page, totalPages);
  const visibleModules = useMemo(() => {
    const start = (currentPage - 1) * pageSize;
    return filteredModules.slice(start, start + pageSize);
  }, [filteredModules, currentPage, pageSize]);

  const groupedVisible = useMemo(() => {
    // Group by folder, in the order of advanceFolders (which is displayOrder).
    const orderedFolders = advanceFolders;
    const map = new Map<string, AdvanceModuleDefinition[]>();
    for (const f of orderedFolders) map.set(f.folderId, []);
    for (const m of visibleModules) {
      const list = map.get(m.folderId);
      if (list) list.push(m);
    }
    return orderedFolders
      .map((f) => [f, map.get(f.folderId) ?? []] as const)
      .filter(([, items]) => items.length > 0);
  }, [visibleModules, advanceFolders]);

  const searchMatches = useMemo(() => {
    const q = query.trim().toLowerCase();
    if (!q) return [];
    return advanceModules
      .filter((m) =>
        `${m.name} ${m.description} ${m.slug}`.toLowerCase().includes(q),
      )
      .slice(0, 8);
  }, [advanceModules, query]);

  const counts = useMemo(() => {
    let inList = 0;
    let completed = 0;
    let pending = 0;
    for (const m of advanceModules) {
      const cart = cartById.get(m.advanceModuleId);
      const qty = cart?.data.quantity ?? 0;
      const purchased = !!cart?.data.purchased;
      if (qty > 0 || purchased) inList += 1;
      if (purchased) completed += 1;
      else if (qty > 0) pending += 1;
    }
    return { all: advanceModules.length, inList, pending, completed };
  }, [advanceModules, cartById]);

  if (!enabled) return null;
  if (advanceFolders.length === 0 || advanceModules.length === 0) {
    return (
      <section className="rounded-2xl border border-dashed border-border bg-surface p-6 text-center">
        <p className="text-sm font-medium">No advance products available</p>
        <p className="mt-1 text-xs text-muted">
          This template doesn&apos;t include any advance folders with products yet.
        </p>
      </section>
    );
  }

  return (
    <section className="space-y-4 rounded-2xl border border-border bg-surface p-5 shadow-sm">
      <header className="flex flex-col gap-3 lg:flex-row lg:items-center lg:justify-between">
        <div>
          <h2 className="text-sm font-semibold text-foreground">
            Advance modules (your shopping list)
          </h2>
          <p className="mt-1 text-xs text-muted">
            Browse products by category, add to your list, adjust quantity, and mark as completed when purchased.
          </p>
        </div>
        <div className="flex items-center gap-2">
          <label className="text-xs text-muted">
            Per page
            <select
              value={pageSize}
              onChange={(e) => {
                setPageSize(Number(e.target.value));
                setPage(1);
              }}
              className="ml-2 rounded-md border border-border bg-surface px-2 py-1 text-xs"
            >
              {PAGE_SIZE_OPTIONS.map((n) => (
                <option key={n} value={n}>
                  {n}
                </option>
              ))}
            </select>
          </label>
        </div>
      </header>

      <div className="grid gap-3 lg:grid-cols-[1fr_auto] lg:items-center">
        <div className="relative">
          <div className="flex items-center gap-2 rounded-xl border border-border bg-surface px-3 py-2 shadow-sm">
            <svg
              viewBox="0 0 24 24"
              className="h-4 w-4 text-muted"
              fill="none"
              stroke="currentColor"
              strokeWidth="1.6"
              strokeLinecap="round"
              strokeLinejoin="round"
            >
              <circle cx="11" cy="11" r="7" />
              <line x1="21" y1="21" x2="16.65" y2="16.65" />
            </svg>
            <input
              value={query}
              onChange={(e) => {
                setQuery(e.target.value);
                setShowSearchResults(true);
                setPage(1);
              }}
              onFocus={() => setShowSearchResults(true)}
              onBlur={() =>
                setTimeout(() => setShowSearchResults(false), 150)
              }
              placeholder="Search products to add (e.g. Ribbon)…"
              className="flex-1 bg-transparent text-sm outline-none placeholder:text-muted"
            />
            {query ? (
              <button
                type="button"
                onClick={() => setQuery("")}
                className="rounded-md p-1 text-xs text-muted hover:bg-surface-muted"
                aria-label="Clear search"
              >
                ✕
              </button>
            ) : null}
          </div>
          {showSearchResults && searchMatches.length > 0 ? (
            <ul className="absolute left-0 right-0 top-full z-20 mt-1 max-h-72 overflow-y-auto rounded-xl border border-border bg-surface shadow-lg">
              {searchMatches.map((m) => (
                <li key={m.advanceModuleId}>
                  <button
                    type="button"
                    onMouseDown={(e) => {
                      e.preventDefault();
                      setQuery(m.name);
                      setShowSearchResults(false);
                      const idx = advanceModules.findIndex(
                        (x) => x.advanceModuleId === m.advanceModuleId,
                      );
                      if (idx >= 0) {
                        const newPage = Math.floor(idx / pageSize) + 1;
                        setPage(newPage);
                      }
                    }}
                    className="flex w-full items-center gap-3 px-3 py-2 text-left text-sm hover:bg-surface-muted"
                  >
                    <span className="grid h-9 w-9 shrink-0 place-items-center overflow-hidden rounded-md border border-dashed border-border bg-surface-muted">
                      {m.image ? (
                        // eslint-disable-next-line @next/next/no-img-element
                        <img
                          src={m.image}
                          alt=""
                          className="h-full w-full object-cover"
                        />
                      ) : (
                        <span className="text-[10px] text-muted">no img</span>
                      )}
                    </span>
                    <span className="min-w-0 flex-1">
                      <span className="block truncate font-medium">
                        {m.name}
                      </span>
                      {m.description ? (
                        <span className="block truncate text-[11px] text-muted">
                          {m.description}
                        </span>
                      ) : null}
                    </span>
                  </button>
                </li>
              ))}
            </ul>
          ) : null}
        </div>

        <div className="flex flex-wrap items-center gap-1 rounded-xl border border-border bg-surface p-1">
          {(
            [
              ["all", `All (${counts.all})`],
              ["in-list", `In list (${counts.inList})`],
              ["pending", `Pending (${counts.pending})`],
              ["completed", `Completed (${counts.completed})`],
            ] as Array<[FilterTab, string]>
          ).map(([key, label]) => (
            <button
              key={key}
              type="button"
              onClick={() => {
                setTab(key);
                setPage(1);
              }}
              className={`rounded-lg px-3 py-1.5 text-xs font-medium transition ${
                tab === key
                  ? "bg-primary text-primary-foreground"
                  : "text-muted hover:bg-surface-muted"
              }`}
            >
              {label}
            </button>
          ))}
        </div>
      </div>

      {loading ? (
        <p className="text-xs text-muted">Loading…</p>
      ) : groupedVisible.length === 0 ? (
        <div className="rounded-2xl border border-dashed border-border bg-surface p-10 text-center">
          <p className="text-sm font-medium">No products match</p>
          <p className="mt-1 text-xs text-muted">
            Try clearing the search or switching tabs.
          </p>
        </div>
      ) : (
        <div className="space-y-5">
          {groupedVisible.map(([folder, items]) => (
            <section key={folder.folderId} className="space-y-2">
              <header className="flex items-center justify-between rounded-xl border border-border bg-surface-muted/40 px-3 py-2">
                <h3 className="text-sm font-semibold text-foreground">
                  {folder.name}
                </h3>
                <span className="text-[11px] text-muted">
                  {items.length} item{items.length === 1 ? "" : "s"}
                </span>
              </header>
              <ul className="grid gap-3 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4">
                {items.map((mod) => (
                  <AdvanceModuleCard
                    key={mod.advanceModuleId}
                    mod={mod}
                    cart={cartById.get(mod.advanceModuleId)}
                    eventId={eventId}
                    onChange={reload}
                  />
                ))}
              </ul>
            </section>
          ))}
        </div>
      )}

      <ShoppingListSummary
        advanceModules={advanceModules}
        cartById={cartById}
        eventId={eventId}
        onChange={reload}
      />

      <footer className="flex flex-wrap items-center justify-between gap-2 border-t border-border pt-3 text-xs text-muted">
        <span>
          Showing{" "}
          <strong className="text-foreground">
            {visibleModules.length}
          </strong>{" "}
          of <strong className="text-foreground">{filteredModules.length}</strong>{" "}
          ({advanceModules.length} total)
        </span>
        <div className="flex items-center gap-1">
          <button
            type="button"
            disabled={currentPage <= 1}
            onClick={() => setPage((p) => Math.max(1, p - 1))}
            className="rounded-md border border-border bg-surface px-2 py-1 hover:bg-surface-muted disabled:opacity-30"
          >
            ← Prev
          </button>
          <span className="px-2">
            Page <strong className="text-foreground">{currentPage}</strong> /{" "}
            {totalPages}
          </span>
          <button
            type="button"
            disabled={currentPage >= totalPages}
            onClick={() => setPage((p) => Math.min(totalPages, p + 1))}
            className="rounded-md border border-border bg-surface px-2 py-1 hover:bg-surface-muted disabled:opacity-30"
          >
            Next →
          </button>
        </div>
      </footer>
    </section>
  );
}

function AdvanceModuleCard({
  mod,
  cart,
  eventId,
  onChange,
}: {
  mod: AdvanceModuleDefinition;
  cart: CartItem | undefined;
  eventId: string;
  onChange: () => Promise<void>;
}) {
  const [qty, setQty] = useState<number>(cart?.data.quantity ?? 0);
  const [purchased, setPurchased] = useState<boolean>(
    !!cart?.data.purchased,
  );
  const [busy, setBusy] = useState(false);
  const [error, setError] = useState<string | null>(null);
  const lastSyncedQtyRef = useRef<number>(cart?.data.quantity ?? 0);
  const lastSyncedPurchasedRef = useRef<boolean>(!!cart?.data.purchased);

useEffect(() => {
    // eslint-disable-next-line react-hooks/set-state-in-effect
    setQty(cart?.data.quantity ?? 0);
     
    setPurchased(!!cart?.data.purchased);
    lastSyncedQtyRef.current = cart?.data.quantity ?? 0;
    lastSyncedPurchasedRef.current = !!cart?.data.purchased;
  }, [cart?.id, cart?.data.quantity, cart?.data.purchased]);

  const persist = useCallback(
    async (
      nextQty: number,
      nextPurchased: boolean,
      options?: { flash?: string },
    ) => {
      setError(null);
      try {
        if (nextQty <= 0 && !cart) {
          return;
        }
        if (cart) {
          if (nextQty <= 0) {
            await apiDelete(`/api/events/${eventId}/items/${cart.id}`);
            if (options?.flash) toast(options.flash, "info");
          } else {
            await apiPatch(`/api/events/${eventId}/items/${cart.id}`, {
              data: {
                ...cart.data,
                quantity: nextQty,
                purchased: nextPurchased,
              },
            });
            if (options?.flash) toast(options.flash, "success");
          }
        } else {
          await apiPost(`/api/events/${eventId}/items`, {
            moduleSlug: mod.slug,
            data: {
              advanceModuleId: mod.advanceModuleId,
              quantity: nextQty,
              purchased: nextPurchased,
            },
          });
          if (options?.flash) toast(options.flash, "success");
        }
        lastSyncedQtyRef.current = nextQty;
        lastSyncedPurchasedRef.current = nextPurchased;
        await onChange();
      } catch (err) {
        setError(err instanceof FetchError ? err.message : "Save failed.");
      } finally {
        setBusy(false);
      }
    },
    [cart, eventId, mod, onChange],
  );

  const onQtyInput = (raw: string) => {
    const parsed = Math.max(0, Math.floor(Number(raw) || 0));
    setQty(parsed);
  };

  const onQtyBlur = () => {
    if (qty === lastSyncedQtyRef.current) return;
    setBusy(true);
    void persist(qty, purchased, {
      flash: qty > 0 ? `${mod.name} updated.` : `${mod.name} removed.`,
    });
  };

  const onQtyKeyDown = (e: React.KeyboardEvent<HTMLInputElement>) => {
    if (e.key === "Enter") {
      e.currentTarget.blur();
    }
  };

  const onStep = (delta: number) => {
    const next = Math.max(0, qty + delta);
    setQty(next);
    setBusy(true);
    void persist(next, purchased, {
      flash:
        delta > 0
          ? `${mod.name} +${delta}.`
          : next === 0
            ? `${mod.name} removed.`
            : `${mod.name} −${Math.abs(delta)}.`,
    });
  };

  const onTogglePurchased = () => {
    const next = !purchased;
    setPurchased(next);
    if (qty === 0) {
      const newQty = next ? 1 : 0;
      setQty(newQty);
      setBusy(true);
      void persist(newQty, next, {
        flash: next
          ? `${mod.name} marked as purchased.`
          : `${mod.name} marked as pending.`,
      });
    } else {
      setBusy(true);
      void persist(qty, next, {
        flash: next
          ? `${mod.name} marked as purchased.`
          : `${mod.name} marked as pending.`,
      });
    }
  };

  const onRemove = () => {
    if (qty === 0 && !cart) return;
    setBusy(true);
    void persist(0, false, { flash: `${mod.name} removed.` });
  };

  return (
    <li
      className={`overflow-hidden rounded-2xl border bg-surface shadow-sm transition-all ${
        purchased
          ? "border-emerald-200 bg-emerald-50/60"
          : qty > 0
            ? "border-rose-200"
            : "border-border"
      }`}
    >
      <div className="relative h-28 w-full bg-gradient-to-br from-indigo-100 via-violet-50 to-pink-50">
        {mod.image ? (
          // eslint-disable-next-line @next/next/no-img-element
          <img
            src={mod.image}
            alt={mod.name}
            className={`h-full w-full object-cover transition ${
              purchased ? "opacity-50" : ""
            }`}
          />
        ) : (
          <div className="grid h-full w-full place-items-center text-xs text-muted">
            No image
          </div>
        )}
        {purchased ? (
          <span className="absolute left-2 top-2 rounded-full bg-emerald-600 px-2 py-0.5 text-[10px] font-semibold uppercase tracking-wide text-white">
            Purchased
          </span>
        ) : null}
      </div>

      <div className="space-y-3 p-3">
        <div>
          <p
            className={`truncate text-sm font-semibold ${
              purchased
                ? "text-muted line-through decoration-2 decoration-emerald-600"
                : "text-foreground"
            }`}
          >
            {mod.name}
          </p>
          {mod.description ? (
            <p className="mt-0.5 line-clamp-2 text-[11px] text-muted">
              {mod.description}
            </p>
          ) : null}
        </div>

        <div className="flex items-center gap-1 rounded-lg bg-surface-muted p-0.5">
          <button
            type="button"
            onClick={() => onStep(-1)}
            disabled={busy || qty === 0}
            className="grid h-8 w-8 shrink-0 place-items-center rounded-md text-sm font-bold hover:bg-surface disabled:opacity-30"
            aria-label="Decrease quantity"
          >
            −
          </button>
          <input
            type="number"
            min={0}
            value={qty}
            onChange={(e) => onQtyInput(e.target.value)}
            onBlur={onQtyBlur}
            onKeyDown={onQtyKeyDown}
            disabled={busy}
            className="h-8 w-full min-w-0 rounded-md border border-border px-2 text-center text-sm font-semibold tabular-nums outline-none focus:border-primary"
            aria-label="Quantity"
          />
          <button
            type="button"
            onClick={() => onStep(1)}
            disabled={busy}
            className="grid h-8 w-8 shrink-0 place-items-center rounded-md text-sm font-bold hover:bg-surface disabled:opacity-30"
            aria-label="Increase quantity"
          >
            +
          </button>
        </div>

        <div className="flex gap-1">
          {qty === 0 && !cart ? (
            <button
              type="button"
              onClick={() => onStep(1)}
              disabled={busy}
              className="flex-1 rounded-lg bg-primary px-2 py-1.5 text-xs font-semibold text-primary-foreground shadow-sm transition hover:opacity-90 disabled:opacity-30"
            >
              + Add to list
            </button>
          ) : (
            <button
              type="button"
              onClick={onTogglePurchased}
              disabled={busy}
              className={`flex-1 rounded-lg px-2 py-1.5 text-xs font-medium transition disabled:opacity-30 ${
                purchased
                  ? "bg-emerald-600 text-white hover:bg-emerald-700"
                  : "border border-border bg-surface text-foreground hover:bg-surface-muted"
              }`}
            >
              {purchased ? "Mark pending" : "Mark purchased"}
            </button>
          )}
          <button
            type="button"
            onClick={onRemove}
            disabled={busy || (qty === 0 && !cart)}
            className="rounded-lg border border-border bg-surface px-2 py-1.5 text-xs text-red-600 hover:bg-red-50 disabled:opacity-30"
            aria-label="Remove"
          >
            ✕
          </button>
        </div>

        {error ? <p className="text-[10px] text-red-600">{error}</p> : null}
      </div>
    </li>
  );
}

function ShoppingListSummary({
  advanceModules,
  cartById,
  eventId,
  onChange,
}: {
  advanceModules: AdvanceModuleDefinition[];
  cartById: Map<string, CartItem>;
  eventId: string;
  onChange: () => Promise<void>;
}) {
  const listed = useMemo(() => {
    const out: Array<{
      mod: AdvanceModuleDefinition;
      cart: CartItem;
    }> = [];
    for (const m of advanceModules) {
      const c = cartById.get(m.advanceModuleId);
      if (!c) continue;
      const qty = c.data.quantity ?? 0;
      if (qty <= 0 && !c.data.purchased) continue;
      out.push({ mod: m, cart: c });
    }
    return out;
  }, [advanceModules, cartById]);

  const totals = useMemo(() => {
    let pending = 0;
    let purchased = 0;
    let items = 0;
    for (const { cart } of listed) {
      const q = cart.data.quantity ?? 0;
      items += q;
      if (cart.data.purchased) purchased += q;
      else pending += q;
    }
    return { items, pending, purchased };
  }, [listed]);

  const onMarkPurchased = async (cartItem: CartItem) => {
    try {
      await apiPatch(`/api/events/${eventId}/items/${cartItem.id}`, {
        data: { ...cartItem.data, purchased: true },
      });
      await onChange();
    } catch (err) {
      toast(err instanceof FetchError ? err.message : "Update failed.", "error");
    }
  };

  const onRemove = async (cartItem: CartItem) => {
    try {
      await apiDelete(`/api/events/${eventId}/items/${cartItem.id}`);
      await onChange();
    } catch (err) {
      toast(err instanceof FetchError ? err.message : "Remove failed.", "error");
    }
  };

  if (listed.length === 0) return null;

  return (
    <section className="space-y-3 rounded-2xl border border-border bg-surface-muted/30 p-4 shadow-sm">
      <header className="flex flex-wrap items-center justify-between gap-2">
        <div>
          <h3 className="text-sm font-semibold text-foreground">
            My shopping list
          </h3>
          <p className="mt-0.5 text-[11px] text-muted">
            {listed.length} item{listed.length === 1 ? "" : "s"} ·{" "}
            <span className="font-medium text-amber-700">
              {totals.pending} pending
            </span>
            {" · "}
            <span className="font-medium text-emerald-700">
              {totals.purchased} purchased
            </span>
          </p>
        </div>
        <span className="rounded-full bg-primary/10 px-2.5 py-1 text-[11px] font-medium text-primary">
          {totals.items} unit{totals.items === 1 ? "" : "s"} total
        </span>
      </header>
      <ul className="divide-y divide-border rounded-xl border border-border bg-surface">
        {listed.map(({ mod, cart }) => {
          const qty = cart.data.quantity ?? 0;
          const purchased = !!cart.data.purchased;
          return (
            <li
              key={cart.id}
              className="flex items-center gap-3 p-3 text-sm"
            >
              <span className="grid h-10 w-10 shrink-0 place-items-center overflow-hidden rounded-md border border-dashed border-border bg-surface-muted">
                {mod.image ? (
                  // eslint-disable-next-line @next/next/no-img-element
                  <img
                    src={mod.image}
                    alt=""
                    className={`h-full w-full object-cover ${
                      purchased ? "opacity-50" : ""
                    }`}
                  />
                ) : (
                  <span className="text-[9px] text-muted">no img</span>
                )}
              </span>
              <div className="min-w-0 flex-1">
                <p
                  className={`truncate font-medium ${
                    purchased
                      ? "text-muted line-through decoration-2 decoration-emerald-600"
                      : "text-foreground"
                  }`}
                >
                  {mod.name}
                </p>
                <p className="text-[11px] text-muted">
                  Qty {qty}
                  {purchased ? " · Purchased" : " · Pending"}
                </p>
              </div>
              {purchased ? (
                <span className="rounded-full bg-emerald-600 px-2 py-0.5 text-[10px] font-semibold uppercase text-white">
                  Paid
                </span>
              ) : (
                <button
                  type="button"
                  onClick={() => onMarkPurchased(cart)}
                  className="rounded-lg bg-primary px-3 py-1.5 text-xs font-semibold text-primary-foreground shadow-sm transition hover:opacity-90"
                >
                  Mark purchased
                </button>
              )}
              <button
                type="button"
                onClick={() => onRemove(cart)}
                className="rounded-md p-1.5 text-muted hover:bg-red-50 hover:text-red-600"
                aria-label="Remove from list"
              >
                ✕
              </button>
            </li>
          );
        })}
      </ul>
    </section>
  );
}