// #item/:id — Task 4 of docs/superpowers/plans/2026-08-03-inventory.md. One
// item's numbers, the accounts it posts to, and its MOVEMENT HISTORY.
//
// The history table borrows the account register's visual language
// (register/RegisterScreen.tsx): date, what happened in plain English, the
// quantity in or out, what it cost, and a RUNNING QUANTITY down the right —
// the register's Balance column, counting things instead of dollars. The
// running figure is computed server-side over the item's whole history in
// date order (server/src/items.ts), so it stays honest even when the list is
// capped at the most recent movements.
//
// Every row drills through to the journal entry the movement rode in on
// (CLAUDE.md §9 "every number drills through"). It always has one: 0018 made
// inventory_movements.entry_id NOT NULL precisely so stock can never move
// without a ledger counterpart — the drill-through is that invariant, made
// visible.
//
// Nothing here is stored: on hand, average cost and value are derived at
// query time from the movements below them (§3.4). If you add them up
// yourself, they agree — that's the point of showing both.

import { useState } from "react";
import { useCompany } from "../lib/company";
import { useApiGet } from "../lib/useApiGet";
import { apiPatch, ApiError } from "../lib/api";
import { fmtMoney } from "../lib/money";
import { fmtDate } from "../lib/date";
import { ErrorBanner } from "../components/ErrorBanner";
import { BackLink } from "../components/BackLink";
import type { NavTarget } from "../lib/useHashView";
import { ItemModal } from "./ItemModal";
import { BomEditor } from "../builds/BomEditor";
import { itemsChanged } from "./useItemsPresence";
import {
  KIND_LABELS, KIND_SECTION_NOTES,
  absQuantity, fmtQuantity, fmtUnitCost, isItemKind, kindLabel, movementLabel,
  type ApiItemDetail,
} from "./itemTypes";

