import { useEffect, useMemo, useState } from "react";
import { api } from "../api";
import { useAuth } from "../auth";
import Icon from "../components/Icon";
import DataTable from "../components/DataTable";
import CategoryPathLabel, { pathParts } from "../components/CategoryPathLabel";
import { categoryInfoMap } from "../categoryPath";
import { locationPathById } from "../locationPath";
import { fmt } from "../units";

// Anzeigeeinheit eines Produkts: die im Formular gewählte Einheit (Gramm,
// Milliliter, Stück) – NICHT die Packung. „1 Packung" ist nichtssagend, weil eine
// Packung 500 g, 1 kg oder 25 ml sein kann; deshalb rechnen wir alles in die
// echte Einheit um. dispFactor = Basiseinheiten je Anzeigeeinheit.
const dispFactor = (p) => (p.unit_factor && p.unit_factor > 0 ? p.unit_factor : 1);
const dispLabel = (p) => (p.unit_name || "Stück");
// Mindestbestand gibt es nur bei Charge-Artikeln: Lebensmittel und
// Verbrauchsgegenstände (bulk). „Menge je Lagerort" und Einzelstücke nicht.
const canHaveMin = (p) => p.tracking !== "object" || p.bulk === true;
// Artikeleinheit (Packung, sonst Anzeigeeinheit): nur noch nötig, um die je-Lagerort-
// Mindestbestände umzurechnen, die in Artikeleinheiten gespeichert sind.
const articleUnit = (p) => (p.package_size && p.package_size > 0 ? p.package_size : (p.unit_factor || 1));

/**
 * Zentrale Liste aller Mindestbestände: je Produkt/Gruppe der Gesamt-Wert und die
 * Bedarfe je Lagerort, inline editierbar. Ergänzt die einzelnen Produkt-/Gruppen-
 * Formulare um einen Überblick an einem Ort.
 */
const EMPTY_DRAFT = { kind: "product", targetId: "", scope: "global", locId: "", menge: "" };

