"use client";

import * as React from "react";
import { CircleCheckIcon, LinkIcon, TriangleAlertIcon } from "lucide-react";

import { Button } from "@/components/ui/button";
import { Checkbox } from "@/components/ui/checkbox";
import { Input } from "@/components/ui/input";
import { NativeSelect, NativeSelectOption } from "@/components/ui/native-select";
import { cn } from "@/lib/utils";

import { AsyncBadge, type AsyncState } from "./async-status";
import { TONE_BOX } from "./tones";

const n2 = (n: number) => n.toLocaleString("en-US", { minimumFractionDigits: 2, maximumFractionDigits: 2 });
const kes = (n: number) => `${n < 0 ? "−" : ""}KES ${n2(Math.abs(n))}`;
const r2 = (n: number) => Math.round(n * 100) / 100;
const days = (from: string, to: string) => Math.round((new Date(to).getTime() - new Date(from).getTime()) / 86400000);

/* ---------------------------------- 18. Reconciliation ---------------------------------- */

/** Signed: money in positive, money out negative. */
export type RecLine = { id: string; date: string; ref: string; details: string; amount: number };

/**
 * Auto-match a statement against the book: same amount, dates within 3 days, each line used once. Returns
 * pairs statementId → bookId. People confirm or undo; nothing is matched silently on export.
 */
export function autoMatch(statement: RecLine[], book: RecLine[]): Record<string, string> {
  const used = new Set<string>();
  const out: Record<string, string> = {};
  for (const s of statement) {
    const hit = book.find((b) => !used.has(b.id) && Math.abs(b.amount - s.amount) < 0.005 && Math.abs(days(b.date, s.date)) <= 3);
    if (hit) {
      used.add(hit.id);
      out[s.id] = hit.id;
    }
  }
  return out;
}

/**
 * Reconciliation (PRD 6.6, AC-12): the statement beside the book. Matched pairs are ticked; what's left on
 * the statement must be recorded in Medora (bank charges, a payment nobody entered), and what's left in the
 * book is outstanding (deposits not yet cleared). It's done when opening + matched statement lines equals the
 * statement's closing balance.
 */