export function ItemDetail({
  itemId,
  navigate,
}: {
  itemId: string;
  navigate: (target: NavTarget) => void;
}) {
  const { selectedCompanyId } = useCompany();
  const { data, loading, error, retry } = useApiGet<ApiItemDetail>(
    `/api/c/items/${itemId}`,
    selectedCompanyId,
  );

  const [editing, setEditing] = useState(false);
  const [busy, setBusy] = useState(false);
  const [actionError, setActionError] = useState<string | null>(null);

  async function setActive(active: boolean) {
    if (!selectedCompanyId || !data) return;
    setBusy(true);
    setActionError(null);
    try {
      await apiPatch(
        `/api/c/items/${itemId}`,
        {
          name: data.name,
          kind: data.kind,
          active,
          ...(data.sku !== null ? { sku: data.sku } : {}),
          ...(data.description !== null ? { description: data.description } : {}),
          ...(data.salesPrice !== null ? { salesPrice: data.salesPrice } : {}),
          ...(data.purchaseCost !== null ? { purchaseCost: data.purchaseCost } : {}),
          ...(data.incomeAccountId !== null ? { incomeAccountId: data.incomeAccountId } : {}),
          ...(data.cogsAccountId !== null ? { cogsAccountId: data.cogsAccountId } : {}),
          ...(data.inventoryAccountId !== null ? { inventoryAccountId: data.inventoryAccountId } : {}),
        },
        selectedCompanyId,
      );
      // Same gate as the Items screen: this may have been the last stocked item.
      itemsChanged(selectedCompanyId);
      retry();
    } catch (err) {
      setActionError(err instanceof ApiError ? err.message : "Couldn't change that item. Try again.");
    } finally {
      setBusy(false);
    }
  }

  const stocked = data?.kind === "inventory";

  return (
    <div className="wrap">
      <BackLink label="Back to items" />

      {error && <ErrorBanner message={error} onRetry={retry} />}
      {loading && !data && <p className="msg-inline">Loading&hellip;</p>}

      {data && !editing && (
        <>
          <header className="top">
            <h1>{data.name}</h1>
            <span className="chip chip-info">{kindLabel(data.kind)}</span>
            {!data.active && <span className="chip chip-muted">Inactive</span>}
          </header>
          <div className="asof">
            {data.sku ? `SKU ${data.sku} · ` : ""}
            {isItemKind(data.kind) ? KIND_SECTION_NOTES[data.kind] : ""}
          </div>

          {data.description && <p className="sub">{data.description}</p>}

          <div className="card" style={{ margin: "18px 0" }}>
            <h2>Where it stands</h2>
            <div className="rows">
              {stocked && (
                <>
                  <div className="row">
                    <span className="n">On hand</span>
                    <span className="a">{data.qtyOnHand !== null ? fmtQuantity(data.qtyOnHand) : "0"}</span>
                  </div>
                  <div className="row">
                    <span className="n">Average cost</span>
                    <span className="a">{data.averageCost !== null ? fmtUnitCost(data.averageCost) : fmtMoney("0.0000")}</span>
                  </div>
                  <div className="row total">
                    <span className="n">Value on the balance sheet</span>
                    <span className="a">{data.value !== null ? fmtMoney(data.value) : fmtMoney("0.0000")}</span>
                  </div>
                </>
              )}
              <div className="row">
                <span className="n">Sales price</span>
                <span className="a">{data.salesPrice !== null ? fmtMoney(data.salesPrice) : "Not set"}</span>
              </div>
              <div className="row">
                <span className="n">{data.kind === "service" ? "Cost" : "Purchase cost"}</span>
                <span className="a">{data.purchaseCost !== null ? fmtMoney(data.purchaseCost) : "Not set"}</span>
              </div>
            </div>
            {stocked && (
              <p className="sub" style={{ marginBottom: 0 }}>
                Nothing above is stored — all three come from the movements below, worked out fresh
                every time you look.
              </p>
            )}
          </div>

          <div className="card" style={{ marginBottom: 18 }}>
            <h2>Where it posts</h2>
            <div className="rows">
              <AccountRow
                label="Income account"
                note="where a sale of this lands"
                id={data.incomeAccountId}
                name={data.incomeAccountName}
                navigate={navigate}
              />
              {/* A service has no cost of sale to record, so an empty "Cost
                  account — Not set" row would be noise rather than a gap worth
                  pointing at. One that DOES carry a cost account (possible via
                  the API — 0018 only requires one for non-services) still
                  shows it. */}
              {(data.kind !== "service" || data.cogsAccountId !== null) && (
                <AccountRow
                  label="Cost account"
                  note={stocked ? "where what it cost you lands when you sell it" : "where what it cost you lands"}
                  id={data.cogsAccountId}
                  name={data.cogsAccountName}
                  navigate={navigate}
                />
              )}
              {stocked && (
                <AccountRow
                  label="Inventory account"
                  note="where the value on the shelf sits"
                  id={data.inventoryAccountId}
                  name={data.inventoryAccountName}
                  navigate={navigate}
                />
              )}
            </div>
          </div>

          {/* Inventory Task 5: only a stocked item can be built from parts
              (0018's assemblies_no_recursion refuses any other kind), so the
              parts list appears only here — and it sits above History, because
              what a thing is made from explains what its history will look
              like. */}
          {stocked && (
            <BomEditor
              itemId={itemId}
              itemName={data.name}
              navigate={navigate}
              onChanged={retry}
            />
          )}

          {stocked ? (
            <div className="card" style={{ marginBottom: 18 }}>
              <h2>History</h2>
              {data.movements.length === 0 ? (
                <div className="empty-state">
                  Nothing has moved yet. Receiving this on a bill is what puts the first of it on
                  the shelf.
                </div>
              ) : (
                <table>
                  <thead>
                    <tr>
                      <th>Date</th>
                      <th>What happened</th>
                      <th className="num">In</th>
                      <th className="num">Out</th>
                      <th className="num">Unit cost</th>
                      <th className="num">Value</th>
                      <th className="num">On hand after</th>
                    </tr>
                  </thead>
                  <tbody>
                    {data.movements.map((m) => {
                      const out = m.qty.trim().startsWith("-");
                      const magnitude = fmtQuantity(absQuantity(m.qty));
                      return (
                        <tr
                          key={m.id}
                          style={{ cursor: "pointer" }}
                          onClick={() => navigate({ view: "entry", id: m.entryId })}
                        >
                          <td style={{ whiteSpace: "nowrap" }}>{fmtDate(m.movedOn)}</td>
                          <td>
                            <div>{movementLabel(m.reason, m.qty)}</div>
                            {/* The entry's own memo names the document —
                                "Bill 4471", "Invoice #12" — so the row says
                                which one without this screen having to
                                re-derive it. */}
                            <div className="register-memo-sub">
                              {m.entryMemo?.trim() || `Entry ${m.entryNo ?? ""}`.trim() || "—"}
                            </div>
                          </td>
                          <td className="num">{out ? "" : magnitude}</td>
                          <td className="num">{out ? magnitude : ""}</td>
                          <td className="num">{fmtUnitCost(m.unitCost)}</td>
                          <td className="num">{fmtMoney(m.value)}</td>
                          <td className="num">{fmtQuantity(m.runningQty)}</td>
                        </tr>
                      );
                    })}
                  </tbody>
                </table>
              )}
            </div>
          ) : (
            <div className="card" style={{ marginBottom: 18 }}>
              <h2>History</h2>
              <p className="sub" style={{ marginBottom: 0 }}>
                {isItemKind(data.kind) ? KIND_LABELS[data.kind] : "This item"}s don&rsquo;t carry a
                quantity, so there&rsquo;s no stock history to show. Every bill and invoice that
                names this item is still in Transactions.
              </p>
            </div>
          )}

          <div className="card">
            <h2>Actions</h2>
            {actionError && <ErrorBanner message={actionError} onRetry={() => setActionError(null)} />}
            <div className="wizard-actions">
              <button type="button" className="btn" onClick={() => setEditing(true)}>
                Edit
              </button>
              <button
                type="button"
                className="btn"
                disabled={busy}
                onClick={() => void setActive(!data.active)}
              >
                {busy ? "Working…" : data.active ? "Deactivate" : "Reactivate"}
              </button>
            </div>
            <p className="sub" style={{ marginBottom: 0 }}>
              Items are turned off, never deleted — everything they&rsquo;ve already done stays in
              the books.
            </p>
          </div>
        </>
      )}

      {data && editing && selectedCompanyId && (
        <ItemModal
          companyId={selectedCompanyId}
          itemId={itemId}
          onClose={() => setEditing(false)}
          onSaved={() => {
            setEditing(false);
            retry();
          }}
        />
      )}
    </div>
  );
}

function AccountRow({
  label,
  note,
  id,
  name,
  navigate,
}: {
  label: string;
  note: string;
  id: string | null;
  name: string | null;
  navigate: (target: NavTarget) => void;
}) {
  return (
    <div className="row">
      {/* A div, not a span: it holds a block-level sub-line, and `.rows .row`
          is a flex container so this behaves identically either way. */}
      <div className="n">
        {label}
        <div className="register-memo-sub">{note}</div>
      </div>
      <span className="a">
        {id && name ? (
          <button
            type="button"
            className="acct-link"
            onClick={() => navigate({ view: "transactions", accountId: id })}
          >
            {name}
          </button>
        ) : (
          "Not set"
        )}
      </span>
    </div>
  );
}

