"use client";

/**
 * Purchase orders and goods received (PRD 3.3: IN-14 to IN-18, SU-04), on the Medora design system and the Inventory
 * purchasing plan (DECISIONS.md, 26 Oct 2026). The design's order form, approval card, pending-orders list, receiving
 * form, GRN list and supplier suggestion (`components/medora/purchasing.tsx`, `goods-in.tsx`, `catalog-ops.tsx`).
 *
 * Every new order is approved before it can be received; posting a GRN puts the stock in (batches), updates the
 * supplier's price list and posts to the ledger, in one step. The order opens at `?id=` so the static export needs no
 * list of ids. Not built: approval limits per person (approvals plan, PS-04), supplier bills (Accounts Part 2).
 */

import * as React from "react";
import { useRouter, useSearchParams } from "next/navigation";
import { CheckIcon, PencilIcon, PlusIcon, SendIcon, TruckIcon, XIcon } from "lucide-react";

import { PanelError } from "@/components/medora/errors";
import { MedoraPageHeader } from "@/components/medora/page-header";
import { RecordDrawer } from "@/components/medora/drawer";
import { ConfirmDialog } from "@/components/medora/confirm-dialog";
import { PendingOrders, PoApproval, PurchaseOrderForm, poProblems, poTotals, type OpenOrder, type PoDraft, type PoStatus } from "@/components/medora/purchasing";
import { GrnList, ReceiveGoodsForm, receiveCheck, type Grn, type ReceiveHeader, type ReceiveLine } from "@/components/medora/goods-in";
import { SupplierChoice } from "@/components/medora/catalog-ops";
import { StatusTimeline, type TimelineStep } from "@/components/medora/records/parties";
import { Button } from "@/components/ui/button";
import { Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle } from "@/components/ui/dialog";
import { InputGroup, InputGroupAddon, InputGroupInput } from "@/components/ui/input-group";
import { NativeSelect, NativeSelectOption } from "@/components/ui/native-select";
import { Spinner } from "@/components/ui/spinner";
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@/components/ui/table";
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
import { toast } from "@/components/ui/toast";
import { BranchesApi, ProductsApi, PurchasingApi, SuppliersApi, type GrnRow, type PoRow } from "@/lib/api";
import { useTabParam, validTab } from "@/lib/use-tab-param";
import { formatDate, formatKES, localToday } from "@/lib/format";
import { useMenuPermissions } from "@/lib/permissions";
import { Loading } from "@/components/medora/loading";

const PAGE = "Inventory/Purchase orders";
const apiError = (e: unknown, fallback: string): string => {
  const m = (e as { response?: { data?: { message?: string | string[] } } })?.response?.data?.message;
  return Array.isArray(m) ? m.join(" ") : m || fallback;
};
const kes = (n: number) => formatKES(n, { decimals: true });
/** API status → the design's words ("Pending" orders from before approval count as approved, Q2). */
const designStatus = (s: string): PoStatus => (s === "Partially Received" ? "Partly received" : s === "Pending" ? "Approved" : (s as PoStatus));
const toGrn = (g: GrnRow): Grn => ({ no: g.number, date: g.date, supplier: g.supplier, po: g.po, dnNo: g.deliveryNoteNo, cuNo: g.cuNumber, location: g.branch, lines: g.lines.filter((l) => l.accepted > 0).map((l) => ({ product: l.product, batch: l.batch ?? "", expiry: l.expiry ?? "", qty: l.accepted, unitCost: l.unitCost, vat: (l.vatRate ? 16 : 0) as 0 | 16 })) });

type Lookups = { suppliers: { id: number; name: string; leadDays: number; status: string }[]; branches: { id: number; name: string }[]; products: { id: number; name: string; costPrice: number }[] };

export default function PurchaseOrdersPage() {
  return (
    <React.Suspense fallback={<Loading what="purchase orders" columns={5} rows={6} toolbar={1} numeric={[4]} />}>
      <PurchaseOrders />
    </React.Suspense>
  );
}