export function Reconciler({
  statement,
  book,
  opening,
  closing,
  matches,
  onUnmatch,
  onMatch,
  formatDate,
}: {
  statement: RecLine[];
  book: RecLine[];
  opening: number;
  closing: number;
  matches: Record<string, string>;
  onUnmatch: (statementId: string) => void;
  onMatch: (statementId: string, bookId: string) => void;
  formatDate: (iso: string) => string;
}) {
  const [pickS, setPickS] = React.useState<string | null>(null);
  const matchedBook = new Set(Object.values(matches));
  const cleared = r2(statement.filter((s) => matches[s.id]).reduce((a, s) => a + s.amount, 0));
  const computed = r2(opening + cleared);
  const diff = r2(closing - computed);
  const toRecord = statement.filter((s) => !matches[s.id]);
  const outstanding = book.filter((b) => !matchedBook.has(b.id));

  const side = (title: string, rows: RecLine[], isStatement: boolean) => (
    <div className="min-w-0">
      <h4 className="mb-1.5 text-sm font-semibold">{title}</h4>
      <div className="overflow-x-auto rounded-lg border border-[var(--color-border-panel)] bg-card">
        <table className="w-full border-collapse text-sm tabular-nums">
          <caption className="sr-only">{title}</caption>
          <thead className="bg-[var(--color-bg-surface-subtle)] text-left text-xs text-muted-foreground">
            <tr>
              <th scope="col" className="w-8 px-2 py-2">
                <span className="sr-only">Matched</span>
              </th>
              <th scope="col" className="px-2 py-2 font-medium">
                Date
              </th>
              <th scope="col" className="px-2 py-2 font-medium">
                Details
              </th>
              <th scope="col" className="px-2 py-2 text-right font-medium">
                Amount
              </th>
            </tr>
          </thead>
          <tbody className="divide-y divide-[var(--color-border-subtle)]">
            {rows.map((r) => {
              const matched = isStatement ? !!matches[r.id] : matchedBook.has(r.id);
              const selectable = !matched && (isStatement || pickS);
              return (
                <tr key={r.id} className={cn(matched ? "text-muted-foreground" : "", isStatement && pickS === r.id && "bg-[var(--color-bg-selected)]")}>
                  <td className="px-2 py-1.5">
                    <Checkbox
                      aria-label={`${matched ? "Unmatch" : isStatement ? "Choose" : "Match with chosen statement line"}: ${r.details} ${n2(r.amount)}`}
                      checked={matched || (isStatement && pickS === r.id)}
                      disabled={!matched && !selectable}
                      onCheckedChange={() => {
                        if (matched) {
                          if (isStatement) onUnmatch(r.id);
                          else onUnmatch(Object.keys(matches).find((k) => matches[k] === r.id)!);
                        } else if (isStatement) setPickS(pickS === r.id ? null : r.id);
                        else if (pickS) {
                          onMatch(pickS, r.id);
                          setPickS(null);
                        }
                      }}
                    />
                  </td>
                  <td className="px-2 py-1.5 whitespace-nowrap">{formatDate(r.date)}</td>
                  <td className="px-2 py-1.5">
                    {r.details}
                    <span className="block font-mono text-[11px] text-muted-foreground">{r.ref}</span>
                  </td>
                  <td className="px-2 py-1.5 text-right">{r.amount < 0 ? `−${n2(-r.amount)}` : n2(r.amount)}</td>
                </tr>
              );
            })}
          </tbody>
        </table>
      </div>
    </div>
  );

  return (
    <div className="space-y-3">
      <dl className="grid grid-cols-2 gap-px overflow-hidden rounded-lg border border-[var(--color-border-panel)] bg-[var(--color-border-panel)] text-sm sm:grid-cols-4">
        {[
          ["Statement opening", kes(opening)],
          ["Matched lines", kes(cleared)],
          ["Statement closing", kes(closing)],
          ["Difference", kes(diff)],
        ].map(([k, v]) => (
          <div key={k} className="bg-card px-3 py-2">
            <dt className="text-xs text-muted-foreground">{k}</dt>
            <dd className={cn("font-semibold tabular-nums", k === "Difference" && diff !== 0 && "text-status-error-text")}>{v}</dd>
          </div>
        ))}
      </dl>
      <p className={cn("flex items-start gap-2 rounded-md border px-3 py-2 text-sm", diff === 0 ? TONE_BOX.success : TONE_BOX.warning)}>
        {diff === 0 ? <CircleCheckIcon className="mt-0.5 size-4 shrink-0" aria-hidden /> : <TriangleAlertIcon className="mt-0.5 size-4 shrink-0" aria-hidden />}
        <span>
          {diff === 0
            ? `Reconciled. ${outstanding.length} book ${outstanding.length === 1 ? "entry is outstanding and carries" : "entries are outstanding and carry"} to the next statement.`
            : `${toRecord.length} statement ${toRecord.length === 1 ? "line isn't" : "lines aren't"} matched${toRecord.length <= 3 ? ` (${toRecord.map((s) => s.details).join(", ")})` : ""}. Match ${toRecord.length === 1 ? "it" : "them"} to the book, or record any that aren't in Medora, to clear the ${kes(Math.abs(diff))} difference.`}
          {pickS ? <span className="mt-1 flex items-center gap-1 font-medium"><LinkIcon className="size-3.5" aria-hidden /> Now tick the book entry that matches it.</span> : null}
        </span>
      </p>
      <div className="grid gap-4 xl:grid-cols-2">
        {side("Bank statement", statement, true)}
        {side("Medora book (1040 Equity Bank)", book, false)}
      </div>
    </div>
  );
}

