"use client";

import { useMemo, useState } from "react";

type CatalogItem = { sbis_id: string; name: string; price: number; available: boolean | null; variant_of: string | null; categoryId?: string; categoryName?: string };
type Catalog = { updated_at?: string; categories: { id: string; name: string; items: CatalogItem[] }[] };
type LocalItem = { id: number; name: string; slug: string };
type Link = { id: number; menuItemId: number; sbisId: string; unlinkedAt: Date | string | null; unlinkReason: string | null };
type SyncState = { lastAttemptAt: Date | string | null; lastSuccessAt: Date | string | null; lastDurationMs: number | null; lastError: string | null; lastSnapshotId: string | null; sourceVersion: string | null; itemsCount: number; categoriesCount: number } | null;
type AuditEvent = { id: number; actor: string; action: string; entityType: string | null; entityId: string | null; createdAt: Date | string };
type SbisItemCard = { sbisId: number; name: string | null; weight: number | null; weightStatus: string; productionVolume: number | null; productionMeasure: string | null; composition: { name: string | null; quantity: number | null; net_g: number | null; output: number | null; unit: string | null }[]; priceListId: number | null; updatedAt: string };
type SbisPrices = { defaultId: number; prices: { id: number; name: string; is_used?: boolean; is_archived?: boolean }[] };

const PULL_FIELDS = [
  { key: "name", label: "Название" },
  { key: "description", label: "Описание" },
  { key: "imageUrl", label: "Фото" }
] as const;

const PUSH_FIELDS = [
  { key: "name", label: "Название" },
  { key: "description", label: "Описание" },
  { key: "imageUrl", label: "Фото" }
] as const;

function csrfHeaders() {
  const csrf = document.cookie.split("; ").find((part) => part.startsWith("kiln-admin-csrf="))?.split("=")[1];
  return csrf ? { "x-csrf-token": decodeURIComponent(csrf) } : {} as Record<string, string>;
}

function formatTime(value: Date | string | null | undefined) {
  if (!value) return "нет данных";
  return new Date(value).toLocaleString("ru-RU");
}