function PurchaseOrders() {
  const [tabParam, setTabParam] = useTabParam(); // page tabs change the URL (tabs spec 7.1)
  const router = useRouter();
  const params = useSearchParams();
  const openId = Number(params.get("id")) || null;
  const { can, loaded } = useMenuPermissions();
  const may = (a: string) => loaded && can(PAGE, a);
  const [rows, setRows] = React.useState<PoRow[] | null>(null);
  const [failed, setFailed] = React.useState(false); // R12 (card 037): a failed load says so, with Try again
  const [grns, setGrns] = React.useState<GrnRow[] | null>(null);
  const [look, setLook] = React.useState<Lookups | null>(null);
  const [status, setStatus] = React.useState("Open");
  const [search, setSearch] = React.useState("");
  const [form, setForm] = React.useState<{ id?: number; d: PoDraft; supplierId?: number } | null>(null);
  const [grnOpen, setGrnOpen] = React.useState<GrnRow | null>(null);
  const [version, setVersion] = React.useState(0);
  const refresh = () => setVersion((v) => v + 1);
  const canRead = may("Read");

  React.useEffect(() => {
    if (!canRead) return;
    PurchasingApi.list({ status, search: search || undefined })
      .then((r) => (setRows(r), setFailed(false)))
      .catch((e) => {
        setRows([]);
        setFailed(true);
        toast.add({ type: "error", title: "Couldn't load orders", description: apiError(e, "Try again.") });
      });
  }, [canRead, status, search, version]);
  React.useEffect(() => {
    if (!canRead) return;
    PurchasingApi.grns().then(setGrns).catch(() => setGrns([]));
    // Each list on its own, so one failing doesn't stop orders being raised from the others
    Promise.all([
      SuppliersApi.getAll().catch(() => {
        toast.add({ type: "error", title: "Couldn't load suppliers", description: "New orders need the supplier list. Try again." });
        return [];
      }),
      BranchesApi.getAll().catch(() => []),
      ProductsApi.getAll().catch(() => []),
    ]).then(([s, b, p]) =>
      setLook({
        suppliers: (s as { id: number; name: string; leadDays?: number | null; status: string }[]).map((x) => ({ id: x.id, name: x.name, leadDays: x.leadDays ?? 0, status: x.status })),
        branches: (b as { id: number; name: string }[]).map((x) => ({ id: x.id, name: x.name })),
        products: (p as { id: number; name: string; costPrice?: number; type?: string }[]).filter((x) => x.type !== "Service").map((x) => ({ id: x.id, name: x.name, costPrice: Number(x.costPrice) || 0 })),
      }),
    );
  }, [canRead, version]);

  if (loaded && !canRead) return <div className="medora mx-auto w-full max-w-6xl py-10 text-center text-sm text-muted-foreground">Your role can&apos;t see purchase orders.</div>;
  const blank = (): PoDraft => ({ supplier: "", supplierRef: "", orderDate: localToday(), deadline: "", deliverTo: look?.branches.length === 1 ? look.branches[0].name : "", notes: "", lines: [{ id: 1, product: "", qty: "", unitCost: "", vat: 16 }] });
  const open: OpenOrder[] = (rows ?? []).map((o) => ({ number: o.number, supplier: o.supplier, status: designStatus(o.status), deadline: o.deadline ?? "", lines: o.lines.map((l) => ({ product: l.product, ordered: l.ordered, received: l.received, unitCost: l.unitCost })) }));

  return (
    <div className="medora mx-auto w-full max-w-6xl space-y-4">
      <MedoraPageHeader
        title="Purchase orders"
        description="Orders are approved before they're sent; receiving them puts the stock in."
        primary={may("Create") && look ? { label: "New order", icon: PlusIcon, onSelect: () => setForm({ d: blank() }) } : undefined}
      />
      <Tabs value={validTab(tabParam, ["orders", "on-order", "grns"], "orders")} onValueChange={(v) => setTabParam(String(v))}>
        <TabsList variant="line" className="w-full justify-start border-b border-border">
          <TabsTrigger value="orders">Orders</TabsTrigger>
          <TabsTrigger value="on-order">On order</TabsTrigger>
          <TabsTrigger value="grns">Goods received</TabsTrigger>
        </TabsList>
        <TabsContent value="orders" className="mt-4">
          {/* One container (data table spec §1): the filters sit inside it, above the table, as on Goods received */}
          <div className="overflow-hidden rounded-md border border-[var(--color-border-panel)] bg-card">
          <div className="flex flex-wrap items-center gap-2 border-b border-[var(--color-border-subtle)] p-3">
            <InputGroup className="max-w-xs">
              <InputGroupAddon>
                <TruckIcon />
              </InputGroupAddon>
              <InputGroupInput placeholder="Order number, supplier or their ref" value={search} onChange={(e) => setSearch(e.target.value)} aria-label="Search orders" />
            </InputGroup>
            <NativeSelect aria-label="Status" value={status} onChange={(e) => setStatus(e.target.value)}>
              {["Open", "Draft", "Awaiting approval", "Approved", "Partially Received", "Received", "Cancelled", "All"].map((s) => (
                <NativeSelectOption key={s} value={s}>
                  {s === "Open" ? "Open orders" : s === "Partially Received" ? "Partly received" : s === "All" ? "All orders" : s}
                </NativeSelectOption>
              ))}
            </NativeSelect>
          </div>
          <div className="overflow-x-auto">
            <Table>
              <TableHeader>
                <TableRow>
                  <TableHead>Order</TableHead>
                  <TableHead>Supplier</TableHead>
                  <TableHead>Status</TableHead>
                  <TableHead>Deadline</TableHead>
                  <TableHead className="text-right">Value with VAT</TableHead>
                </TableRow>
              </TableHeader>
              <TableBody>
                {failed ? (
                  <TableRow>
                <TableCell colSpan={5}>
                  <PanelError what="purchase orders" onRetry={refresh} />
                </TableCell>
              </TableRow>
                ) : !rows ? (
                  <TableRow>
                    <TableCell colSpan={5} className="py-8 text-center">
                      <Loading what="purchase orders" rows={4} columns={5} bare />
                    </TableCell>
                  </TableRow>
                ) : (
                  rows.map((o) => (
                    <TableRow key={o.id} className="cursor-pointer" onClick={() => router.push(`/purchase-orders?id=${o.id}`)}>
                      <TableCell>
                        <span className="font-mono text-xs font-medium text-primary">{o.number}</span>
                        <span className="block text-xs text-muted-foreground">
                          {formatDate(o.orderDate)}
                          {o.raisedBy ? `, ${o.raisedBy}` : ""}
                        </span>
                      </TableCell>
                      <TableCell>{o.supplier}</TableCell>
                      <TableCell>
                        {designStatus(o.status)}
                        {o.status === "Draft" && o.rejectReason ? <span className="block text-xs text-status-warning-text">Sent back: {o.rejectReason}</span> : null}
                      </TableCell>
                      <TableCell>{o.deadline ? formatDate(o.deadline) : "–"}</TableCell>
                      <TableCell className="text-right tabular-nums">{kes(o.value)}</TableCell>
                    </TableRow>
                  ))
                )}
                {rows && !rows.length ? (
                  <TableRow>
                    <TableCell colSpan={5} className="py-8 text-center text-muted-foreground">
                      No orders here.
                    </TableCell>
                  </TableRow>
                ) : null}
              </TableBody>
            </Table>
          </div>
          </div>
        </TabsContent>
        <TabsContent value="on-order" className="mt-4">
          {rows ? <PendingOrders orders={open} today={localToday()} /> : <Loading what="orders on order" columns={5} rows={4} numeric={[4]} />}
          <p className="mt-2 text-xs text-muted-foreground">Uses the orders listed on the Orders tab: choose &quot;Open orders&quot; there to see everything still to arrive.</p>
        </TabsContent>
        <TabsContent value="grns" className="mt-4">
          {grns ? <GrnList grns={grns.map(toGrn)} onOpen={(no) => setGrnOpen(grns.find((g) => g.number === no) ?? null)} /> : <Loading what="goods received" columns={7} rows={5} toolbar={2} numeric={[4, 5, 6]} />}
        </TabsContent>
      </Tabs>
      {form && look ? (
        <OrderForm
          initial={form}
          look={look}
          onClose={() => setForm(null)}
          onSaved={(id) => {
            setForm(null);
            refresh();
            router.push(`/purchase-orders?id=${id}`);
          }}
        />
      ) : null}
      {openId && look ? <OrderDrawer id={openId} look={look} may={may} onClose={() => router.push("/purchase-orders")} onEdit={(o) => setForm({ id: o.id, supplierId: o.supplierId, d: toDraft(o, look) })} onChanged={refresh} /> : null}
      <GrnDialog grn={grnOpen} onClose={() => setGrnOpen(null)} />
    </div>
  );
}

