"use client";

/**
 * Suppliers (PRD 6.5), on the Medora design system (Stage 7) and the Suppliers API plan (DECISIONS.md, 26 Oct 2026).
 * The list is the design's `SuppliersList` (SU-02: search, CSV import reviewed row by row, export, a statement link);
 * the record follows the detail-page pattern with the design's `SupplierHeader`: Details (SU-01, SU-07), Price list
 * (SU-03, with the design's `SupplierPrices` comparison), Received orders (SU-05 until GRNs exist, Q4), Returns
 * (SU-06, the design's `SupplierReturnLines`) and Statement (Accounts 6.3's payables, as before).
 *
 * The record opens at `?id=` so the static export (desktop) needs no list of ids. Purchase orders, approval and goods
 * received are on their own page in Inventory (`/purchase-orders`); the supplier's Goods received tab lists their GRNs.
 */

import * as React from "react";
import { useRouter, useSearchParams } from "next/navigation";
import { ArrowLeftIcon, DownloadIcon, PackageMinusIcon, PencilIcon, PlusIcon, PowerIcon, Trash2Icon } from "lucide-react";

import { PanelError } from "@/components/medora/errors";
import { MedoraPageHeader } from "@/components/medora/page-header";
import { StatusTag } from "@/components/medora/record-lists";
import { RecordDrawer } from "@/components/medora/drawer";
import { DescriptionList } from "@/components/medora/description-list";
import { ConfirmDialog } from "@/components/medora/confirm-dialog";
import { SuppliersList, type SupplierListRow } from "@/components/medora/registers";
import { SupplierForm, supplierProblems, type SupplierDraft } from "@/components/medora/purchasing";
import { SupplierHeader } from "@/components/medora/records/parties";
import { SupplierPrices } from "@/components/medora/parties-ops";
import { SupplierReturnLines } from "@/components/medora/catalog-ops";
import { GrnList } from "@/components/medora/goods-in";
import { AgingBuckets, BalanceSummary, PaymentForm, StatementTable, type PaymentFormStatus } from "@/components/medora/records/finance";
import { Button } from "@/components/ui/button";
import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle } from "@/components/ui/dialog";
import { Field, FieldDescription, FieldLabel } from "@/components/ui/field";
import { Input } from "@/components/ui/input";
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 { SuppliersApi, type SupplierPriceRow, type SupplierRecord, type SupplierReturnRow, type SupplierRow, PurchasingApi, type GrnRow } from "@/lib/api";
import { useTabParam, validTab } from "@/lib/use-tab-param";
import { formatPhone, count, formatDate, formatKES, localToday } from "@/lib/format";
import { useMenuPermissions } from "@/lib/permissions";
import { Loading } from "@/components/medora/loading";

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 day = (iso?: string | null) => (iso ? formatDate(iso) : "");
const usePerm = () => {
  const { can, loaded } = useMenuPermissions();
  return (page: string, action: string) => loaded && can(page, action);
};
/** The design's return reasons (SU-06); the API checks the same list. */
const RETURN_REASONS = ["Short expiry on delivery", "Damaged in transit", "Recalled", "Wrong item sent", "Overstocked (agreed return)"];

export default function SuppliersPage() {
  // useSearchParams needs a Suspense boundary in the static export
  return (
    <React.Suspense fallback={<Loading what="suppliers" columns={5} rows={6} toolbar={0} numeric={[3]} />}>
      <Suppliers />
    </React.Suspense>
  );
}

function Suppliers() {
  const params = useSearchParams();
  const id = Number(params.get("id")) || null;
  return id ? <SupplierProfile id={id} initialTab={params.get("tab") || "details"} /> : <SupplierListPage />;
}

/* ------------------------------------ The form (SU-01, SU-07) ------------------------------------ */

const blank = (): SupplierDraft => ({ name: "", kraPin: "", contact: "", phone: "", email: "", postal: "", address: "", city: "", country: "Kenya", bankName: "", bankBranch: "", accountNo: "", wht: false, creditLimit: "", termsDays: "30", discount: "", leadDays: "" });
const toDraft = (s: SupplierRecord): SupplierDraft => ({
  name: s.name, kraPin: s.kraPin ?? "", contact: s.contactPerson ?? "", phone: s.phone ?? "", email: s.email ?? "", postal: s.postalAddress ?? "", address: s.address ?? "", city: s.city ?? "", country: s.country ?? "",
  bankName: s.bankName ?? "", bankBranch: s.bankBranch ?? "", accountNo: s.bankAccount ?? "", wht: !!s.wht, creditLimit: s.creditLimit != null ? String(s.creditLimit) : "", termsDays: String(s.termsDays ?? 0),
  discount: s.discountPct != null ? String(s.discountPct) : "", leadDays: s.leadDays != null ? String(s.leadDays) : "",
});
/** Draft field → API field, and back for the API's per-field problems. */
const FIELD: Record<keyof SupplierDraft, string> = { name: "name", kraPin: "kraPin", contact: "contactPerson", phone: "phone", email: "email", postal: "postalAddress", address: "address", city: "city", country: "country", bankName: "bankName", bankBranch: "bankBranch", accountNo: "bankAccount", wht: "wht", creditLimit: "creditLimit", termsDays: "termsDays", discount: "discountPct", leadDays: "leadDays" };
const BANK: (keyof SupplierDraft)[] = ["bankName", "bankBranch", "accountNo"];

type Form = { id?: number; d: SupplierDraft; original: SupplierDraft; otherPins: string[]; showBank: boolean };