/* ------------------------------------- 19. Claims ------------------------------------- */

export type ClaimStatus = "Draft" | "Submitted" | "Part paid" | "Paid" | "Rejected";
export type Claim = { ref: string; scheme: string; member: string; sale: string; date: string; submitted?: string; claimed: number; paid: number; status: ClaimStatus; reason?: string };

const CLAIM_STATE: Record<ClaimStatus, AsyncState> = { Draft: "queued", Submitted: "waiting", "Part paid": "attention", Paid: "done", Rejected: "failed" };

export function ClaimBadge({ status }: { status: ClaimStatus }) {
  return <AsyncBadge state={CLAIM_STATE[status]} label={status} className={status === "Submitted" ? "[&>svg]:animate-none" : undefined} />;
}

/** Totals per scheme: claimed, received, still due, and how long the oldest submitted claim has waited. */
export function claimsByScheme(claims: Claim[], today: string) {
  const map = new Map<string, { claimed: number; paid: number; due: number; oldest: number }>();
  for (const c of claims) {
    if (c.status === "Draft") continue;
    const row = map.get(c.scheme) ?? { claimed: 0, paid: 0, due: 0, oldest: 0 };
    row.claimed += c.claimed;
    row.paid += c.paid;
    if (c.status === "Submitted" || c.status === "Part paid") {
      row.due += c.claimed - c.paid;
      if (c.submitted) row.oldest = Math.max(row.oldest, days(c.submitted, today));
    }
    map.set(c.scheme, row);
  }
  return [...map.entries()];
}

/* ----------------------------------- 20. Petty cash ----------------------------------- */

export type PettyEntry = { id: string; date: string; kind: "Top-up" | "Spend"; category?: string; details: string; amount: number; voucher: string; receipt: boolean };

/**
 * Petty cash book (PRD 6.11, AC-17): float, spends by category and top-ups, with the running balance.
 * A spend without a receipt is marked. The top-up needed to restore the float is the amount spent.
 */