function toDraft(o: PoRow, look: Lookups): PoDraft {
  return {
    supplier: o.supplier, supplierRef: o.supplierRef ?? "", orderDate: o.orderDate, deadline: o.deadline ?? "", deliverTo: look.branches.find((b) => b.id === o.deliverToBranchId)?.name ?? "", notes: o.notes ?? "",
    lines: o.lines.map((l, i) => ({ id: i + 1, product: l.product, qty: String(l.ordered), unitCost: String(l.unitCost), vat: (l.vatRate ? 16 : 0) as 0 | 16 })),
  };
}

/* ------------------------------------ New or edit order (IN-14, SU-04) ------------------------------------ */

function OrderForm({ initial, look, onClose, onSaved }: { initial: { id?: number; d: PoDraft; supplierId?: number }; look: Lookups; onClose: () => void; onSaved: (id: number) => void }) {
  const [d, setD] = React.useState(initial.d);
  const [tried, setTried] = React.useState(false);
  const [saving, setSaving] = React.useState(false);
  const [lastCosts, setLastCosts] = React.useState<Record<string, number>>({});
  const [quotes, setQuotes] = React.useState<Record<string, { supplier: string; cost: number; lastReceived: string; usual?: boolean; stale?: boolean }[]>>({});
  const supplier = look.suppliers.find((s) => s.name === d.supplier);
  React.useEffect(() => {
    if (!supplier) return setLastCosts({});
    SuppliersApi.prices(supplier.id)
      .then((p) => setLastCosts(Object.fromEntries(p.map((x) => [x.product, x.cost]))))
      .catch(() => setLastCosts({}));
  }, [supplier?.id]); // eslint-disable-line react-hooks/exhaustive-deps
  const byName = (n: string) => look.products.find((p) => p.name.toLowerCase() === n.trim().toLowerCase());
  const named = d.lines.filter((l) => l.product.trim());
  const unknown = named.filter((l) => !byName(l.product)).map((l) => l.product);
  // SU-04: each named product's suppliers, cheapest current first
  React.useEffect(() => {
    for (const l of named) {
      const p = byName(l.product);
      if (!p || quotes[l.product]) continue;
      SuppliersApi.productPrices(p.id)
        .then((r) => {
          const age = (iso: string) => (Date.now() - new Date(iso).getTime()) / 86400000;
          setQuotes((q) => ({ ...q, [l.product]: r.prices.filter((x) => x.lastReceived).map((x) => ({ supplier: x.supplier, cost: x.cost, lastReceived: x.lastReceived!.slice(0, 10), stale: age(x.lastReceived!) > 90 })) }));
        })
        .catch(() => undefined);
    }
  }, [named.map((l) => l.product).join("|")]); // eslint-disable-line react-hooks/exhaustive-deps
  const problems = [...poProblems(d), ...unknown.map((n) => `"${n}" isn't one of your products. Pick its exact name from Products and stock.`)];
  const save = async (submit: boolean) => {
    setTried(true);
    if (problems.length || !supplier) return;
    setSaving(true);
    try {
      const body = {
        supplierRef: d.supplierRef, deadline: d.deadline, deliverToBranchId: look.branches.find((b) => b.name === d.deliverTo)?.id ?? null, notes: d.notes,
        lines: named.map((l) => ({ productId: byName(l.product)!.id, quantity: Number(l.qty), unitCost: Number(String(l.unitCost).replace(/,/g, "")) })),
      };
      let id = initial.id;
      if (id) {
        await PurchasingApi.editDraft(id, body);
        if (submit) await PurchasingApi.submit(id);
      } else id = (await PurchasingApi.create({ ...body, supplierId: supplier.id, submit })).id;
      toast.add({ title: submit ? "Sent for approval" : "Draft saved" });
      onSaved(id!);
    } catch (e) {
      toast.add({ type: "error", title: "Not saved", description: apiError(e, "Try again.") });
    } finally {
      setSaving(false);
    }
  };
  const t = poTotals(d.lines.map((l) => ({ qty: l.qty, unitCost: l.unitCost, vat: l.vat })));
  return (
    <RecordDrawer
      open
      onOpenChange={(o) => !o && onClose()}
      variant="edit"
      width="lg"
      saving={saving}
      dirty={JSON.stringify(d) !== JSON.stringify(initial.d)}
      title={initial.id ? "Edit draft order" : "New purchase order"}
      footer={
        <>
          <Button variant="outline" disabled={saving} onClick={onClose}>
            Cancel
          </Button>
          <Button variant="outline" disabled={saving} onClick={() => void save(false)}>
            Save draft
          </Button>
          <Button disabled={saving} onClick={() => void save(true)}>
            {saving ? <Spinner /> : <SendIcon data-icon="inline-start" />}
            Send for approval
          </Button>
        </>
      }
    >
      <div className="space-y-4">
        {initial.id ? <p className="text-xs text-muted-foreground">The supplier can&apos;t change on a saved order: cancel it and raise a new one instead.</p> : null}
        <PurchaseOrderForm d={d} onChange={(x) => setD(initial.id ? { ...x, supplier: d.supplier } : x)} suppliers={look.suppliers.filter((s) => s.status !== "Inactive")} locations={look.branches.map((b) => b.name)} lastCosts={lastCosts} tried={tried} products={look.products.map((p) => p.name)} />
        {tried && problems.length ? (
          <ul className="list-disc space-y-0.5 pl-5 text-sm text-status-error-text">
            {problems.map((p) => (
              <li key={p}>{p}</li>
            ))}
          </ul>
        ) : null}
        {named.some((l) => quotes[l.product]?.length) ? (
          <section className="space-y-2">
            <h3 className="text-sm font-semibold">Who sells these cheapest</h3>
            {named
              .filter((l) => quotes[l.product]?.length)
              .map((l) => (
                <SupplierChoice key={l.id} product={l.product} quotes={quotes[l.product].map((q) => ({ ...q, usual: q.supplier === d.supplier }))} value={d.supplier} onChange={(s) => (initial.id ? toast.add({ title: "Raise a separate order for that supplier" }) : setD({ ...d, supplier: s }))} />
              ))}
          </section>
        ) : null}
        <p className="text-right text-sm tabular-nums">
          {kes(t.subtotal)} before VAT, {kes(t.vat)} VAT, <strong>{kes(t.total)}</strong>
        </p>
      </div>
    </RecordDrawer>
  );
}