export function SbisCatalogPanel({ localItems, links: initialLinks, syncState, auditEvents = [] }: { localItems: LocalItem[]; links: Link[]; syncState: SyncState; auditEvents?: AuditEvent[] }) {
  const [catalog, setCatalog] = useState<Catalog | null>(null);
  const [links, setLinks] = useState(initialLinks.filter((link) => !link.unlinkedAt));
  const [query, setQuery] = useState("");
  const [selected, setSelected] = useState<string[]>([]);
  const [selectedLocal, setSelectedLocal] = useState<Record<string, string>>({});
  const [manualItemId, setManualItemId] = useState("");
  const [manualSbisId, setManualSbisId] = useState("");
  const [message, setMessage] = useState("");
  const [loading, setLoading] = useState(false);
  const [card, setCard] = useState<{ sbisId: string; data: SbisItemCard } | null>(null);
  const [prices, setPrices] = useState<SbisPrices | null>(null);

  async function loadCard(sbisId: string) {
    setLoading(true); setMessage("");
    try { const response = await fetch(`/api/admin/menu/sbis-item?sbisId=${encodeURIComponent(sbisId)}`); const body = await response.json(); if (!response.ok) throw new Error(body.error || "Карточка недоступна"); setCard({ sbisId, data: body }); }
    catch (error) { setMessage(error instanceof Error ? error.message : "Карточка недоступна"); }
    finally { setLoading(false); }
  }

  async function loadPrices() {
    setLoading(true); setMessage("");
    try { const response = await fetch("/api/admin/menu/sbis-item?prices=1"); const body = await response.json(); if (!response.ok) throw new Error(body.error || "Прайсы недоступны"); setPrices(body); }
    catch (error) { setMessage(error instanceof Error ? error.message : "Прайсы недоступны"); }
    finally { setLoading(false); }
  }

  async function load() {
    setLoading(true); setMessage("");
    try { const response = await fetch("/api/admin/menu/catalog"); const body = await response.json(); if (!response.ok) throw new Error(body.error || "Каталог недоступен"); setCatalog(body); }
    catch (error) { setMessage(error instanceof Error ? error.message : "Каталог недоступен"); }
    finally { setLoading(false); }
  }

  const items = useMemo(() => (catalog?.categories || []).flatMap((category) => category.items.map((item) => ({ ...item, categoryName: category.name }))).filter((item) => `${item.name} ${item.categoryName} ${item.sbis_id}`.toLowerCase().includes(query.toLowerCase())), [catalog, query]);
  const activeLocalItems = localItems.filter((item) => !links.some((link) => link.menuItemId === item.id));

  async function linkItem(menuItemId: number, sbisId: string) {
    setLoading(true); setMessage("");
    try {
      const response = await fetch("/api/admin/menu/link", { method: "POST", headers: { "content-type": "application/json", ...csrfHeaders() }, body: JSON.stringify({ menuItemId, sbisId }) });
      const body = await response.json(); if (!response.ok) throw new Error(body.error || "Привязка не выполнена");
      setLinks((current) => [...current, { id: Date.now(), menuItemId, sbisId, unlinkedAt: null, unlinkReason: null }]); setMessage(`Позиция ${sbisId} привязана`);
    } catch (error) { setMessage(error instanceof Error ? error.message : "Привязка не выполнена"); }
    finally { setLoading(false); }
  }

  async function unlinkItem(menuItemId: number) {
    setLoading(true); setMessage("");
    try { const response = await fetch("/api/admin/menu/link", { method: "DELETE", headers: { "content-type": "application/json", ...csrfHeaders() }, body: JSON.stringify({ menuItemId, reason: "admin" }) }); const body = await response.json(); if (!response.ok) throw new Error(body.error || "Отвязка не выполнена"); setLinks((current) => current.filter((link) => link.menuItemId !== menuItemId)); setMessage("Позиция отвязана"); }
    catch (error) { setMessage(error instanceof Error ? error.message : "Отвязка не выполнена"); }
    finally { setLoading(false); }
  }

  async function importSelected() { setLoading(true); try { const response = await fetch("/api/admin/menu/catalog", { method: "POST", headers: { "content-type": "application/json", ...csrfHeaders() }, body: JSON.stringify({ sbisIds: selected }) }); const body = await response.json(); if (!response.ok) throw new Error(body.error || "Импорт не выполнен"); setMessage(`Импортировано позиций: ${body.imported}`); setSelected([]); } catch (error) { setMessage(error instanceof Error ? error.message : "Импорт не выполнен"); } finally { setLoading(false); } }

  async function pullFields(menuItemId: number, fields: string[]) {
    setLoading(true); setMessage("");
    try { const response = await fetch("/api/admin/menu/pull", { method: "POST", headers: { "content-type": "application/json", ...csrfHeaders() }, body: JSON.stringify({ menuItemId, fields }) }); const body = await response.json(); if (!response.ok) throw new Error(body.error || "Pull не выполнен"); setMessage(`Из СБИС взяты поля: ${fields.join(", ")}`); }
    catch (error) { setMessage(error instanceof Error ? error.message : "Pull не выполнен"); }
    finally { setLoading(false); }
  }

  async function pushFields(menuItemId: number, local: LocalItem, fields: string[]) {
    // §6.3: push requires an explicit second confirmation of the payload.
    if (!window.confirm(`Отправить в СБИС (${local.name}): ${fields.join(", ")}? Изменение запишется в карточку номенклатуры СБИС.`)) return;
    setLoading(true); setMessage("");
    try {
      const response = await fetch("/api/admin/menu/push", { method: "POST", headers: { "content-type": "application/json", ...csrfHeaders() }, body: JSON.stringify({ menuItemId, fields, confirm: true }) });
      const body = await response.json() as { updated?: string[]; errors?: { fields: string[]; error: string }[]; verified?: Record<string, boolean | null> };
      const parts: string[] = [];
      if (body.updated?.length) parts.push(`записано в СБИС: ${body.updated.join(", ")}`);
      const unverified = Object.entries(body.verified || {}).filter(([, value]) => value === false).map(([field]) => field);
      if (unverified.length) parts.push(`не подтвердилось перечитыванием: ${unverified.join(", ")}`);
      if (body.errors?.length) parts.push(`ошибки: ${body.errors.map((entry) => `${entry.fields.join("/")} — ${entry.error}`).join("; ")}`);
      if (!parts.length) parts.push("Ничего не изменено");
      setMessage(parts.join(" · "));
      if (!response.ok && !body.updated?.length) throw new Error(body.errors?.[0]?.error || "Push не выполнен");
    } catch (error) { setMessage(error instanceof Error ? error.message : "Push не выполнен"); }
    finally { setLoading(false); }
  }

  const successAgeMinutes = syncState?.lastSuccessAt ? Math.floor((Date.now() - new Date(syncState.lastSuccessAt).getTime()) / 60000) : null;
  const stale = successAgeMinutes === null || successAgeMinutes >= 3;

  return <section className="mt-8 card p-5"><div className="flex flex-wrap items-start justify-between gap-3"><div><p className="eyebrow">Источник каталога</p><h2 className="text-2xl">Каталог СБИС</h2><p className="mt-2 text-sm text-kiln-milk/60">Цена, вес и наличие читаются из актуального каталога. Редакционные поля остаются на сайте.</p></div><div className="flex flex-wrap gap-2"><button className="button button-secondary" onClick={loadPrices} disabled={loading}>{loading ? "Загрузка…" : "Прайсы СБИС"}</button><button className="button button-secondary" onClick={load} disabled={loading}>{loading ? "Загрузка…" : "Загрузить каталог"}</button></div></div>
    {prices ? <p className="mt-3 text-xs text-kiln-milk/60">Прайс по умолчанию (SBIS_PRICE_ID бота): {prices.defaultId}{prices.prices.find((price) => price.id === prices.defaultId)?.name ? ` — ${prices.prices.find((price) => price.id === prices.defaultId)!.name}` : ""} · доступно прайсов: {prices.prices.length}. Переключение выполняется настройкой бота с последующим обновлением каталога.</p> : null}
    <div className="mt-4 grid gap-2 rounded border border-white/10 p-3 text-sm">
      <div className="flex flex-wrap items-center gap-2">
        <span className={stale ? "font-semibold text-kiln-flame" : "font-semibold text-kiln-milk"}>{stale ? "Данные устарели" : "Каталог свежий"}</span>
        {successAgeMinutes !== null ? <span className="text-kiln-milk/60">· снимок {successAgeMinutes} мин назад</span> : <span className="text-kiln-milk/60">· успешных снимков нет</span>}
        {syncState ? <span className="text-kiln-milk/60">· позиций: {syncState.itemsCount} · категорий: {syncState.categoriesCount} · снимок {(syncState.lastSnapshotId || "").slice(0, 8) || "—"}</span> : null}
      </div>
      <div className="text-xs text-kiln-milk/55">Последняя попытка: {formatTime(syncState?.lastAttemptAt)} · последний успех: {formatTime(syncState?.lastSuccessAt)}{syncState?.lastDurationMs !== null && syncState?.lastDurationMs !== undefined ? ` · ${syncState.lastDurationMs} мс` : ""}</div>
      {syncState?.lastError ? <div className="text-xs text-kiln-flame">Ошибка синхронизации: {syncState.lastError}</div> : null}
    </div>
    <div className="mt-5 grid gap-3 rounded border border-white/10 p-3"><p className="text-sm font-semibold">Ручная привязка по `sbis_id`</p><div className="grid gap-3 md:grid-cols-[1fr_1fr_auto]"><select className="input" value={manualItemId} onChange={(event) => setManualItemId(event.target.value)}><option value="">Карточка сайта</option>{activeLocalItems.map((item) => <option key={item.id} value={item.id}>{item.name} · #{item.id}</option>)}</select><input className="input" value={manualSbisId} onChange={(event) => setManualSbisId(event.target.value)} placeholder="sbis_id из актуального каталога" /><button className="button button-primary" disabled={loading || !manualItemId || !manualSbisId} onClick={() => linkItem(Number(manualItemId), manualSbisId.trim())}>Привязать</button></div></div>
    {catalog ? <><div className="mt-4 flex flex-wrap gap-3"><input className="input min-w-64 flex-1" placeholder="Поиск по названию, категории или ID" value={query} onChange={(event) => setQuery(event.target.value)} /><button className="button button-primary" disabled={loading || !selected.length} onClick={importSelected}>Добавить выбранные ({selected.length})</button></div><div className="mt-4 max-h-[36rem] overflow-auto rounded border border-white/10">{items.map((item) => { const link = links.find((candidate) => candidate.sbisId === item.sbis_id); const local = link ? localItems.find((candidate) => candidate.id === link.menuItemId) : null; return <div key={item.sbis_id} className="grid gap-2 border-b border-white/10 p-3 last:border-0"><div className="flex items-start gap-3"><input type="checkbox" checked={selected.includes(item.sbis_id)} onChange={() => setSelected((current) => current.includes(item.sbis_id) ? current.filter((id) => id !== item.sbis_id) : [...current, item.sbis_id])} /><span className="min-w-0 flex-1"><strong className="block">{item.name}</strong><span className="text-xs text-kiln-milk/55">{item.categoryName} · СБИС {item.sbis_id} · {item.price} ₽ · {item.available === null ? "наличие уточняется" : item.available ? "в наличии" : "нет в наличии"}</span></span><button className="button button-secondary" disabled={loading} onClick={() => card?.sbisId === item.sbis_id ? setCard(null) : loadCard(item.sbis_id)}>{card?.sbisId === item.sbis_id ? "Скрыть карточку" : "Карточка СБИС"}</button></div>{card?.sbisId === item.sbis_id ? <div className="rounded border border-white/10 bg-black/15 p-3 text-xs"><div className="flex flex-wrap gap-3"><span>Вес по СБИС: {card.data.weight !== null ? `${card.data.weight} г` : "нет выхода в СБИС"}</span>{card.data.productionVolume !== null ? <span>объём: {card.data.productionVolume} {card.data.productionMeasure || ""}</span> : null}<span>прайс: {card.data.priceListId ?? "—"}</span></div>{card.data.weight === null ? <p className="mt-1 text-kiln-flame">СБИС не отдаёт итоговый выход — массу можно указать на сайте вручную, расхождение будет видно.</p> : null}{card.data.composition.length ? <table className="mt-2 w-full text-left"><thead><tr><th className="pr-3">Состав</th><th className="pr-3">Кол-во</th><th className="pr-3">Нетто</th><th>Выход</th></tr></thead><tbody>{card.data.composition.map((row, index) => <tr key={index} className="border-t border-white/5"><td className="pr-3">{row.name ?? "—"}</td><td className="pr-3">{row.quantity ?? "—"}</td><td className="pr-3">{row.net_g ?? "—"}</td><td>{row.output ?? "—"}</td></tr>)}</tbody></table> : <p className="mt-1 muted">Состав в СБИС не задан.</p>}</div> : null}                    {link ? <div className="flex flex-wrap items-center justify-between gap-2 rounded bg-kiln-flame/10 px-3 py-2 text-sm"><span>Привязано к карточке {local ? `«${local.name}»` : `#${link.menuItemId}`}</span><span className="flex flex-wrap gap-2">{PULL_FIELDS.map((field) => <button key={field.key} className="button button-secondary" disabled={loading} onClick={() => pullFields(link.menuItemId, [field.key])} title={`Взять «${field.label}» из СБИС`}>↧ {field.label}</button>)}<span className="mx-1 w-px self-stretch bg-white/15" />{PUSH_FIELDS.map((field) => local ? <button key={field.key} className="button button-secondary" disabled={loading} onClick={() => pushFields(link.menuItemId, local, [field.key])} title={`Записать «${field.label}» в СБИС`}>↑ {field.label}</button> : null)}<button className="button button-secondary" disabled={loading} onClick={() => unlinkItem(link.menuItemId)}>Отвязать</button></span></div> : <div className="flex flex-wrap gap-2"><select className="input min-w-64 flex-1" value={selectedLocal[item.sbis_id] || ""} onChange={(event) => setSelectedLocal((current) => ({ ...current, [item.sbis_id]: event.target.value }))}><option value="">Выбрать карточку сайта</option>{activeLocalItems.map((local) => <option key={local.id} value={local.id}>{local.name} · #{local.id}</option>)}</select><button className="button button-secondary" disabled={loading || !selectedLocal[item.sbis_id]} onClick={() => linkItem(Number(selectedLocal[item.sbis_id]), item.sbis_id)}>Привязать</button></div>}</div>; })}</div></> : null}
    {auditEvents.length ? <div className="mt-5 rounded border border-white/10 p-3"><p className="text-sm font-semibold">Журнал административных операций</p><div className="mt-2 grid max-h-56 gap-1 overflow-auto text-xs text-kiln-milk/60">{auditEvents.map((event) => <span key={event.id}>{new Date(event.createdAt).toLocaleString("ru-RU")} · {event.actor} · {event.action}{event.entityType ? ` · ${event.entityType}${event.entityId ? ` #${event.entityId}` : ""}` : ""}</span>)}</div></div> : null}
    {message ? <p className="mt-3 text-sm text-kiln-flame">{message}</p> : null}</section>;
}