function SupplierFormDrawer({ form, onClose, onSaved }: { form: Form; onClose: () => void; onSaved: (s: { id: number; name: string }) => void }) {
  const [d, setD] = React.useState(form.d);
  const [tried, setTried] = React.useState(false);
  const [server, setServer] = React.useState<Partial<Record<keyof SupplierDraft, string>>>({});
  const [saving, setSaving] = React.useState(false);
  const [duplicate, setDuplicate] = React.useState<{ id: number; name: string; by: "kraPin" | "name" } | null>(null);
  const local = supplierProblems(d, form.otherPins);
  const problems = tried ? { ...local, ...server } : server;

  const save = async (allowDuplicate = false) => {
    setTried(true);
    if (Object.keys(local).length) return;
    setSaving(true);
    setServer({});
    try {
      // Only what changed on an edit, and never bank fields the role can't see (Q2)
      const keys = (Object.keys(FIELD) as (keyof SupplierDraft)[]).filter((k) => (form.showBank || !BANK.includes(k)) && (!form.id || d[k] !== form.original[k] || k === "kraPin"));
      const body: Record<string, unknown> = Object.fromEntries(keys.map((k) => [FIELD[k], d[k]]));
      const saved = form.id ? await SuppliersApi.update(form.id, { ...body, allowDuplicate }) : await SuppliersApi.create({ ...body, allowDuplicate });
      toast.add({ title: form.id ? "Supplier saved" : "Supplier added" });
      onSaved(saved);
    } catch (e) {
      const res = (e as { response?: { status?: number; data?: { problems?: Record<string, string>; duplicate?: { id: number; name: string; by: "kraPin" | "name" } } } })?.response;
      if (res?.status === 409 && res.data?.duplicate) setDuplicate(res.data.duplicate);
      else if (res?.data?.problems) {
        const back = Object.fromEntries(Object.entries(FIELD).map(([k, v]) => [v, k]));
        setServer(Object.fromEntries(Object.entries(res.data.problems).map(([k, v]) => [back[k] ?? k, v])));
      } else toast.add({ type: "error", title: "Not saved", description: apiError(e, "Try again.") });
    } finally {
      setSaving(false);
    }
  };

  return (
    <>
      <RecordDrawer
        open
        onOpenChange={(o) => !o && onClose()}
        variant="edit"
        width="lg"
        saving={saving}
        dirty={JSON.stringify(d) !== JSON.stringify(form.original)}
        title={form.id ? `Edit ${form.original.name}` : "New supplier"}
        footer={
          <>
            <Button variant="outline" disabled={saving} onClick={onClose}>
              Cancel
            </Button>
            <Button disabled={saving} onClick={() => void save()}>
              {saving ? <Spinner /> : null}
              Save supplier
            </Button>
          </>
        }
      >
        {!form.showBank ? <p className="mb-4 text-xs text-muted-foreground">Bank details are shown only to roles with Accounts permission. Leave them blank to keep what is on file.</p> : null}
        <SupplierForm d={d} onChange={setD} problems={problems} today={localToday()} />
      </RecordDrawer>
      <Dialog open={!!duplicate} onOpenChange={(o) => !o && setDuplicate(null)}>
        <DialogContent>
          <DialogHeader>
            <DialogTitle>{duplicate?.by === "kraPin" ? `${duplicate?.name} has this KRA PIN` : `There's already a supplier called ${duplicate?.name}`}</DialogTitle>
            <DialogDescription>
              {duplicate?.by === "kraPin" ? "A KRA PIN belongs to one company. Open that supplier instead of adding a second one." : "Two records for one company split its bills, prices and returns. Open the existing supplier, or save if this is a different company."}
            </DialogDescription>
          </DialogHeader>
          <DialogFooter>
            <Button variant="outline" onClick={() => (window.location.href = `/suppliers?id=${duplicate?.id}`)}>
              Open {duplicate?.name}
            </Button>
            {duplicate?.by === "name" ? (
              <Button
                onClick={() => {
                  setDuplicate(null);
                  void save(true);
                }}
              >
                It&apos;s a different company: save
              </Button>
            ) : null}
          </DialogFooter>
        </DialogContent>
      </Dialog>
    </>
  );
}

/* ------------------------------------ The list (SU-02) ------------------------------------ */