export function PettyCashBook({ float, opening, entries, formatDate }: { float: number; /** In the box when the period starts. */ opening: number; entries: PettyEntry[]; formatDate: (iso: string) => string }) {
  const running = entries.reduce<number[]>((acc, e) => [...acc, r2((acc.at(-1) ?? opening) + (e.kind === "Top-up" ? e.amount : -e.amount))], []);
  const balance = running.at(-1) ?? opening;
  const byCat = new Map<string, number>();
  for (const e of entries) if (e.kind === "Spend") byCat.set(e.category ?? "Other", (byCat.get(e.category ?? "Other") ?? 0) + e.amount);
  const noReceipt = entries.filter((e) => e.kind === "Spend" && !e.receipt).length;
  return (
    <div className="space-y-3">
      <dl className="grid grid-cols-3 gap-px overflow-hidden rounded-lg border border-[var(--color-border-panel)] bg-[var(--color-border-panel)] text-sm">
        {[
          ["Float", kes(float)],
          ["In the box", kes(balance)],
          ["Top-up to restore", kes(r2(float - balance))],
        ].map(([k, v]) => (
          <div key={k} className="bg-card px-3 py-2">
            <dt className="text-xs text-muted-foreground">{k}</dt>
            <dd className="font-semibold tabular-nums">{v}</dd>
          </div>
        ))}
      </dl>
      <div className="grid gap-4 lg:grid-cols-[minmax(0,2fr)_minmax(0,1fr)]">
        <div className="overflow-x-auto rounded-lg border border-[var(--color-border-panel)] bg-card">
          <table className="w-full border-collapse text-sm tabular-nums">
            <caption className="sr-only">Petty cash vouchers</caption>
            <thead className="bg-[var(--color-bg-surface-subtle)] text-left text-xs text-muted-foreground">
              <tr>
                {["Date", "Voucher", "Details", "Out", "In", "Balance"].map((h) => (
                  <th key={h} scope="col" className={cn("px-3 py-2 font-medium", ["Out", "In", "Balance"].includes(h) && "text-right")}>
                    {h}
                  </th>
                ))}
              </tr>
            </thead>
            <tbody className="divide-y divide-[var(--color-border-subtle)]">
              {entries.map((e, i) => (
                <tr key={e.id}>
                  <td className="px-3 py-2 whitespace-nowrap">{formatDate(e.date)}</td>
                  <td className="px-3 py-2 font-mono text-xs">{e.voucher}</td>
                  <td className="px-3 py-2">
                    {e.details}
                    <span className="block text-xs text-muted-foreground">
                      {e.category ?? e.kind}
                      {e.kind === "Spend" && !e.receipt ? <span className="ml-1.5 font-medium text-status-warning-text">No receipt</span> : null}
                    </span>
                  </td>
                  <td className="px-3 py-2 text-right">{e.kind === "Spend" ? n2(e.amount) : ""}</td>
                  <td className="px-3 py-2 text-right">{e.kind === "Top-up" ? n2(e.amount) : ""}</td>
                  <td className="px-3 py-2 text-right font-medium">{n2(running[i])}</td>
                </tr>
              ))}
            </tbody>
          </table>
        </div>
        <div className="space-y-2">
          <h4 className="text-sm font-semibold">Spent by category</h4>
          <ul className="divide-y divide-[var(--color-border-subtle)] rounded-lg border border-[var(--color-border-panel)] text-sm bg-card">
            {[...byCat.entries()]
              .sort((a, b) => b[1] - a[1])
              .map(([c, v]) => (
                <li key={c} className="flex justify-between px-3 py-2">
                  {c}
                  <span className="tabular-nums">{n2(v)}</span>
                </li>
              ))}
          </ul>
          {noReceipt ? <p className="text-xs text-status-warning-text">{noReceipt} {noReceipt === 1 ? "spend has" : "spends have"} no receipt attached.</p> : null}
        </div>
      </div>
    </div>
  );
}

/* --------------------------------- 21. Asset register --------------------------------- */

export type Asset = { code: string; name: string; category: string; location: string; allocatedTo?: string; serial?: string; bought: string; cost: number; ratePct: number; status: "In use" | "In repair" | "Disposed" };

/** Straight-line depreciation at the yearly % of cost, by whole months, never below zero. */
export function depreciation(a: Asset, asAt: string) {
  const b = new Date(a.bought);
  const t = new Date(asAt);
  const months = Math.max(0, (t.getFullYear() - b.getFullYear()) * 12 + (t.getMonth() - b.getMonth()));
  const perMonth = (a.cost * a.ratePct) / 100 / 12;
  const acc = r2(Math.min(a.cost, perMonth * months));
  return { months, perYear: r2(perMonth * 12), accumulated: acc, nbv: r2(a.cost - acc) };
}

/** Year-by-year schedule: depreciation each year and net book value at year end, until fully written down. */
export function depreciationSchedule(a: Asset) {
  const rows: { year: number; charge: number; nbv: number }[] = [];
  let nbv = a.cost;
  const start = new Date(a.bought);
  const firstMonths = 12 - start.getMonth();
  let year = start.getFullYear();
  let months = firstMonths;
  while (nbv > 0.005 && rows.length < 30) {
    const charge = r2(Math.min(nbv, ((a.cost * a.ratePct) / 100 / 12) * months));
    nbv = r2(nbv - charge);
    rows.push({ year, charge, nbv });
    year += 1;
    months = 12;
  }
  return rows;
}

/* ------------------------------- 22. Quotation → invoice ------------------------------- */

export type QuoteLine = { id: string; name: string; qty: number; quoted: number; current: number; inStock: number };

/**
 * Converting a quotation (PRD 6.9, AC-15): each line is checked against today's price and stock before it
 * becomes an invoice or sale. Price changes are shown both ways; the user chooses to honour the quoted
 * price or use today's. Short stock is said per line. An expired quote says so first.
 */