/* ------------------------------------ One order: approval, receiving, GRNs ------------------------------------ */

function steps(o: PoRow): TimelineStep[] {
  const s = o.status;
  const at = (iso: string | null) => (iso ? formatDate(String(iso).slice(0, 10), { short: true }) : undefined);
  if (s === "Cancelled") return [{ label: "Raised", at: at(o.orderDate), state: "done" }, { label: "Cancelled", state: "failed" }];
  const approved = ["Approved", "Pending", "Partially Received", "Received"].includes(s);
  return [
    { label: "Raised", at: at(o.orderDate), state: "done" },
    { label: s === "Draft" && o.rejectReason ? "Sent back" : "Awaiting approval", at: at(o.submittedAt), state: s === "Draft" ? (o.rejectReason ? "failed" : "todo") : s === "Awaiting approval" ? "current" : "done" },
    { label: "Approved", at: at(o.approvedAt), state: approved ? "done" : "todo" },
    { label: s === "Partially Received" ? "Partly received" : "Received", at: at(o.receivedDate), state: s === "Received" ? "done" : s === "Partially Received" ? "current" : "todo" },
  ];
}

function OrderDrawer({ id, look, may, onClose, onEdit, onChanged }: { id: number; look: Lookups; may: (a: string) => boolean; onClose: () => void; onEdit: (o: PoRow) => void; onChanged: () => void }) {
  const [o, setO] = React.useState<(PoRow & { grns: GrnRow[] }) | null>(null);
  const [receiving, setReceiving] = React.useState(false);
  const [cancelling, setCancelling] = React.useState(false);
  const me = React.useMemo(() => {
    try {
      return JSON.parse(localStorage.getItem("user") || "{}") as { id?: number; username?: string };
    } catch {
      return {};
    }
  }, []);
  const load = React.useCallback(() => {
    PurchasingApi.detail(id)
      .then(setO)
      .catch((e) => {
        toast.add({ type: "error", title: "Couldn't open the order", description: apiError(e, "It may belong to another pharmacy.") });
        onClose();
      });
  }, [id]); // eslint-disable-line react-hooks/exhaustive-deps
  React.useEffect(load, [load]);
  const act = async (fn: () => Promise<unknown>, done: string) => {
    try {
      await fn();
      toast.add({ title: done });
      load();
      onChanged();
    } catch (e) {
      toast.add({ type: "error", title: "Not done", description: apiError(e, "Try again.") });
    }
  };
  const receivable = o && ["Approved", "Pending", "Partially Received"].includes(o.status);
  return (
    <RecordDrawer open onOpenChange={(x) => !x && onClose()} variant="detail" width="lg" title={o ? `${o.number}, ${o.supplier}` : "Purchase order"}>
      {!o ? (
        <Loading what="the order" shape="detail" />
      ) : (
        <div className="space-y-5">
          <StatusTimeline steps={steps(o)} />
          {o.status === "Draft" && o.rejectReason ? <p className="text-sm text-status-warning-text">Sent back: {o.rejectReason}</p> : null}
          <div className="flex flex-wrap gap-2">
            {o.status === "Draft" && may("Update") ? (
              <>
                <Button size="sm" variant="outline" onClick={() => onEdit(o)}>
                  <PencilIcon data-icon="inline-start" />
                  Edit draft
                </Button>
                <Button size="sm" onClick={() => void act(() => PurchasingApi.submit(o.id), "Sent for approval")}>
                  <SendIcon data-icon="inline-start" />
                  Send for approval
                </Button>
              </>
            ) : null}
            {receivable && may("Update") ? (
              <Button size="sm" onClick={() => setReceiving(true)}>
                <CheckIcon data-icon="inline-start" />
                Receive goods
              </Button>
            ) : null}
            {!["Received", "Cancelled", "Partially Received"].includes(o.status) && may("Update") ? (
              <Button size="sm" variant="ghost" className="text-status-error-text" onClick={() => setCancelling(true)}>
                <XIcon data-icon="inline-start" />
                Cancel order
              </Button>
            ) : null}
          </div>
          {o.status === "Awaiting approval" ? (
            may("Approve") ? (
              <PoApproval
                number={o.number}
                supplier={o.supplier}
                total={o.value}
                raisedBy={o.raisedBy ?? "Unknown"}
                raisedAt={o.submittedAt ? new Date(o.submittedAt).toLocaleString("en-GB", { day: "2-digit", month: "short", hour: "2-digit", minute: "2-digit" }) : formatDate(o.orderDate)}
                viewer={o.raisedById != null && o.raisedById === me.id ? (o.raisedBy ?? "") : (me.username ?? "you")}
                viewerLimit={Number.MAX_SAFE_INTEGER}
                onDecide={(dec) => void act(() => (dec.approved ? PurchasingApi.approve(o.id) : PurchasingApi.reject(o.id, dec.reason ?? "")), dec.approved ? `${o.number} approved` : `${o.number} sent back`)}
              />
            ) : (
              <p className="text-sm text-muted-foreground">Waiting for someone with the Approve permission on purchase orders.</p>
            )
          ) : null}
          <dl className="grid gap-x-6 gap-y-2 text-sm sm:grid-cols-3">
            {[
              ["Their reference", o.supplierRef || "–"],
              ["Deadline", o.deadline ? formatDate(o.deadline) : "–"],
              ["Deliver to", look.branches.find((b) => b.id === o.deliverToBranchId)?.name ?? "–"],
              ["Raised by", o.raisedBy ?? "–"],
              ["Approved by", o.approvedBy ?? "–"],
              ["Notes", o.notes || "–"],
            ].map(([k, v]) => (
              <div key={k}>
                <dt className="text-xs text-muted-foreground">{k}</dt>
                <dd>{v}</dd>
              </div>
            ))}
          </dl>
          <div className="overflow-x-auto rounded-lg border border-[var(--color-border-panel)] bg-card">
            <Table>
              <TableHeader>
                <TableRow>
                  <TableHead>Item</TableHead>
                  <TableHead className="text-right">Ordered</TableHead>
                  <TableHead className="text-right">Received</TableHead>
                  <TableHead className="text-right">Unit cost</TableHead>
                  <TableHead className="text-right">VAT</TableHead>
                  <TableHead className="text-right">Value</TableHead>
                </TableRow>
              </TableHeader>
              <TableBody>
                {o.lines.map((l) => (
                  <TableRow key={l.id}>
                    <TableCell>{l.product}</TableCell>
                    <TableCell className="text-right tabular-nums">{l.ordered}</TableCell>
                    <TableCell className="text-right tabular-nums">{l.received}</TableCell>
                    <TableCell className="text-right tabular-nums">{kes(l.unitCost)}</TableCell>
                    <TableCell className="text-right tabular-nums">{l.vatRate}%</TableCell>
                    <TableCell className="text-right tabular-nums">{kes(l.value)}</TableCell>
                  </TableRow>
                ))}
              </TableBody>
            </Table>
          </div>
          <section className="space-y-2">
            <h3 className="text-sm font-semibold">Goods received</h3>
            {o.grns.length ? (
              <GrnList grns={o.grns.map(toGrn)} />
            ) : (
              <p className="text-sm text-muted-foreground">{o.lines.some((l) => l.received > 0) ? "Received before goods received notes existed (or from an older app), with quantities only: no delivery note, CU number or batches." : "Nothing received yet."}</p>
            )}
          </section>
          {receiving ? (
            <ReceiveDrawer
              order={o}
              look={look}
              onClose={() => setReceiving(false)}
              onDone={() => {
                setReceiving(false);
                load();
                onChanged();
              }}
            />
          ) : null}
          <ConfirmDialog
            open={cancelling}
            onOpenChange={setCancelling}
            level="B"
            title={`Cancel ${o.number}?`}
            consequences={["The supplier should be told the order is off. Nothing has been received, so no stock or money changes."]}
            actionLabel="Cancel order"
            busyLabel="Cancelling…"
            reasonHelp="Kept on the order and in the activity log."
            onConfirm={async ({ reason }) => {
              try {
                await PurchasingApi.cancel(o.id, reason ?? "");
              } catch (e) {
                throw new Error(apiError(e, "Not cancelled."));
              }
              toast.add({ title: `${o.number} cancelled` });
              setCancelling(false);
              load();
              onChanged();
            }}
          />
        </div>
      )}
    </RecordDrawer>
  );
}