export default function MinStock() {
  const { isAdmin } = useAuth();
  const [products, setProducts] = useState([]);
  const [groups, setGroups] = useState([]);
  const [locations, setLocations] = useState([]);
  const [categories, setCategories] = useState([]);
  const [error, setError] = useState(null);
  const [loading, setLoading] = useState(true);
  // „+ hinzufügen"-Dialog: Ziel (Produkt/Gruppe) → Geltung (Gesamt/Lagerort) → Menge.
  const [showAdd, setShowAdd] = useState(false);
  const [draft, setDraft] = useState(EMPTY_DRAFT);

  async function load() {
    setLoading(true);
    try {
      const [ps, gs, ls, cs] = await Promise.all([
        api.listProducts("", ""), api.listGroups(), api.listLocations(), api.listCategories(),
      ]);
      setProducts(ps); setGroups(gs); setLocations(ls); setCategories(cs);
    } catch (err) { setError(err.message); } finally { setLoading(false); }
  }
  useEffect(() => { load(); }, []);

  const catInfo = useMemo(() => categoryInfoMap(categories), [categories]);

  const rows = useMemo(() => {
    const out = [];
    for (const p of products.filter(canHaveMin)) {
      out.push({
        key: `p:${p.id}:global`, kind: "product", scope: "global", entity: p,
        name: p.name, catId: p.category_id, ort: "(Gesamt)",
        // Alles in der Anzeigeeinheit (Gramm/Milliliter/Stück): min_stock ist in
        // Basiseinheiten gespeichert.
        soll: p.min_stock != null ? p.min_stock / dispFactor(p) : null,
        unit: dispLabel(p),
        bestand: p.stock / dispFactor(p), bestandUnit: dispLabel(p),
      });
      for (const l of (p.location_min_stocks || [])) {
        out.push({
          key: `p:${p.id}:${l.location_id}`, kind: "product", scope: "loc", entity: p,
          locId: l.location_id, name: p.name, catId: p.category_id,
          ort: locationPathById(l.location_id, locations) || "?",
          // je-Lagerort ist in Artikeleinheiten gespeichert → in Anzeigeeinheit umrechnen.
          soll: (l.min_stock * articleUnit(p)) / dispFactor(p), unit: dispLabel(p), bestand: null,
        });
      }
    }
    for (const g of groups) {
      out.push({
        key: `g:${g.id}:global`, kind: "group", scope: "global", entity: g,
        name: g.name, catId: null, ort: "(Gesamt)",
        soll: g.min_stock, unit: g.min_stock_unit_name || "", bestand: g.stock, bestandUnit: g.min_stock_unit_name || "",
      });
      for (const l of (g.location_min_stocks || [])) {
        out.push({
          key: `g:${g.id}:${l.location_id}`, kind: "group", scope: "loc", entity: g,
          locId: l.location_id, name: g.name, catId: null,
          ort: locationPathById(l.location_id, locations) || "?",
          soll: l.min_stock, unit: g.min_stock_unit_name || "", bestand: null,
        });
      }
    }
    return out;
  }, [products, groups, locations]);

  // Eine Lagerort-Bedarfsliste mit einem geänderten/neuen/entfernten Eintrag neu
  // bauen. Der Endpunkt ersetzt die komplette Liste – neue Lagerorte müssen also
  // ergänzt (nicht nur bestehende geändert) werden, sonst wird nie einer angelegt.
  const mergeLoc = (list, locId, num) => {
    const out = (list || []).map((e) => ({ location_id: e.location_id, min_stock: e.min_stock }));
    const hit = out.find((e) => String(e.location_id) === String(locId));
    if (hit) hit.min_stock = num;
    else out.push({ location_id: locId, min_stock: num });
    return out.filter((e) => e.min_stock != null && e.min_stock > 0);
  };

  async function saveSoll(row, value) {
    const roh = String(value).trim().replace(",", ".");
    const num = roh === "" ? null : Number(roh);
    if (num !== null && (Number.isNaN(num) || num < 0)) return;
    if (num === (row.soll ?? null)) return;
    try {
      if (row.kind === "product" && row.scope === "global") {
        const p = row.entity;
        // Eingabe in der Anzeigeeinheit → Basiseinheiten speichern.
        await api.updateProduct(p.id, {
          min_stock: num == null ? null : Math.round(num * dispFactor(p)),
          min_stock_in_packages: false,
          min_stock_unit_id: p.display_unit_id ?? null,
        });
      } else if (row.kind === "product" && row.scope === "loc") {
        const p = row.entity;
        // Eingabe in der Anzeigeeinheit → Artikeleinheiten (so gespeichert).
        const artikel = num == null ? null : (num * dispFactor(p)) / articleUnit(p);
        await api.setProductLocationMinStock(p.id, mergeLoc(p.location_min_stocks, row.locId, artikel));
      } else if (row.kind === "group" && row.scope === "global") {
        await api.updateGroup(row.entity.id, { min_stock: num });
      } else if (row.kind === "group" && row.scope === "loc") {
        await api.setGroupLocationMinStock(row.entity.id, mergeLoc(row.entity.location_min_stocks, row.locId, num));
      }
      await load();
    } catch (err) { setError(err.message); }
  }

  // Ausgewähltes Ziel des Dialogs + dessen Einheit (Menge wird darin erfasst).
  const draftTarget = draft.kind === "product"
    ? products.find((p) => String(p.id) === String(draft.targetId))
    : groups.find((g) => String(g.id) === String(draft.targetId));
  const draftUnit = draft.kind === "product"
    ? (draftTarget ? dispLabel(draftTarget) : "")
    : (draftTarget ? (draftTarget.min_stock_unit_name || "Stück") : "");

  function resetAdd() {
    setShowAdd(false);
    setDraft(EMPTY_DRAFT);
  }

  async function saveNew() {
    setError(null);
    const roh = String(draft.menge).trim().replace(",", ".");
    const num = roh === "" ? null : Number(roh);
    if (!draft.targetId) { setError("Bitte ein Ziel wählen."); return; }
    if (num == null || Number.isNaN(num) || num <= 0) { setError("Bitte eine Menge größer 0 angeben."); return; }
    if (draft.scope === "loc" && !draft.locId) { setError("Bitte einen Lagerort wählen."); return; }
    try {
      if (draft.kind === "product") {
        const p = draftTarget;
        if (draft.scope === "global") {
          // Menge in der Anzeigeeinheit erfasst → in Basiseinheiten speichern.
          await api.updateProduct(p.id, {
            min_stock: Math.round(num * dispFactor(p)),
            min_stock_in_packages: false,
            min_stock_unit_id: p.display_unit_id ?? null,
          });
        } else {
          // Anzeigeeinheit → Artikeleinheiten (so gespeichert).
          const artikel = (num * dispFactor(p)) / articleUnit(p);
          await api.setProductLocationMinStock(p.id, mergeLoc(p.location_min_stocks, draft.locId, artikel));
        }
      } else {
        const g = draftTarget;
        if (draft.scope === "global") {
          await api.updateGroup(g.id, { min_stock: num });
        } else {
          await api.setGroupLocationMinStock(g.id, mergeLoc(g.location_min_stocks, draft.locId, num));
        }
      }
      resetAdd();
      await load();
    } catch (err) { setError(err.message); }
  }

  const columns = [
    { key: "art", header: "Art", width: 96, filterText: (r) => (r.kind === "group" ? "Gruppe" : "Produkt"),
      render: (r) => <span className="badge nowrap">{r.kind === "group" ? "Gruppe" : "Produkt"}</span> },
    { key: "name", header: "Name", grow: true, min: 180,
      filterText: (r) => r.name, sortValue: (r) => r.name,
      render: (r) => <span className="strong">{r.name}</span> },
    { key: "category", header: "Kategorie", width: 200,
      filterText: (r) => (r.catId != null ? (catInfo.get(r.catId)?.path || "") : ""),
      filterValues: (r) => (r.catId != null ? (catInfo.get(r.catId)?.tokens || [""]) : [""]),
      filterOptionLabel: (v) => (v === "" ? "(Leere)" : <CategoryPathLabel parts={pathParts(v)} />),
      render: (r) => (r.catId != null
        ? <CategoryPathLabel parts={catInfo.get(r.catId)?.parts} fallback="–" />
        : <span className="muted">–</span>) },
    { key: "ort", header: "Ort", width: 220, filterText: (r) => r.ort, sortValue: (r) => r.ort,
      render: (r) => (r.scope === "global" ? <span className="muted">{r.ort}</span> : r.ort) },
    { key: "soll", header: "Mindestbestand", width: 150,
      sortValue: (r) => r.soll ?? -1,
      render: (r) => (isAdmin ? (
        <input type="number" step="any" min="0" style={{ marginTop: 0, minWidth: 80 }}
          key={r.soll ?? "none"} defaultValue={r.soll ?? ""}
          onBlur={(e) => saveSoll(r, e.target.value)} />
      ) : (r.soll != null ? fmt(r.soll) : "–")) },
    { key: "unit", header: "Einheit", width: 120, filterText: (r) => r.unit || "",
      render: (r) => <span className="muted">{r.unit || "–"}</span> },
    { key: "bestand", header: "Bestand", width: 120, align: "num",
      render: (r) => (r.bestand != null ? `${fmt(r.bestand)} ${r.bestandUnit || ""}` : <span className="muted">–</span>) },
  ];

  return (
    <div>
      <div className="page-head">
        <div>
          <h1>Mindestbestände</h1>
          <div className="sub">
            Gesamt- und Lagerort-Bedarfe von Produkten und Gruppen an einem Ort{isAdmin ? " – Werte direkt editierbar" : ""}.
          </div>
        </div>
        {isAdmin && (
          <button className="btn primary" onClick={() => (showAdd ? resetAdd() : setShowAdd(true))}>
            <Icon name={showAdd ? "close" : "plus"} size={16} />
            {showAdd ? "Abbrechen" : "Mindestbestand"}
          </button>
        )}
      </div>
      {error && <div className="alert error"><Icon name="alert" size={16} />{error}</div>}

      {isAdmin && showAdd && (
        <div className="card" style={{ marginBottom: "var(--sp-3)" }}>
          <div className="card-head"><Icon name="plus" /><h2>Mindestbestand hinzufügen</h2></div>
          <div className="row">
            <label className="seg-label">
              Ziel
              <div className="segmented">
                <button type="button" className={draft.kind === "product" ? "active" : ""}
                  onClick={() => setDraft({ ...draft, kind: "product", targetId: "" })}>Gegenstand / Lebensmittel</button>
                <button type="button" className={draft.kind === "group" ? "active" : ""}
                  onClick={() => setDraft({ ...draft, kind: "group", targetId: "" })}>Gruppe</button>
              </div>
            </label>
          </div>
          <div className="row">
            <label className="grow">
              {draft.kind === "product" ? "Produkt / Gegenstand" : "Gruppe"}
              <select value={draft.targetId} onChange={(e) => setDraft({ ...draft, targetId: e.target.value })}>
                <option value="">– wählen –</option>
                {(draft.kind === "product"
                  ? products.filter(canHaveMin).sort((a, b) => a.name.localeCompare(b.name, "de"))
                  : [...groups].sort((a, b) => a.name.localeCompare(b.name, "de"))
                ).map((t) => (
                  <option key={t.id} value={t.id}>
                    {t.name}{draft.kind === "product" && t.brand ? ` · ${t.brand}` : ""}
                  </option>
                ))}
              </select>
            </label>
          </div>
          <div className="row">
            <label className="seg-label">
              Geltung
              <div className="segmented">
                <button type="button" className={draft.scope === "global" ? "active" : ""}
                  onClick={() => setDraft({ ...draft, scope: "global", locId: "" })}>Gesamt</button>
                <button type="button" className={draft.scope === "loc" ? "active" : ""}
                  onClick={() => setDraft({ ...draft, scope: "loc" })}>Lagerort</button>
              </div>
            </label>
            {draft.scope === "loc" && (
              <label className="grow" style={{ maxWidth: 360 }}>
                Lagerort
                <select value={draft.locId} onChange={(e) => setDraft({ ...draft, locId: e.target.value })}>
                  <option value="">– wählen –</option>
                  {locations.map((l) => (
                    <option key={l.id} value={l.id}>{locationPathById(l.id, locations)}</option>
                  ))}
                </select>
              </label>
            )}
          </div>
          <div className="row">
            <label className="grow" style={{ maxWidth: 300 }}>
              Menge{draftUnit ? ` (in ${draftUnit})` : ""}
              <input type="number" step="any" min="0" value={draft.menge}
                onChange={(e) => setDraft({ ...draft, menge: e.target.value })} placeholder="z.B. 2" />
            </label>
          </div>
          <div className="field-inline" style={{ marginTop: "var(--sp-2)" }}>
            <button className="btn primary" onClick={saveNew}>Speichern</button>
            <button className="btn" onClick={resetAdd}>Abbrechen</button>
          </div>
        </div>
      )}

      <div className="card">
        <DataTable id="min-stock" columns={columns} rows={rows} loading={loading}
          getRowKey={(r) => r.key} empty="Noch keine Mindestbestände." />
      </div>
    </div>
  );
}