export function QuoteCheck({ lines, honour }: { lines: QuoteLine[]; honour: boolean }) {
  const total = lines.reduce((s, l) => s + l.qty * (honour ? l.quoted : l.current), 0);
  return (
    <div className="overflow-x-auto rounded-lg border border-[var(--color-border-panel)] bg-card">
      <table className="w-full border-collapse text-sm tabular-nums">
        <caption className="sr-only">Quotation lines checked against today</caption>
        <thead className="bg-[var(--color-bg-surface-subtle)] text-left text-xs text-muted-foreground">
          <tr>
            {["Item", "Qty", "Quoted", "Today", "Stock", "Line total"].map((h) => (
              <th key={h} scope="col" className={cn("px-3 py-2 font-medium", h !== "Item" && "text-right")}>
                {h}
              </th>
            ))}
          </tr>
        </thead>
        <tbody className="divide-y divide-[var(--color-border-subtle)]">
          {lines.map((l) => {
            const changed = l.current !== l.quoted;
            const short = l.inStock < l.qty;
            return (
              <tr key={l.id}>
                <th scope="row" className="px-3 py-2 text-left font-medium">
                  {l.name}
                </th>
                <td className="px-3 py-2 text-right">{l.qty}</td>
                <td className={cn("px-3 py-2 text-right", honour && "font-semibold")}>{n2(l.quoted)}</td>
                <td className={cn("px-3 py-2 text-right", !honour && "font-semibold")}>
                  {n2(l.current)}
                  {changed ? <span className="block text-[11px] font-medium text-status-warning-text">{l.current > l.quoted ? "Up" : "Down"} {n2(Math.abs(l.current - l.quoted))}</span> : null}
                </td>
                <td className="px-3 py-2 text-right">
                  {l.inStock}
                  {short ? <span className="block text-[11px] font-medium text-status-error-text">{l.qty - l.inStock} short</span> : null}
                </td>
                <td className="px-3 py-2 text-right">{n2(l.qty * (honour ? l.quoted : l.current))}</td>
              </tr>
            );
          })}
        </tbody>
        <tfoot className="border-t-2 border-[var(--color-border-panel)] font-semibold">
          <tr>
            <th scope="row" colSpan={5} className="px-3 py-2 text-left">
              Total before VAT, at {honour ? "quoted" : "today's"} prices
            </th>
            <td className="px-3 py-2 text-right">{n2(total)}</td>
          </tr>
        </tfoot>
      </table>
    </div>
  );
}
/* ------------------------------- 19b. Claim payment ------------------------------- */

/**
 * Record a scheme's payment against a claim (PRD 6.7, AC-13): can't exceed what's still due. Paying less
 * leaves it Part paid unless the shortfall is written off with the scheme's reason.
 */