function ReceiveDrawer({ order, look, onClose, onDone }: { order: PoRow; look: Lookups; onClose: () => void; onDone: () => void }) {
  const [header, setHeader] = React.useState<ReceiveHeader>({ po: order.number, supplier: order.supplier, dnNo: "", cuNo: "", location: look.branches.find((b) => b.id === order.deliverToBranchId)?.name ?? (look.branches.length === 1 ? look.branches[0].name : ""), date: localToday() });
  const [lines, setLines] = React.useState<ReceiveLine[]>(order.lines.filter((l) => l.outstanding > 0).map((l) => ({ id: l.id, product: l.product, ordered: l.ordered, before: l.received, unitCost: l.unitCost, vat: (l.vatRate ? 16 : 0) as 0 | 16, delivered: String(l.outstanding), received: String(l.outstanding), batch: "", expiry: "", reason: "" })));
  const [tried, setTried] = React.useState(false);
  const [saving, setSaving] = React.useState(false);
  const check = receiveCheck(header, lines, localToday());
  const post = async () => {
    setTried(true);
    if (check.blocking.length) return;
    setSaving(true);
    try {
      const r = await PurchasingApi.receive(order.id, {
        deliveryNoteNo: header.dnNo.trim(), cuNumber: header.cuNo.replace(/\s/g, ""), branchId: look.branches.find((b) => b.name === header.location)?.id ?? null, date: header.date,
        lines: lines.map((l) => ({ poItemId: l.id, delivered: Number(l.delivered) || 0, accepted: Number(l.received) || 0, reason: l.reason || null, batch: l.batch.trim(), expiry: l.expiry })),
      });
      toast.add({ title: `${r.grn.number} posted: ${kes(r.grn.total)} of goods in stock` });
      onDone();
    } catch (e) {
      toast.add({ type: "error", title: "Not posted", description: apiError(e, "Try again.") });
    } finally {
      setSaving(false);
    }
  };
  return (
    <RecordDrawer
      open
      onOpenChange={(x) => !x && onClose()}
      variant="edit"
      width="lg"
      saving={saving}
      dirty
      title={`Receive goods on ${order.number}`}
      footer={
        <>
          <Button variant="outline" disabled={saving} onClick={onClose}>
            Cancel
          </Button>
          <Button disabled={saving} onClick={() => void post()}>
            {saving ? <Spinner /> : null}
            Post goods received note
          </Button>
        </>
      }
    >
      <ReceiveGoodsForm header={header} onHeader={setHeader} lines={lines} onLines={setLines} locations={look.branches.map((b) => b.name)} check={check} tried={tried} />
      <p className="mt-3 text-xs text-muted-foreground">Posting puts the accepted units into stock as new batches, updates {order.supplier}&apos;s price list and records what you owe them.</p>
    </RecordDrawer>
  );
}