function SupplierListPage() {
  const router = useRouter();
  const may = usePerm();
  const { loaded } = useMenuPermissions();
  const [rows, setRows] = React.useState<SupplierRow[] | null>(null);
  const [importing, setImporting] = React.useState(false);
  const [failed, setFailed] = React.useState(false); // R12 (card 037): a failed load says so, with Try again
  const [form, setForm] = React.useState<Form | null>(null);
  const [tabParam, setTab] = useTabParam(); // page tabs change the URL (tabs spec 7.1)
  const tab = validTab(tabParam, ["suppliers", "orders", "returns"], "suppliers");
  const canRead = may("Suppliers/Supplier list", "Read");

  const load = React.useCallback(async () => {
    try {
      setRows((await SuppliersApi.list()).rows);
      setFailed(false);
    } catch (e) {
      setRows([]);
      setFailed(true);
      toast.add({ type: "error", title: "Couldn't load suppliers", description: apiError(e, "Try again.") });
    }
  }, []);
  React.useEffect(() => {
    if (canRead) void load();
  }, [canRead, load]);
  const canOrders = may("Inventory/Purchase orders", "Read");
  const canReturns = may("Suppliers/Supplier returns", "Read");
  React.useEffect(() => {
    if (loaded && !canRead && canOrders) setTab("orders");
  }, [loaded, canRead, canOrders, setTab]);

  const listRows: SupplierListRow[] = (rows ?? []).map((s) => ({ id: s.id, name: s.status === "Inactive" ? `${s.name} (inactive)` : s.name, kraPin: s.kraPin ?? "", contact: s.contactPerson ?? "", phone: s.phone ?? "", owed: s.owed, termsDays: s.termsDays, overdue: s.overdue }));
  const importRows = async (r: Record<string, string>[]) => {
    const cell = (v: string) => (/[",\n]/.test(v) ? `"${v.replace(/"/g, '""')}"` : v);
    const csv = ["KRA PIN,Name,Phone,Terms days", ...r.map((x) => [x.kraPin ?? "", x.name ?? "", x.phone ?? "", x.terms ?? ""].map(cell).join(","))].join("\n");
    try {
      const res = await SuppliersApi.importCsv(csv);
      const skipped = res.results.filter((x) => x.outcome !== "Created");
      toast.add({ type: skipped.length ? "warning" : "success", title: `${res.created} supplier${res.created === 1 ? "" : "s"} added`, description: skipped.length ? skipped.slice(0, 3).map((x) => `Line ${x.row}: ${x.message}`).join(" ") : undefined });
      void load();
    } catch (e) {
      toast.add({ type: "error", title: "Import failed", description: apiError(e, "Check the file and try again.") });
    }
  };

  return (
    <div className="medora mx-auto w-full max-w-6xl space-y-4">
      <MedoraPageHeader
        title="Suppliers"
        description={tab === "suppliers" ? (rows ? `${count(rows.length, "supplier")}, ${formatKES(rows.reduce((t, r) => t + r.owed, 0))} owed` : "Loading…") : tab === "orders" ? "Purchase orders" : "Stock returned to suppliers"}
        primary={tab === "suppliers" && may("Suppliers/Supplier list", "Create") ? { label: "Add supplier", icon: PlusIcon, onSelect: () => setForm({ d: blank(), original: blank(), otherPins: (rows ?? []).map((r) => r.kraPin ?? "").filter(Boolean), showBank: may("Accounts/Payables", "Read") }) } : undefined}
        secondary={tab === "suppliers" && may("Suppliers/Supplier list", "Export") ? [{ label: "Export all fields", icon: DownloadIcon, onSelect: () => void SuppliersApi.exportCsv().catch((e) => toast.add({ type: "error", title: "Export failed", description: apiError(e, "Try again.") })) }] : []}
        more={tab === "suppliers" && may("Suppliers/Supplier list", "Create") ? [{ label: "Import from CSV", onSelect: () => setImporting(true) }] : []}
      />
      <Tabs value={tab} onValueChange={(v) => setTab(String(v))}>
        <TabsList variant="line" className="w-full justify-start border-b border-border">
          {canRead ? <TabsTrigger value="suppliers">Suppliers</TabsTrigger> : null}
          {canOrders ? <TabsTrigger value="orders">Purchase orders</TabsTrigger> : null}
          {canReturns ? <TabsTrigger value="returns">Returns</TabsTrigger> : null}
        </TabsList>
        {canRead ? (
          <TabsContent value="suppliers" className="mt-4">
            {failed ? (
              <PanelError framed what="suppliers" onRetry={() => void load()} />
            ) : rows ? (
              <SuppliersList rows={listRows} importOpen={importing} onImportOpenChange={setImporting} onOpen={(id) => router.push(`/suppliers?id=${id}`)} onStatement={(id) => router.push(`/suppliers?id=${id}&tab=statement`)} onImport={(r) => void importRows(r)} />
            ) : (
              <Loading what="suppliers" columns={5} rows={6} toolbar={0} numeric={[3]} />
            )}
          </TabsContent>
        ) : null}
        {canOrders ? (
          <TabsContent value="orders" className="mt-4">
            <p className="text-sm">
              Purchase orders, approval and goods received are now in Inventory.{" "}
              <a href="/purchase-orders" className="font-medium text-primary hover:underline">
                Open Purchase orders
              </a>
            </p>
          </TabsContent>
        ) : null}
        {canReturns ? (
          <TabsContent value="returns" className="mt-4">
            <ReturnsTable />
          </TabsContent>
        ) : null}
      </Tabs>
      {form ? (
        <SupplierFormDrawer
          form={form}
          onClose={() => setForm(null)}
          onSaved={(s) => {
            setForm(null);
            router.push(`/suppliers?id=${s.id}`);
          }}
        />
      ) : null}
    </div>
  );
}

/** SU-06 returns report: every return, or one supplier's. */
function ReturnsTable({ supplierId, refresh = 0 }: { supplierId?: number; refresh?: number }) {
  const [data, setData] = React.useState<{ summary: { returns: number; units: number; creditExpected: number }; rows: SupplierReturnRow[] } | null>(null);
  React.useEffect(() => {
    SuppliersApi.returns({ supplierId })
      .then(setData)
      .catch((e) => {
        setData({ summary: { returns: 0, units: 0, creditExpected: 0 }, rows: [] });
        toast.add({ type: "error", title: "Couldn't load returns", description: apiError(e, "Try again.") });
      });
  }, [supplierId, refresh]);
  if (!data) return <Loading what="returns" columns={5} rows={4} numeric={[4]} />;
  return (
    <div className="space-y-3">
      <p className="text-sm">
        {data.summary.returns} return{data.summary.returns === 1 ? "" : "s"}, {count(data.summary.units, "unit")}. Credit expected: <strong className="tabular-nums">{formatKES(data.summary.creditExpected)}</strong>
        <span className="block text-xs text-muted-foreground">Shown until the supplier&apos;s credit note is recorded in Accounts; it isn&apos;t taken off what we owe yet.</span>
      </p>
      <div className="overflow-x-auto rounded-lg border border-[var(--color-border-panel)] bg-card">
        <Table>
          <TableHeader>
            <TableRow>
              <TableHead>Return</TableHead>
              {supplierId ? null : <TableHead>Supplier</TableHead>}
              <TableHead>Items and batches</TableHead>
              <TableHead>Status</TableHead>
              <TableHead className="text-right">Credit expected</TableHead>
            </TableRow>
          </TableHeader>
          <TableBody>
            {data.rows.map((r) => (
              <TableRow key={r.id}>
                <TableCell>
                  <span className="font-mono text-xs">{r.ref}</span>
                  <span className="block text-xs text-muted-foreground">
                    {day(r.date)}
                    {r.by ? `, ${r.by}` : ""}
                  </span>
                </TableCell>
                {supplierId ? null : <TableCell>{r.supplier}</TableCell>}
                <TableCell className="whitespace-normal">
                  {r.lines.map((l, i) => (
                    <span key={i} className="block text-xs">
                      {l.quantity} × {l.product}
                      <span className="text-muted-foreground">
                        {" "}
                        · <span className="font-mono">{l.batch || "no batch no."}</span> · {l.reason}
                      </span>
                    </span>
                  ))}
                </TableCell>
                <TableCell>
                  <StatusTag status={r.status} />
                </TableCell>
                <TableCell className="text-right tabular-nums">{formatKES(r.creditExpected)}</TableCell>
              </TableRow>
            ))}
            {!data.rows.length ? (
              <TableRow>
                <TableCell colSpan={5} className="py-8 text-center text-muted-foreground">
                  No returns yet.
                </TableCell>
              </TableRow>
            ) : null}
          </TableBody>
        </Table>
      </div>
    </div>
  );
}

/* ------------------------------------ The record ------------------------------------ */

type Profile = Awaited<ReturnType<typeof SuppliersApi.profile>>;

function SupplierProfile({ id, initialTab }: { id: number; initialTab: string }) {
  const router = useRouter();
  const may = usePerm();
  const [p, setP] = React.useState<Profile | null | "missing">(null);
  const [form, setForm] = React.useState<Form | null>(null);
  const [deleting, setDeleting] = React.useState(false);
  const [returning, setReturning] = React.useState(false);
  const [returnsVersion, setReturnsVersion] = React.useState(0);
  const [tabParam, setTab] = useTabParam(); // page tabs change the URL (tabs spec 7.1)
  const tab = validTab(tabParam, ["details", "prices", "received", "returns", "statement"], initialTab);

  const load = React.useCallback(async () => {
    try {
      setP(await SuppliersApi.profile(id));
    } catch (e) {
      const status = (e as { response?: { status?: number } })?.response?.status;
      if (status === 404) setP("missing");
      else toast.add({ type: "error", title: "Couldn't load the supplier", description: apiError(e, "Try again.") });
    }
  }, [id]);
  React.useEffect(() => {
    void load();
  }, [load]);

  if (p === "missing")
    return (
      <div className="medora mx-auto w-full max-w-6xl space-y-4 py-10 text-center">
        <p className="text-sm text-muted-foreground">This supplier doesn&apos;t exist, or belongs to another pharmacy.</p>
        <Button variant="outline" onClick={() => router.push("/suppliers")}>
          Back to suppliers
        </Button>
      </div>
    );
  if (!p)
    return (
      <Loading what="the supplier" shape="detail" rows={8} />
    );
  const s = p.supplier;
  const showBank = may("Accounts/Payables", "Read");
  const canPayables = may("Accounts/Payables", "Read");
  const canReturn = may("Suppliers/Supplier returns", "Create");
  const canUpdate = may("Suppliers/Supplier list", "Update");

  return (
    <div className="medora mx-auto w-full max-w-6xl space-y-6">
      <div className="space-y-3">
        <Button variant="ghost" size="sm" className="-ml-2" onClick={() => router.push("/suppliers")}>
          <ArrowLeftIcon data-icon="inline-start" />
          Suppliers
        </Button>
        <SupplierHeader
          name={s.status === "Inactive" ? `${s.name} (inactive)` : s.name}
          contact={s.contactPerson || "No contact person"}
          phone={s.phone ? formatPhone(s.phone) : "no phone"}
          town={[s.city, s.country].filter(Boolean).join(", ") || null}
          leadDays={s.leadDays}
          actions={
            <>
              {canPayables ? (
                <Button variant="outline" size="sm" onClick={() => setTab("statement")}>
                  Statement
                </Button>
              ) : null}
              {canReturn && s.status === "Active" ? (
                <Button variant="outline" size="sm" onClick={() => setReturning(true)}>
                  <PackageMinusIcon data-icon="inline-start" />
                  Return stock
                </Button>
              ) : null}
              {canUpdate ? (
                <Button size="sm" onClick={() => setForm({ id: s.id, d: toDraft(s), original: toDraft(s), otherPins: [], showBank })}>
                  <PencilIcon data-icon="inline-start" />
                  Edit
                </Button>
              ) : null}
            </>
          }
        />
      </div>
      <Tabs value={tab} onValueChange={(v) => setTab(String(v))}>
        <TabsList variant="line" className="w-full justify-start border-b border-border">
          <TabsTrigger value="details">Details</TabsTrigger>
          <TabsTrigger value="prices">Price list</TabsTrigger>
          <TabsTrigger value="received">Goods received</TabsTrigger>
          {may("Suppliers/Supplier returns", "Read") ? <TabsTrigger value="returns">Returns</TabsTrigger> : null}
          {canPayables ? <TabsTrigger value="statement">Statement</TabsTrigger> : null}
        </TabsList>
        <div className="mt-5 grid gap-6 xl:grid-cols-12">
          <div className="min-w-0 xl:col-span-8">
            <TabsContent value="details" className="space-y-6">
              <div className="rounded-lg border border-[var(--color-border-panel)] bg-card p-4">
                <DescriptionList
                  columns={2}
                  title="Company and contact"
                  items={[
                    { label: "KRA PIN", value: s.kraPin || "" },
                    { label: "Status", value: s.status },
                    { label: "Contact person", value: s.contactPerson || "" },
                    { label: "Phone", value: s.phone ? formatPhone(s.phone) : "" },
                    { label: "Email for orders", value: s.email || "", span: 2 },
                    { label: "Physical address", value: s.address || "" },
                    { label: "Postal address", value: s.postalAddress || "" },
                    { label: "Town or city", value: s.city || "" },
                    { label: "Country", value: s.country || "" },
                  ]}
                />
              </div>
              <div className="rounded-lg border border-[var(--color-border-panel)] bg-card p-4">
                <DescriptionList
                  columns={2}
                  title="Bank and tax"
                  items={[
                    ...(showBank
                      ? [
                          { label: "Bank", value: s.bankName || "" },
                          { label: "Branch", value: s.bankBranch || "" },
                          { label: "Account number", value: s.bankAccount || "" },
                        ]
                      : [{ label: "Bank details", value: s.hasBankDetails ? "On file, shown to roles with Accounts permission" : "None on file", span: 2 as const }]),
                    { label: "Withholding tax", value: s.wht ? "Deducted from payments" : "Not deducted" },
                  ]}
                />
              </div>
              <div className="rounded-lg border border-[var(--color-border-panel)] bg-card p-4">
                <DescriptionList
                  columns={2}
                  title="Terms"
                  items={[
                    { label: "Payment terms", value: s.termsDays ? `${s.termsDays} days after receipt` : "Pay on delivery" },
                    { label: "Credit limit", value: s.creditLimit != null ? formatKES(s.creditLimit) : "No limit" },
                    { label: "Agreed discount", value: s.discountPct != null ? `${s.discountPct}%` : "" },
                    { label: "Delivery lead time", value: s.leadDays != null ? `${s.leadDays} days` : "" },
                  ]}
                />
              </div>
              <p className="text-xs text-muted-foreground">Changes to bank details and terms are logged with the old and new values. Changing bank details needs the Approve permission on Supplier list.</p>
            </TabsContent>
            <TabsContent value="prices">
              <PriceList supplierId={s.id} canEdit={canUpdate} />
            </TabsContent>
            <TabsContent value="received">
              <ReceivedOrders supplierId={s.id} />
            </TabsContent>
            {may("Suppliers/Supplier returns", "Read") ? (
              <TabsContent value="returns">
                <ReturnsTable supplierId={s.id} refresh={returnsVersion} />
              </TabsContent>
            ) : null}
            {canPayables ? (
              <TabsContent value="statement">
                <Statement supplierId={s.id} name={s.name} wht={!!s.wht} creditLimit={s.creditLimit} canPay={may("Accounts/Payables", "Create")} onPaid={() => void load()} />
              </TabsContent>
            ) : null}
          </div>
          <aside aria-label="Summary" className="rounded-lg border border-[var(--color-border-panel)] bg-card p-4 xl:col-span-4 xl:self-start">
            <DescriptionList
              variant="inline"
              density="compact"
              title="At a glance"
              items={[
                ...(canPayables ? [{ label: "We owe", value: formatKES(p.owed), money: true }, { label: "Overdue", value: p.overdue ? formatKES(p.overdue) : "Nothing", money: !!p.overdue }] : []),
                { label: "Terms", value: s.termsDays ? `${s.termsDays} days` : "On delivery" },
                { label: "Products priced", value: String(p.prices) },
                { label: "Credit expected", value: p.creditExpected ? formatKES(p.creditExpected) : "None", money: !!p.creditExpected },
              ]}
            />
            {may("Suppliers/Supplier list", "Delete") || canUpdate ? (
              <div className="mt-4 flex flex-wrap gap-2 border-t border-border pt-4">
                {canUpdate ? (
                  <Button
                    size="sm"
                    variant="outline"
                    onClick={async () => {
                      try {
                        await SuppliersApi.update(s.id, { status: s.status === "Active" ? "Inactive" : "Active" });
                        toast.add({ title: s.status === "Active" ? `${s.name} deactivated` : `${s.name} reactivated` });
                        void load();
                      } catch (e) {
                        toast.add({ type: "error", title: "Not changed", description: apiError(e, "Try again.") });
                      }
                    }}
                  >
                    <PowerIcon data-icon="inline-start" />
                    {s.status === "Active" ? "Deactivate" : "Reactivate"}
                  </Button>
                ) : null}
                {may("Suppliers/Supplier list", "Delete") ? (
                  <Button size="sm" variant="ghost" className="text-status-error-text" onClick={() => setDeleting(true)}>
                    <Trash2Icon data-icon="inline-start" />
                    Delete…
                  </Button>
                ) : null}
              </div>
            ) : null}
          </aside>
        </div>
      </Tabs>
      {form ? (
        <SupplierFormDrawer
          form={form}
          onClose={() => setForm(null)}
          onSaved={() => {
            setForm(null);
            void load();
          }}
        />
      ) : null}
      {returning ? (
        <ReturnDrawer
          supplier={s}
          onClose={() => setReturning(false)}
          onDone={() => {
            setReturning(false);
            setReturnsVersion((v) => v + 1);
            setTab("returns");
            void load();
          }}
        />
      ) : null}
      <ConfirmDialog
        open={deleting}
        onOpenChange={setDeleting}
        level="A"
        title={`Delete ${s.name}?`}
        consequences={["Only a supplier with no orders, payments, stock or returns can be deleted. One with a history is deactivated instead, so its records stay.", "This can't be undone."]}
        actionLabel="Delete supplier"
        busyLabel="Deleting…"
        onConfirm={async () => {
          try {
            await SuppliersApi.delete(s.id);
          } catch (e) {
            throw new Error(apiError(e, "Not deleted. Try again."));
          }
          toast.add({ title: `${s.name} deleted` });
          router.push("/suppliers");
        }}
      />
    </div>
  );
}

/* ------------------------------------ Price list (SU-03) ------------------------------------ */

function PriceList({ supplierId, canEdit }: { supplierId: number; canEdit: boolean }) {
  const [rows, setRows] = React.useState<SupplierPriceRow[] | null>(null);
  const [editing, setEditing] = React.useState<SupplierPriceRow | null>(null);
  const [cost, setCost] = React.useState("");
  const [comparing, setComparing] = React.useState<Awaited<ReturnType<typeof SuppliersApi.productPrices>> | null>(null);
  const load = React.useCallback(() => {
    SuppliersApi.prices(supplierId)
      .then(setRows)
      .catch((e) => {
        setRows([]);
        toast.add({ type: "error", title: "Couldn't load the price list", description: apiError(e, "Try again.") });
      });
  }, [supplierId]);
  React.useEffect(load, [load]);
  if (!rows) return <Loading what="the price list" columns={4} rows={6} numeric={[1, 2]} />;
  const compare = (productId: number) =>
    SuppliersApi.productPrices(productId)
      .then(setComparing)
      .catch((e) => toast.add({ type: "error", title: "Couldn't compare", description: apiError(e, "Try again.") }));
  const priced = comparing?.prices.filter((x) => x.lastReceived) ?? [];
  return (
    <div className="space-y-3">
      <p className="text-xs text-muted-foreground">Each product&apos;s last cost from this supplier, set when stock is received from them. A cost set by hand is replaced by the next delivery.</p>
      <div className="overflow-x-auto rounded-lg border border-[var(--color-border-panel)] bg-card">
        <Table>
          <TableHeader>
            <TableRow>
              <TableHead>Product</TableHead>
              <TableHead className="text-right">Last cost</TableHead>
              <TableHead>Last received</TableHead>
              <TableHead>Source</TableHead>
              <TableHead />
            </TableRow>
          </TableHeader>
          <TableBody>
            {rows.map((r) => (
              <TableRow key={r.productId}>
                <TableCell>
                  <span className="font-medium">{r.product}</span>
                  {r.code ? <span className="block font-mono text-xs text-muted-foreground">{r.code}</span> : null}
                </TableCell>
                <TableCell className="text-right tabular-nums">{formatKES(r.cost)}</TableCell>
                <TableCell>{r.lastReceived ? day(r.lastReceived) : "Not yet received"}</TableCell>
                <TableCell>{r.source === "Manual" ? `Set by hand${r.updatedBy ? `, ${r.updatedBy}` : ""}` : "Goods received"}</TableCell>
                <TableCell className="text-right">
                  <Button size="sm" variant="ghost" onClick={() => void compare(r.productId)}>
                    Compare
                  </Button>
                  {canEdit ? (
                    <Button
                      size="sm"
                      variant="ghost"
                      onClick={() => {
                        setEditing(r);
                        setCost(String(r.cost));
                      }}
                    >
                      Edit cost
                    </Button>
                  ) : null}
                </TableCell>
              </TableRow>
            ))}
            {!rows.length ? (
              <TableRow>
                <TableCell colSpan={5} className="py-8 text-center text-muted-foreground">
                  Nothing received from this supplier yet. Prices appear here as stock arrives.
                </TableCell>
              </TableRow>
            ) : null}
          </TableBody>
        </Table>
      </div>
      <Dialog open={!!editing} onOpenChange={(o) => !o && setEditing(null)}>
        <DialogContent>
          <DialogHeader>
            <DialogTitle>Cost of {editing?.product}</DialogTitle>
            <DialogDescription>The price agreed with this supplier per unit. The change is logged, and the next delivery&apos;s cost replaces it.</DialogDescription>
          </DialogHeader>
          <Field>
            <FieldLabel htmlFor="sp-cost">Cost per unit (KES)</FieldLabel>
            <Input id="sp-cost" inputMode="decimal" className="tabular-nums" value={cost} onChange={(e) => setCost(e.target.value)} />
            {!(Number(cost.replace(/,/g, "")) > 0) ? <FieldDescription>Enter a cost more than 0.</FieldDescription> : null}
          </Field>
          <DialogFooter>
            <Button
              variant="ghost"
              className="mr-auto text-status-error-text"
              onClick={async () => {
                if (!editing) return;
                try {
                  await SuppliersApi.removePrice(supplierId, editing.productId);
                  setEditing(null);
                  load();
                } catch (e) {
                  toast.add({ type: "error", title: "Not removed", description: apiError(e, "Try again.") });
                }
              }}
            >
              Remove from list
            </Button>
            <Button variant="outline" onClick={() => setEditing(null)}>
              Cancel
            </Button>
            <Button
              disabled={!(Number(cost.replace(/,/g, "")) > 0)}
              onClick={async () => {
                if (!editing) return;
                try {
                  await SuppliersApi.setPrice(supplierId, editing.productId, Number(cost.replace(/,/g, "")));
                  toast.add({ title: "Cost saved" });
                  setEditing(null);
                  load();
                } catch (e) {
                  toast.add({ type: "error", title: "Not saved", description: apiError(e, "Try again.") });
                }
              }}
            >
              Save cost
            </Button>
          </DialogFooter>
        </DialogContent>
      </Dialog>
      <Dialog open={!!comparing} onOpenChange={(o) => !o && setComparing(null)}>
        <DialogContent className="sm:max-w-2xl">
          <DialogHeader>
            <DialogTitle>Who sells {comparing?.product.name}</DialogTitle>
            <DialogDescription>Each supplier&apos;s last cost from goods received. Costs older than 90 days are marked stale.</DialogDescription>
          </DialogHeader>
          {priced.length ? (
            <SupplierPrices product={comparing!.product.name} prices={priced.map((x) => ({ supplier: x.supplier, cost: x.cost, lastReceived: x.lastReceived!.slice(0, 10), leadDays: x.leadDays }))} today={localToday()} formatDate={(iso) => formatDate(iso)} />
          ) : (
            <p className="text-sm text-muted-foreground">No supplier has delivered this product yet.</p>
          )}
        </DialogContent>
      </Dialog>
    </div>
  );
}

/* ------------------------------------ Received orders (SU-05, until GRNs) ------------------------------------ */

function ReceivedOrders({ supplierId }: { supplierId: number }) {
  const [rows, setRows] = React.useState<Awaited<ReturnType<typeof SuppliersApi.received>> | null>(null);
  const [grns, setGrns] = React.useState<GrnRow[] | null>(null);
  React.useEffect(() => {
    SuppliersApi.received(supplierId)
      .then(setRows)
      .catch((e) => {
        setRows([]);
        toast.add({ type: "error", title: "Couldn't load received orders", description: apiError(e, "Try again.") });
      });
    PurchasingApi.supplierGrns(supplierId).then(setGrns).catch(() => setGrns([]));
  }, [supplierId]);
  if (!rows || !grns) return <Loading what="goods received" columns={6} rows={5} toolbar={1} numeric={[3, 4, 5]} />;
  // SU-05: goods received notes first; orders received before GRNs existed (or through an older app) below
  const withGrn = new Set(grns.map((g) => g.poId));
  const older = rows.filter((r) => !withGrn.has(r.poId));
  return (
    <div className="space-y-3">
      <h3 className="text-sm font-semibold">Goods received notes</h3>
      {grns.length ? (
        <GrnList grns={grns.map((g) => ({ 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 })) }))} supplier={grns[0]?.supplier} />
      ) : (
        <p className="text-sm text-muted-foreground">No goods received notes yet. They are posted from Inventory → Purchase orders.</p>
      )}
      <h3 className="pt-2 text-sm font-semibold">Orders received before goods received notes</h3>
      <p className="text-xs text-muted-foreground">Received with quantities only (before 26 Oct 2026, or from an older app), so they have no delivery note or CU number.</p>
      <div className="overflow-x-auto rounded-lg border border-[var(--color-border-panel)] bg-card">
        <Table>
          <TableHeader>
            <TableRow>
              <TableHead>Order</TableHead>
              <TableHead>Received</TableHead>
              <TableHead>Due</TableHead>
              <TableHead>Lines</TableHead>
              <TableHead className="text-right">Value</TableHead>
            </TableRow>
          </TableHeader>
          <TableBody>
            {older.map((r) => (
              <TableRow key={r.poId}>
                <TableCell>
                  <span className="font-mono text-xs">{r.ref}</span>
                  {r.status !== "Received" ? <span className="mt-0.5 block"><StatusTag status={r.status} /></span> : null}
                </TableCell>
                <TableCell>{r.receivedDate ? day(r.receivedDate) : `Ordered ${day(r.orderDate)}`}</TableCell>
                <TableCell>{day(r.dueDate)}</TableCell>
                <TableCell className="whitespace-normal">
                  {r.lines.map((l, i) => (
                    <span key={i} className="block text-xs">
                      {l.received}
                      {l.received !== l.ordered ? ` of ${l.ordered}` : ""} × {l.product} <span className="text-muted-foreground tabular-nums">at {formatKES(l.unitCost)}</span>
                    </span>
                  ))}
                </TableCell>
                <TableCell className="text-right tabular-nums">{formatKES(r.value)}</TableCell>
              </TableRow>
            ))}
            {!older.length ? (
              <TableRow>
                <TableCell colSpan={5} className="py-8 text-center text-muted-foreground">
                  Nothing received from this supplier yet.
                </TableCell>
              </TableRow>
            ) : null}
          </TableBody>
        </Table>
      </div>
    </div>
  );
}

/* ------------------------------------ Return to supplier (SU-06) ------------------------------------ */

function ReturnDrawer({ supplier, onClose, onDone }: { supplier: SupplierRecord; onClose: () => void; onDone: () => void }) {
  const [batches, setBatches] = React.useState<Awaited<ReturnType<typeof SuppliersApi.returnable>> | null>(null);
  const [qty, setQty] = React.useState<Record<string, number>>({});
  const [reason, setReason] = React.useState<Record<string, string>>({});
  const [note, setNote] = React.useState("");
  const [tried, setTried] = React.useState(false);
  const [saving, setSaving] = React.useState(false);
  React.useEffect(() => {
    SuppliersApi.returnable(supplier.id)
      .then(setBatches)
      .catch((e) => {
        setBatches([]);
        toast.add({ type: "error", title: "Couldn't load batches", description: apiError(e, "Try again.") });
      });
  }, [supplier.id]);
  const chosen = Object.entries(qty).filter(([, q]) => q > 0);
  const missingReason = chosen.some(([id]) => !reason[id]);
  const save = async () => {
    setTried(true);
    if (!chosen.length || missingReason) return;
    setSaving(true);
    try {
      const r = await SuppliersApi.createReturn(supplier.id, { lines: chosen.map(([id, q]) => ({ batchId: Number(id), quantity: q, reason: reason[id] })), note: note.trim() || undefined });
      toast.add({ title: `${r.ref}: ${formatKES(r.creditExpected)} credit expected from ${supplier.name}` });
      onDone();
    } catch (e) {
      toast.add({ type: "error", title: "Not returned", description: apiError(e, "Try again.") });
    } finally {
      setSaving(false);
    }
  };
  return (
    <RecordDrawer
      open
      onOpenChange={(o) => !o && onClose()}
      variant="edit"
      width="lg"
      saving={saving}
      dirty={chosen.length > 0}
      title={`Return stock to ${supplier.name}`}
      footer={
        <>
          <Button variant="outline" disabled={saving} onClick={onClose}>
            Cancel
          </Button>
          <Button disabled={saving} onClick={() => void save()}>
            {saving ? <Spinner /> : null}
            {chosen.length ? `Return ${count(chosen.reduce((t, [, q]) => t + q, 0), "unit")}` : "Return units"}
          </Button>
        </>
      }
    >
      <div className="space-y-4">
        <p className="text-sm text-muted-foreground">Batches bought from {supplier.name}, and batches with no supplier recorded. The units leave stock now; the credit expected stays on record until their credit note arrives.</p>
        {batches === null ? (
          <Loading what="batches" columns={5} rows={3} numeric={[3, 4]} />
        ) : batches.length ? (
          <SupplierReturnLines
            batches={batches.map((b) => ({ id: String(b.id), name: b.name, batch: b.batch || "no batch no.", expiry: b.expiry ? formatDate(b.expiry) : "not recorded", onHand: b.onHand, unitCost: b.unitCost, grn: b.fromSupplier ? supplier.name : "no supplier recorded" }))}
            qty={qty}
            reasons={RETURN_REASONS}
            reason={reason}
            onQty={(bid, n) => setQty((q) => ({ ...q, [bid]: n }))}
            onReason={(bid, r) => setReason((x) => ({ ...x, [bid]: r }))}
          />
        ) : (
          <p className="text-sm">Nothing in stock that can go back to this supplier.</p>
        )}
        {tried && !chosen.length ? <p className="text-sm text-status-error-text">Choose how many units go back.</p> : null}
        {tried && missingReason ? <p className="text-sm text-status-error-text">Give a reason for each line.</p> : null}
        <Field>
          <FieldLabel htmlFor="sr-note">Note (optional)</FieldLabel>
          <Input id="sr-note" value={note} maxLength={255} onChange={(e) => setNote(e.target.value)} placeholder="For example: collected by their rider, ref on their slip" />
        </Field>
      </div>
    </RecordDrawer>
  );
}

/* ------------------------------------ Statement (Accounts 6.3, as before) ------------------------------------ */

function Statement({ supplierId, name, wht, creditLimit, canPay, onPaid }: { supplierId: number; name: string; wht: boolean; creditLimit: number | null; canPay: boolean; onPaid: () => void }) {
  type St = {
    summary: { balance: number; creditOnAccount: number };
    aging: { current: number; days31: number; days61: number; days90plus: number };
    transactions: { type: string; date: string; reference: string; charge: number; credit: number }[];
  };
  const [s, setS] = React.useState<St | null>(null);
  const [paying, setPaying] = React.useState<PaymentFormStatus | null>(null);
  const [payError, setPayError] = React.useState<string>();
  // Plan I (Q2): withholding tax kept back from this payment, with its certificate
  const [whtPart, setWhtPart] = React.useState({ amount: "", certificate: "" });
  const load = React.useCallback(() => {
    SuppliersApi.statement(supplierId)
      .then(setS)
      .catch((e) => toast.add({ type: "error", title: "Couldn't load the statement", description: apiError(e, "Try again.") }));
  }, [supplierId]);
  React.useEffect(load, [load]);
  if (!s) return <Loading what="the statement" shape="stats" rows={4} />;
  const overdue = s.aging.days31 + s.aging.days61 + s.aging.days90plus;
  return (
    <div className="space-y-5">
      <div className="flex flex-wrap items-center justify-between gap-2">
        <p className="text-xs text-muted-foreground">Bills are due after the supplier&apos;s terms and aged from that date: goods received, or the supplier&apos;s own bills once Accounts → Payables enters them. Credit notes and withholding tax reduce what is owed.</p>
        {canPay && s.summary.balance > 0 ? (
          <Button size="sm" onClick={() => setPaying("idle")}>
            Record payment
          </Button>
        ) : null}
      </div>
      <BalanceSummary kind="payable" balance={s.summary.balance} creditLimit={creditLimit ?? undefined} overdue={overdue || undefined} />
      <AgingBuckets data={{ current: s.aging.current, d31to60: s.aging.days31, d61to90: s.aging.days61, over90: s.aging.days90plus }} />
      {s.summary.creditOnAccount ? <p className="text-sm">Paid ahead: {formatKES(s.summary.creditOnAccount)} on account.</p> : null}
      {/* The statement covers the whole history, so there is no balance brought forward to show */}
      <StatementTable chargeLabel="Bills" paymentLabel="Payments" rows={s.transactions.map((t) => ({ date: String(t.date).slice(0, 10), ref: t.reference, description: t.type === "Bill" ? (t.reference.startsWith("BIL-") ? "Supplier bill" : "Goods received") : t.type === "Credit note" ? "Credit note" : "Payment made", charge: t.charge || undefined, payment: t.credit || undefined }))} />
      <Dialog open={paying !== null} onOpenChange={(o) => !o && paying !== "submitting" && setPaying(null)}>
        <DialogContent>
          <DialogHeader>
            <DialogTitle>Record a payment to {name}</DialogTitle>
            <DialogDescription>Applied to the oldest unpaid bills first; bills held for a query wait.</DialogDescription>
          </DialogHeader>
          {wht ? (
            <div className="grid gap-3">
              <Field>
                <FieldLabel htmlFor="sp-wht">Withholding tax kept back</FieldLabel>
                <Input id="sp-wht" inputMode="decimal" className="tabular-nums" placeholder="0.00" value={whtPart.amount} onChange={(e) => setWhtPart({ ...whtPart, amount: e.target.value })} />
              </Field>
              <Field>
                <FieldLabel htmlFor="sp-whtc">WHT certificate number</FieldLabel>
                <Input id="sp-whtc" className="font-mono" value={whtPart.certificate} onChange={(e) => setWhtPart({ ...whtPart, certificate: e.target.value })} />
              </Field>
              <p className="text-xs text-muted-foreground sm:col-span-2">Enter what was paid below; the tax kept back settles the bills too and is owed to KRA.</p>
            </div>
          ) : null}
          <PaymentForm
            party={name}
            outstanding={s.summary.balance}
            today={localToday()}
            status={paying ?? "idle"}
            error={payError}
            onSubmit={async (pay) => {
              setPaying("submitting");
              setPayError(undefined);
              try {
                const whtAmount = wht ? Number(whtPart.amount.replace(/,/g, "")) || 0 : 0;
                await SuppliersApi.recordPayment(supplierId, { amount: pay.amount, method: pay.method, reference: pay.reference, paymentDate: pay.date, ...(whtAmount ? { whtAmount, whtCertificate: whtPart.certificate.trim() } : {}) });
                toast.add({ title: `${formatKES(pay.amount)} paid to ${name}`, description: whtAmount ? `${formatKES(whtAmount)} withholding tax kept back.` : undefined });
                setWhtPart({ amount: "", certificate: "" });
                setPaying(null);
                load();
                onPaid();
              } catch (e) {
                setPaying("error");
                setPayError(apiError(e, "Not recorded. Try again."));
              }
            }}
          />
        </DialogContent>
      </Dialog>
    </div>
  );
}