export function ClaimPaymentForm({ claim, onSave }: { claim: Claim; onSave: (p: { amount: number; reference: string; writeOff: boolean; reason?: string }) => void }) {
  const due = r2(claim.claimed - claim.paid);
  const [amount, setAmount] = React.useState(String(due));
  const [reference, setReference] = React.useState("");
  const [writeOff, setWriteOff] = React.useState(false);
  const [reason, setReason] = React.useState("");
  const [tried, setTried] = React.useState(false);
  const n = Number(amount.replace(/,/g, ""));
  const short = r2(due - (n || 0));
  const errs = {
    amount: !(n > 0) ? "Enter the amount the scheme paid." : n > due ? `Only ${kes(due)} is still due on this claim.` : undefined,
    reference: reference.trim() ? undefined : "Enter the remittance or EFT reference.",
    reason: writeOff && short > 0 && !reason.trim() ? "Say why the scheme paid less (from its remittance advice)." : undefined,
  };
  return (
    <div className="space-y-3 text-sm">
      <p>
        {claim.ref} · {claim.scheme} · due <span className="font-semibold tabular-nums">{kes(due)}</span>
      </p>
      <div className="grid gap-3 sm:grid-cols-2">
        <label className="space-y-1">
          <span className="font-medium">Amount received (KES)</span>
          <Input className="tabular-nums" inputMode="decimal" value={amount} onChange={(e) => setAmount(e.target.value)} aria-invalid={tried && !!errs.amount} />
          {tried && errs.amount ? <span className="block text-xs text-status-error-text">{errs.amount}</span> : null}
        </label>
        <label className="space-y-1">
          <span className="font-medium">Remittance reference</span>
          <Input className="font-mono" value={reference} onChange={(e) => setReference(e.target.value)} aria-invalid={tried && !!errs.reference} />
          {tried && errs.reference ? <span className="block text-xs text-status-error-text">{errs.reference}</span> : null}
        </label>
      </div>
      {n > 0 && short > 0 ? (
        <div className="space-y-2 rounded-md border border-[var(--color-border-panel)] p-3 bg-card">
          <p>
            {kes(short)} less than claimed.{" "}
            <label className="ml-1 inline-flex items-center gap-1.5">
              <Checkbox checked={writeOff} onCheckedChange={(v) => setWriteOff(!!v)} /> Write off the difference
            </label>
          </p>
          {writeOff ? (
            <label className="block space-y-1">
              <span className="font-medium">Scheme&apos;s reason</span>
              <Input placeholder="e.g. Paid at scheme tariff" value={reason} onChange={(e) => setReason(e.target.value)} aria-invalid={tried && !!errs.reason} />
              {tried && errs.reason ? <span className="block text-xs text-status-error-text">{errs.reason}</span> : null}
            </label>
          ) : (
            <p className="text-xs text-muted-foreground">The claim stays Part paid with {kes(short)} still due.</p>
          )}
        </div>
      ) : null}
      <div className="flex justify-end">
        <Button
          onClick={() => {
            setTried(true);
            if (!Object.values(errs).some(Boolean)) onSave({ amount: n, reference: reference.trim(), writeOff: writeOff && short > 0, reason: reason.trim() || undefined });
          }}
        >
          Record payment
        </Button>
      </div>
    </div>
  );
}

/* ------------------------------- 20b. Petty cash entry ------------------------------- */

/**
 * A petty cash voucher (PRD 6.11): a spend can't exceed what's in the box; a spend over the receipt-free
 * limit needs a receipt. A top-up says what it brings the box to.
 */