function GrnDialog({ grn, onClose }: { grn: GrnRow | null; onClose: () => void }) {
  return (
    <Dialog open={!!grn} onOpenChange={(o) => !o && onClose()}>
      <DialogContent className="sm:max-w-3xl">
        <DialogHeader>
          <DialogTitle>
            {grn?.number}, {grn?.supplier}
          </DialogTitle>
          <DialogDescription>
            {grn ? `${formatDate(grn.date)} on ${grn.po}. Delivery note ${grn.deliveryNoteNo}, KRA CU ${grn.cuNumber}${grn.branch ? `, into ${grn.branch}` : ""}${grn.by ? `, by ${grn.by}` : ""}.` : ""}
          </DialogDescription>
        </DialogHeader>
        {grn ? (
          <div className="overflow-x-auto">
            <Table>
              <TableHeader>
                <TableRow>
                  <TableHead>Item and batch</TableHead>
                  <TableHead className="text-right">Delivered</TableHead>
                  <TableHead className="text-right">Accepted</TableHead>
                  <TableHead>Not accepted because</TableHead>
                  <TableHead className="text-right">Value</TableHead>
                </TableRow>
              </TableHeader>
              <TableBody>
                {grn.lines.map((l) => (
                  <TableRow key={l.id}>
                    <TableCell>
                      {l.product}
                      {l.batch ? (
                        <span className="block text-xs text-muted-foreground">
                          <span className="font-mono">{l.batch}</span>
                          {l.expiry ? `, exp ${formatDate(l.expiry)}` : ""}
                        </span>
                      ) : null}
                    </TableCell>
                    <TableCell className="text-right tabular-nums">{l.delivered}</TableCell>
                    <TableCell className="text-right tabular-nums">{l.accepted}</TableCell>
                    <TableCell>{l.reason ?? ""}</TableCell>
                    <TableCell className="text-right tabular-nums">{kes(l.accepted * l.unitCost * (1 + l.vatRate / 100))}</TableCell>
                  </TableRow>
                ))}
              </TableBody>
            </Table>
            <p className="mt-2 text-right text-sm tabular-nums">
              {kes(grn.subtotal)} before VAT, {kes(grn.vat)} VAT, <strong>{kes(grn.total)}</strong>
            </p>
          </div>
        ) : null}
      </DialogContent>
    </Dialog>
  );
}