export function PettyEntryForm({ balance, float, categories, receiptFreeLimit, onSave }: { balance: number; float: number; categories: string[]; receiptFreeLimit: number; onSave: (e: { kind: "Spend" | "Top-up"; category?: string; details: string; amount: number; receipt: boolean }) => void }) {
  const [kind, setKind] = React.useState<"Spend" | "Top-up">("Spend");
  const [category, setCategory] = React.useState(categories[0]);
  const [details, setDetails] = React.useState("");
  const [amount, setAmount] = React.useState("");
  const [receipt, setReceipt] = React.useState(false);
  const [tried, setTried] = React.useState(false);
  const n = Number(amount.replace(/,/g, ""));
  const errs = {
    details: details.trim() ? undefined : kind === "Spend" ? "Say what it was for." : "Say where the money came from.",
    amount: !(n > 0) ? "Enter the amount." : kind === "Spend" && n > balance ? `The box only has ${kes(balance)}.` : undefined,
    receipt: kind === "Spend" && n > receiptFreeLimit && !receipt ? `Spends over ${kes(receiptFreeLimit)} need a receipt.` : undefined,
  };
  const input = "w-full";
  return (
    <div className="space-y-3 text-sm">
      <div className="grid gap-3 sm:grid-cols-2">
        <label className="space-y-1">
          <span className="font-medium">Type</span>
          <NativeSelect className={input} value={kind} onChange={(e) => setKind(e.target.value as "Spend" | "Top-up")}>
            <NativeSelectOption>Spend</NativeSelectOption>
            <NativeSelectOption>Top-up</NativeSelectOption>
          </NativeSelect>
        </label>
        {kind === "Spend" ? (
          <label className="space-y-1">
            <span className="font-medium">Category</span>
            <NativeSelect className={input} value={category} onChange={(e) => setCategory(e.target.value)}>
              {categories.map((c) => (
                <NativeSelectOption key={c}>{c}</NativeSelectOption>
              ))}
            </NativeSelect>
          </label>
        ) : null}
        <label className="space-y-1">
          <span className="font-medium">Details</span>
          <Input className={input} value={details} onChange={(e) => setDetails(e.target.value)} aria-invalid={tried && !!errs.details} />
          {tried && errs.details ? <span className="block text-xs text-status-error-text">{errs.details}</span> : null}
        </label>
        <label className="space-y-1">
          <span className="font-medium">Amount (KES)</span>
          <Input className={cn(input, "tabular-nums")} inputMode="decimal" value={amount} onChange={(e) => setAmount(e.target.value)} aria-invalid={tried && !!errs.amount} />
          {tried && errs.amount ? (
            <span className="block text-xs text-status-error-text">{errs.amount}</span>
          ) : n > 0 ? (
            <span className="block text-xs text-muted-foreground">Box after: {kes(r2(kind === "Spend" ? balance - n : balance + n))}{kind === "Top-up" && balance + n > float ? ` (above the ${kes(float)} float)` : ""}</span>
          ) : null}
        </label>
      </div>
      {kind === "Spend" ? (
        <label className="flex items-center gap-2">
          <Checkbox checked={receipt} onCheckedChange={(v) => setReceipt(!!v)} /> Receipt attached
          {tried && errs.receipt ? <span className="text-xs text-status-error-text">{errs.receipt}</span> : <span className="text-xs text-muted-foreground">Needed over {kes(receiptFreeLimit)}</span>}
        </label>
      ) : null}
      <div className="flex justify-end">
        <Button
          onClick={() => {
            setTried(true);
            if (Object.values(errs).some(Boolean)) return;
            onSave({ kind, category: kind === "Spend" ? category : undefined, details: details.trim(), amount: n, receipt: kind === "Top-up" || receipt });
            setDetails("");
            setAmount("");
            setReceipt(false);
            setTried(false);
          }}
        >
          Record {kind === "Spend" ? "spend" : "top-up"}
        </Button>
      </div>
    </div>
  );
}

/* ------------------------------- 21b. Asset events ------------------------------- */

export type AssetEvent = { id: string; date: string; kind: "Allocated" | "Revoked" | "Maintenance" | "Moved"; detail: string; cost?: number; by: string };

/** An asset's history: who had it, where it was, and what was spent keeping it working (AC-18). */
export function AssetHistory({ events, formatDate }: { events: AssetEvent[]; formatDate: (iso: string) => string }) {
  const maintenance = events.filter((e) => e.kind === "Maintenance").reduce((s, e) => s + (e.cost ?? 0), 0);
  return (
    <div className="space-y-2">
      <ol className="space-y-2 border-l-2 border-[var(--color-border-panel)] pl-4 text-sm">
        {[...events].reverse().map((e) => (
          <li key={e.id}>
            <p className="text-xs text-muted-foreground">
              {formatDate(e.date)} · {e.by}
            </p>
            <p>
              <span className="font-medium">{e.kind}</span>: {e.detail}
              {e.cost ? <span className="tabular-nums"> · {kes(e.cost)}</span> : null}
            </p>
          </li>
        ))}
      </ol>
      {maintenance ? <p className="text-xs text-muted-foreground">Maintenance to date: {kes(maintenance)} (expensed, not added to the asset&apos;s cost).</p> : null}
    </div>
  );
}
