"use client";

import * as React from "react";
import { ChevronRightIcon, SearchIcon } from "lucide-react";

import { kes } from "@/components/medora/payroll";
import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input";
import { NativeSelect, NativeSelectOption } from "@/components/ui/native-select";
import { addDays, count, daysBetween, formatDate } from "@/lib/format";
import { cn } from "@/lib/utils";

/*
 * Purchasing chain, second half: receiving against an order (IN-16), the goods received notes it creates
 * (IN-17, SU-05), and the supplier bills and credit notes posted against them (AC-10). A supplier bill is
 * for goods received, never goods ordered, so it is raised from a GRN and checked against its value.
 */

const r2 = (n: number) => Math.round(n * 100) / 100;
const th = "px-2 py-2 font-medium";
const thR = "px-2 py-2 text-right font-medium";

/* ------------------------------------ Receiving (IN-16) ------------------------------------ */

export type ReceiveLine = { id: number; product: string; ordered: number; before: number; unitCost: number; vat: 0 | 16; delivered: string; received: string; batch: string; expiry: string; reason: string };
export type ReceiveHeader = { po: string; supplier: string; dnNo: string; cuNo: string; location: string; date: string };

export const SHORT_REASONS = ["Damaged", "Wrong item", "Short expiry", "Not on the delivery note"] as const;

export type ReceiveCheck = { blocking: string[]; warnings: string[] };

/**
 * What stops a GRN being posted, and what only needs a second look. Received can't exceed delivered, and
 * delivered can't exceed what is still outstanding; anything delivered but refused needs a reason; every
 * received line needs a batch and a future expiry. Short shelf life is a warning, not a block.
 */
export function receiveCheck(h: ReceiveHeader, lines: ReceiveLine[], today: string): ReceiveCheck {
  const blocking: string[] = [];
  const warnings: string[] = [];
  if (!h.dnNo.trim()) blocking.push("Enter the delivery note number from the supplier's paperwork.");
  if (!/^\d{10,20}$/.test(h.cuNo.replace(/\s/g, ""))) blocking.push("Enter the KRA CU invoice number from the supplier's eTIMS invoice: 10 to 20 digits. Without it the VAT can't be claimed.");
  if (!h.location) blocking.push("Choose the location the goods go into.");
  if (!lines.some((l) => Number(l.received) > 0)) blocking.push("Nothing is being received. Enter the quantities accepted.");
  for (const l of lines) {
    const left = l.ordered - l.before;
    const dl = l.delivered === "" ? 0 : Number(l.delivered);
    const rc = l.received === "" ? 0 : Number(l.received);
    if (!/^\d*$/.test(l.delivered) || !/^\d*$/.test(l.received)) {
      blocking.push(`${l.product}: quantities are whole numbers.`);
      continue;
    }
    if (dl > left) blocking.push(`${l.product}: ${dl} delivered but only ${left} still on order. Receive ${left} and return the rest, or amend the order first.`);
    if (rc > dl) blocking.push(`${l.product}: can't accept ${rc} when ${dl} were delivered.`);
    if (rc < dl && !l.reason) blocking.push(`${l.product}: ${dl - rc} delivered but not accepted. Say why.`);
    if (rc > 0) {
      if (!l.batch.trim()) blocking.push(`${l.product}: enter the batch number printed on the pack.`);
      if (!l.expiry) blocking.push(`${l.product}: enter the expiry date.`);
      else if (l.expiry <= today) blocking.push(`${l.product}: expiry ${formatDate(l.expiry)} has passed. Refuse these and record the reason.`);
      else if (daysBetween(today, l.expiry) < 180) warnings.push(`${l.product}: expires ${formatDate(l.expiry)}, under 6 months away. Accept only if you'll sell it in time.`);
    }
    if (dl > 0 && dl < left && rc === dl) warnings.push(`${l.product}: ${left - dl} still to come; the order stays open.`);
  }
  return { blocking, warnings };
}

export const receivedValue = (lines: ReceiveLine[]) => {
  let sub = 0;
  let vat = 0;
  for (const l of lines) {
    const v = (Number(l.received) || 0) * l.unitCost;
    sub += v;
    vat += (v * l.vat) / 100;
  }
  return { subtotal: r2(sub), vat: r2(vat), total: r2(sub + vat) };
};

/** IN-16: book a delivery against its order, line by line: ordered, delivered, accepted, batch and expiry. */
export function ReceiveGoodsForm({ header, onHeader, lines, onLines, locations, check, tried }: { header: ReceiveHeader; onHeader: (h: ReceiveHeader) => void; lines: ReceiveLine[]; onLines: (l: ReceiveLine[]) => void; locations: string[]; check: ReceiveCheck; tried: boolean }) {
  const setH = <K extends keyof ReceiveHeader>(k: K, v: ReceiveHeader[K]) => onHeader({ ...header, [k]: v });
  const setL = (id: number, p: Partial<ReceiveLine>) => onLines(lines.map((l) => (l.id === id ? { ...l, ...p } : l)));
  const v = receivedValue(lines);
  return (
    <div className="space-y-4">
      <p className="text-sm text-muted-foreground">
        Against <span className="font-mono text-foreground">{header.po}</span> from {header.supplier}. The GRN number is assigned when posted.
      </p>
      <div className="grid gap-3 sm:grid-cols-2 lg:grid-cols-4">
        <div className="space-y-1">
          <label htmlFor="gr-dn" className="text-sm font-medium">
            Delivery note number
          </label>
          <Input id="gr-dn" value={header.dnNo} onChange={(e) => setH("dnNo", e.target.value)} aria-invalid={tried && !header.dnNo.trim()} />
        </div>
        <div className="space-y-1">
          <label htmlFor="gr-cu" className="text-sm font-medium">
            KRA CU invoice number
          </label>
          <Input id="gr-cu" className="font-mono" inputMode="numeric" value={header.cuNo} onChange={(e) => setH("cuNo", e.target.value.replace(/[^\d\s]/g, ""))} aria-invalid={tried && !/^\d{10,20}$/.test(header.cuNo.replace(/\s/g, ""))} />
        </div>
        <div className="space-y-1">
          <label htmlFor="gr-loc" className="text-sm font-medium">
            Receive into
          </label>
          <NativeSelect id="gr-loc" value={header.location} onChange={(e) => setH("location", e.target.value)} aria-invalid={tried && !header.location}>
            <NativeSelectOption value="">Choose…</NativeSelectOption>
            {locations.map((l) => (
              <NativeSelectOption key={l}>{l}</NativeSelectOption>
            ))}
          </NativeSelect>
        </div>
        <div className="space-y-1">
          <label htmlFor="gr-date" className="text-sm font-medium">
            Date received
          </label>
          <Input id="gr-date" type="date" value={header.date} onChange={(e) => setH("date", e.target.value)} />
        </div>
      </div>
      <div className="overflow-x-auto rounded-md border border-[var(--color-border-panel)] bg-card">
        <table className="w-full min-w-[920px] text-sm">
          <thead className="bg-[var(--color-bg-surface-subtle)] text-left text-xs text-muted-foreground">
            <tr>
              <th className={th}>Product</th>
              <th className={thR}>Ordered</th>
              <th className={thR}>Already in</th>
              <th className={cn(thR, "w-20")}>Delivered</th>
              <th className={cn(thR, "w-20")}>Accepted</th>
              <th className={cn(th, "w-36")}>Not accepted because</th>
              <th className={cn(th, "w-28")}>Batch</th>
              <th className={cn(th, "w-36")}>Expiry</th>
              <th className={thR}>Value</th>
            </tr>
          </thead>
          <tbody className="divide-y divide-[var(--color-border-subtle)]">
            {lines.map((l) => {
              const short = Number(l.received || 0) < Number(l.delivered || 0);
              const rc = Number(l.received) || 0;
              return (
                <tr key={l.id} className="align-top">
                  <td className="px-2 py-2">
                    {l.product}
                    <span className="block text-xs text-muted-foreground">at {kes(l.unitCost)}</span>
                  </td>
                  <td className="px-2 py-2 text-right tabular-nums">{l.ordered}</td>
                  <td className="px-2 py-2 text-right tabular-nums">{l.before || "–"}</td>
                  <td className="px-2 py-1.5">
                    <Input aria-label={`${l.product} delivered`} className="text-right tabular-nums" inputMode="numeric" value={l.delivered} onChange={(e) => setL(l.id, { delivered: e.target.value })} />
                  </td>
                  <td className="px-2 py-1.5">
                    <Input aria-label={`${l.product} accepted`} className="text-right tabular-nums" inputMode="numeric" value={l.received} onChange={(e) => setL(l.id, { received: e.target.value })} />
                  </td>
                  <td className="px-2 py-1.5">
                    {short ? (
                      <NativeSelect aria-label={`${l.product} reason not accepted`} value={l.reason} onChange={(e) => setL(l.id, { reason: e.target.value })} aria-invalid={tried && !l.reason}>
                        <NativeSelectOption value="">Choose…</NativeSelectOption>
                        {SHORT_REASONS.map((r) => (
                          <NativeSelectOption key={r}>{r}</NativeSelectOption>
                        ))}
                      </NativeSelect>
                    ) : (
                      <span className="block pt-2 text-xs text-muted-foreground">–</span>
                    )}
                  </td>
                  <td className="px-2 py-1.5">
                    <Input aria-label={`${l.product} batch`} className="font-mono uppercase" value={l.batch} disabled={!rc} onChange={(e) => setL(l.id, { batch: e.target.value.toUpperCase() })} aria-invalid={tried && rc > 0 && !l.batch.trim()} />
                  </td>
                  <td className="px-2 py-1.5">
                    <Input aria-label={`${l.product} expiry`} type="date" value={l.expiry} disabled={!rc} onChange={(e) => setL(l.id, { expiry: e.target.value })} aria-invalid={tried && rc > 0 && !l.expiry} />
                  </td>
                  <td className="px-2 py-2 text-right tabular-nums">{kes(rc * l.unitCost)}</td>
                </tr>
              );
            })}
          </tbody>
          <tfoot className="border-t-2 border-[var(--color-border-panel)] text-sm">
            <tr>
              <td colSpan={8} className="px-2 py-2 text-right text-muted-foreground">
                Accepted before VAT {kes(v.subtotal)} · VAT {kes(v.vat)} · total
              </td>
              <td className="px-2 py-2 text-right font-semibold tabular-nums">{kes(v.total)}</td>
            </tr>
          </tfoot>
        </table>
      </div>
      {tried && check.blocking.length ? (
        <div role="alert" className="rounded-md border border-status-error-border bg-status-error-bg p-3 text-sm text-status-error-text">
          <p className="font-medium">Fix before posting:</p>
          <ul className="mt-1 list-disc pl-5">
            {check.blocking.map((b) => (
              <li key={b}>{b}</li>
            ))}
          </ul>
        </div>
      ) : null}
      {check.warnings.length ? (
        <ul className="list-disc space-y-0.5 pl-5 text-sm text-status-warning-text">
          {check.warnings.map((w) => (
            <li key={w}>{w}</li>
          ))}
        </ul>
      ) : null}
    </div>
  );
}

/* ------------------------------------ GRN list (IN-17, SU-05) ------------------------------------ */

export type Grn = { no: string; date: string; supplier: string; po: string; dnNo: string; cuNo: string; location: string; lines: { product: string; batch: string; expiry: string; qty: number; unitCost: number; vat: 0 | 16 }[] };

export const grnTotals = (g: Grn) => {
  const sub = g.lines.reduce((s, l) => s + l.qty * l.unitCost, 0);
  const vat = g.lines.reduce((s, l) => s + (l.qty * l.unitCost * l.vat) / 100, 0);
  return { subtotal: r2(sub), vat: r2(vat), total: r2(sub + vat) };
};

/**
 * IN-17: goods received notes, as a summary (one row each) or itemised (one row per batch received).
 * Searchable by GRN, order, delivery note and CU numbers. Pass `supplier` to show one supplier's GRNs
 * on their record (SU-05): the supplier column and filter then drop away.
 */
export function GrnList({ grns, supplier, onOpen }: { grns: Grn[]; supplier?: string; onOpen?: (no: string) => void }) {
  const [view, setView] = React.useState<"summary" | "itemised">("summary");
  const [q, setQ] = React.useState("");
  const [sup, setSup] = React.useState("");
  const [open, setOpen] = React.useState<string | null>(null);
  const [from, setFrom] = React.useState("");
  const [to, setTo] = React.useState("");
  const term = q.trim().toLowerCase();
  const shown = grns.filter((g) => (supplier ? g.supplier === supplier : !sup || g.supplier === sup) && (!from || g.date >= from) && (!to || g.date <= to) && (!term || `${g.no} ${g.po} ${g.dnNo} ${g.cuNo}`.toLowerCase().includes(term)));
  const totals = shown.reduce((t, g) => {
    const x = grnTotals(g);
    return { subtotal: t.subtotal + x.subtotal, vat: t.vat + x.vat, total: t.total + x.total };
  }, { subtotal: 0, vat: 0, total: 0 });
  const suppliers = [...new Set(grns.map((g) => g.supplier))].sort();
  return (
    // One container (data table spec, 02-data-display §1): the toolbar sits inside it, above the table
    <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">
        <div className="relative min-w-52 flex-1">
          <SearchIcon className="pointer-events-none absolute top-1/2 left-2.5 size-4 -translate-y-1/2 text-muted-foreground" aria-hidden />
          <Input aria-label="Search GRNs" placeholder="GRN, order, delivery note or CU number" className="pl-8" value={q} onChange={(e) => setQ(e.target.value)} />
        </div>
        <Input aria-label="Received from" type="date" className="w-40" max={to || undefined} value={from} onChange={(e) => setFrom(e.target.value)} />
        <Input aria-label="Received to" type="date" className="w-40" min={from || undefined} value={to} onChange={(e) => setTo(e.target.value)} />
        {!supplier ? (
          <NativeSelect aria-label="Supplier" value={sup} onChange={(e) => setSup(e.target.value)}>
            <NativeSelectOption value="">All suppliers</NativeSelectOption>
            {suppliers.map((s) => (
              <NativeSelectOption key={s}>{s}</NativeSelectOption>
            ))}
          </NativeSelect>
        ) : null}
        <div role="radiogroup" aria-label="View" className="inline-flex overflow-hidden rounded-md border border-[var(--color-border-panel)] text-sm bg-card">
          {(["summary", "itemised"] as const).map((k) => (
            <button key={k} type="button" role="radio" aria-checked={view === k} onClick={() => setView(k)} className={cn("cursor-pointer px-3 py-1.5 capitalize", view === k ? "bg-[var(--color-bg-selected)] font-medium" : "text-muted-foreground hover:bg-muted")}>
              {k}
            </button>
          ))}
        </div>
      </div>
      <div className="overflow-x-auto">
        <table className="w-full min-w-[720px] text-sm">
          <thead className="bg-[var(--color-bg-surface-subtle)] text-left text-xs text-muted-foreground">
            {view === "summary" ? (
              <tr>
                <th className="w-8" />
                <th className={th}>GRN</th>
                {!supplier ? <th className={th}>Supplier</th> : null}
                <th className={th}>Order and delivery note</th>
                <th className={thR}>Before VAT</th>
                <th className={thR}>VAT</th>
                <th className={thR}>Invoice total</th>
              </tr>
            ) : (
              <tr>
                <th className={th}>GRN</th>
                <th className={th}>Product</th>
                <th className={th}>Batch and expiry</th>
                <th className={thR}>Qty</th>
                <th className={thR}>Unit cost</th>
                <th className={thR}>Value</th>
              </tr>
            )}
          </thead>
          {view === "summary" ? (
            shown.map((g) => {
              const t = grnTotals(g);
              const isOpen = open === g.no;
              return (
                <tbody key={g.no} className="border-t border-[var(--color-border-subtle)]">
                  <tr className={cn(isOpen && "bg-muted/30")}>
                    <td className="px-1 py-2">
                      <Button size="icon-sm" variant="ghost" aria-expanded={isOpen} aria-label={`${isOpen ? "Hide" : "Show"} lines on ${g.no}`} onClick={() => setOpen(isOpen ? null : g.no)}>
                        <ChevronRightIcon className={cn("transition-transform", isOpen && "rotate-90")} />
                      </Button>
                    </td>
                    <td className="px-2 py-2 whitespace-nowrap">
                      <button type="button" className="cursor-pointer font-mono text-xs text-primary underline-offset-4 hover:underline" onClick={() => onOpen?.(g.no)}>
                        {g.no}
                      </button>
                      <span className="block text-xs text-muted-foreground">
                        {formatDate(g.date)} · {g.location}
                      </span>
                    </td>
                    {!supplier ? <td className="px-2 py-2">{g.supplier}</td> : null}
                    <td className="px-2 py-2 text-xs">
                      <span className="font-mono">{g.po}</span> · DN {g.dnNo}
                      <span className="block font-mono text-muted-foreground">CU {g.cuNo}</span>
                    </td>
                    <td className="px-2 py-2 text-right tabular-nums">{kes(t.subtotal)}</td>
                    <td className="px-2 py-2 text-right tabular-nums">{kes(t.vat)}</td>
                    <td className="px-2 py-2 text-right font-medium tabular-nums">{kes(t.total)}</td>
                  </tr>
                  {isOpen ? (
                    <tr>
                      <td />
                      <td colSpan={supplier ? 5 : 6} className="px-2 pb-3">
                        <ul className="space-y-0.5 text-xs">
                          {g.lines.map((l) => (
                            <li key={l.batch} className="flex justify-between gap-3">
                              <span>
                                {l.qty} × {l.product} <span className="font-mono text-muted-foreground">{l.batch}</span>, exp {formatDate(l.expiry)}
                              </span>
                              <span className="tabular-nums">{kes(l.qty * l.unitCost)}</span>
                            </li>
                          ))}
                        </ul>
                      </td>
                    </tr>
                  ) : null}
                </tbody>
              );
            })
          ) : (
            <tbody className="divide-y divide-[var(--color-border-subtle)]">
              {shown.flatMap((g) =>
                g.lines.map((l) => (
                  <tr key={`${g.no}-${l.batch}`}>
                    <td className="px-2 py-2 font-mono text-xs">{g.no}</td>
                    <td className="px-2 py-2">{l.product}</td>
                    <td className="px-2 py-2 text-xs">
                      <span className="font-mono">{l.batch}</span> · {formatDate(l.expiry)}
                    </td>
                    <td className="px-2 py-2 text-right tabular-nums">{l.qty}</td>
                    <td className="px-2 py-2 text-right tabular-nums">{kes(l.unitCost)}</td>
                    <td className="px-2 py-2 text-right tabular-nums">{kes(l.qty * l.unitCost)}</td>
                  </tr>
                )),
              )}
            </tbody>
          )}
          <tfoot className="border-t-2 border-[var(--color-border-panel)] bg-muted/30 font-semibold">
            <tr>
              <td colSpan={view === "summary" ? (supplier ? 3 : 4) : 5} className="px-2 py-2">
                {count(shown.length, "GRN")}{view === "itemised" ? " (value before VAT)" : ""}
              </td>
              {view === "summary" ? (
                <>
                  <td className="px-2 py-2 text-right tabular-nums">{kes(totals.subtotal)}</td>
                  <td className="px-2 py-2 text-right tabular-nums">{kes(totals.vat)}</td>
                  <td className="px-2 py-2 text-right tabular-nums">{kes(totals.total)}</td>
                </>
              ) : (
                <td className="px-2 py-2 text-right tabular-nums">{kes(totals.subtotal)}</td>
              )}
            </tr>
          </tfoot>
        </table>
      </div>
    </div>
  );
}

/* ------------------------------------ Supplier bills (AC-10) ------------------------------------ */

export type BillDraft = { invoiceNo: string; cuNo: string; invoiceDate: string; amount: string; note: string };

/**
 * A bill is entered against a GRN. Its amount should equal the goods received (VAT included); a difference
 * above a shilling must be explained, and the bill is held from payment until someone clears it. The due
 * date comes from the supplier's payment terms, not from typing.
 */
export function billProblems(b: BillDraft, grnTotal: number, existingInvoiceNos: string[]) {
  const p: string[] = [];
  if (!b.invoiceNo.trim()) p.push("Enter the supplier's invoice number.");
  else if (existingInvoiceNos.includes(b.invoiceNo.trim().toUpperCase())) p.push(`Invoice ${b.invoiceNo.trim()} from this supplier is already entered. Don't pay it twice.`);
  if (!/^\d{10,20}$/.test(b.cuNo.replace(/\s/g, ""))) p.push("Enter the KRA CU invoice number (digits only).");
  if (!b.invoiceDate) p.push("Enter the invoice date.");
  const amt = Number(b.amount.replace(/,/g, ""));
  if (!/^\d+(\.\d{1,2})?$/.test(b.amount.replace(/,/g, ""))) p.push("Enter the invoice total.");
  else if (Math.abs(amt - grnTotal) > 1 && !b.note.trim()) p.push(`The invoice is ${amt > grnTotal ? "more" : "less"} than the goods received by ${kes(Math.abs(amt - grnTotal))}. Explain the difference, or correct the amount.`);
  return p;
}

export function BillForm({ grn, termsDays, b, onChange, problems, tried }: { grn: Grn; termsDays: number; b: BillDraft; onChange: (b: BillDraft) => void; problems: string[]; tried: boolean }) {
  const t = grnTotals(grn);
  const amt = Number(b.amount.replace(/,/g, ""));
  const diff = Number.isFinite(amt) && b.amount ? r2(amt - t.total) : 0;
  const set = <K extends keyof BillDraft>(k: K, v: BillDraft[K]) => onChange({ ...b, [k]: v });
  return (
    <div className="space-y-4">
      <p className="text-sm">
        For <span className="font-mono">{grn.no}</span> ({grn.supplier}, received {formatDate(grn.date)}): goods worth <strong className="tabular-nums">{kes(t.total)}</strong> including VAT.
      </p>
      <div className="grid gap-3 sm:grid-cols-2">
        <div className="space-y-1">
          <label htmlFor="bl-inv" className="text-sm font-medium">
            Supplier&apos;s invoice number
          </label>
          <Input id="bl-inv" className="uppercase" value={b.invoiceNo} onChange={(e) => set("invoiceNo", e.target.value)} />
        </div>
        <div className="space-y-1">
          <label htmlFor="bl-cu" className="text-sm font-medium">
            KRA CU invoice number
          </label>
          <Input id="bl-cu" className="font-mono" value={b.cuNo} onChange={(e) => set("cuNo", e.target.value.replace(/[^\d\s]/g, ""))} />
          <p className="text-xs text-muted-foreground">{b.cuNo && b.cuNo.replace(/\s/g, "") === grn.cuNo ? "Matches the GRN." : grn.cuNo ? `The GRN recorded ${grn.cuNo}.` : null}</p>
        </div>
        <div className="space-y-1">
          <label htmlFor="bl-date" className="text-sm font-medium">
            Invoice date
          </label>
          <Input id="bl-date" type="date" value={b.invoiceDate} onChange={(e) => set("invoiceDate", e.target.value)} />
          <p className="text-xs text-muted-foreground">{b.invoiceDate ? `Due ${formatDate(addDays(b.invoiceDate, termsDays))} on ${termsDays}-day terms.` : null}</p>
        </div>
        <div className="space-y-1">
          <label htmlFor="bl-amt" className="text-sm font-medium">
            Invoice total, VAT included
          </label>
          <Input id="bl-amt" className="tabular-nums" inputMode="decimal" value={b.amount} onChange={(e) => set("amount", e.target.value)} />
          <p className={cn("text-xs", Math.abs(diff) > 1 ? "text-status-warning-text" : "text-muted-foreground")}>{b.amount ? (Math.abs(diff) <= 1 ? "Agrees with the goods received." : `${diff > 0 ? "Over" : "Under"} the goods received by ${kes(Math.abs(diff))}.`) : null}</p>
        </div>
        {Math.abs(diff) > 1 ? (
          <div className="space-y-1 sm:col-span-2">
            <label htmlFor="bl-note" className="text-sm font-medium">
              Why does it differ?
            </label>
            <Input id="bl-note" value={b.note} onChange={(e) => set("note", e.target.value)} placeholder="e.g. Supplier billed the 6 damaged tubes; credit note requested" />
            <p className="text-xs text-muted-foreground">The bill is held from payment until an Admin clears the difference.</p>
          </div>
        ) : null}
      </div>
      {tried && problems.length ? (
        <ul role="alert" className="list-disc pl-5 text-sm text-status-error-text">
          {problems.map((x) => (
            <li key={x}>{x}</li>
          ))}
        </ul>
      ) : null}
    </div>
  );
}

export type SupplierBill = { no: string; supplier: string; invoiceNo: string; grn: string; invoiceDate: string; dueDate: string; amount: number; grnValue: number; paid: number; credited: number; held?: string };

export const billBalance = (b: SupplierBill) => r2(b.amount - b.paid - b.credited);

export function billState(b: SupplierBill, today: string): { label: string; tone: "ok" | "warn" | "bad" | "neutral" } {
  const bal = billBalance(b);
  if (bal <= 0.005) return { label: "Settled", tone: "ok" };
  if (b.held) return { label: "Held: query", tone: "bad" };
  if (b.dueDate < today) return { label: `Overdue ${daysBetween(b.dueDate, today)} day${daysBetween(b.dueDate, today) === 1 ? "" : "s"}`, tone: "bad" };
  if (b.paid || b.credited) return { label: "Part paid", tone: "warn" };
  return { label: `Due ${formatDate(b.dueDate, { short: true })}`, tone: "neutral" };
}

const TONE: Record<"ok" | "warn" | "bad" | "neutral", string> = {
  ok: "bg-status-success-bg text-status-success-text",
  warn: "bg-status-warning-bg text-status-warning-text",
  bad: "bg-status-error-bg text-status-error-text",
  neutral: "bg-muted text-muted-foreground",
};

/** AC-10: bills against goods received, with what's paid, credited and left, and why any is held. */
export function SupplierBills({ bills, today }: { bills: SupplierBill[]; today: string }) {
  const t = bills.reduce((s, b) => ({ amount: s.amount + b.amount, paid: s.paid + b.paid, credited: s.credited + b.credited }), { amount: 0, paid: 0, credited: 0 });
  return (
    <div className="overflow-x-auto rounded-md border border-[var(--color-border-panel)] bg-card">
      <table className="w-full min-w-[760px] text-sm">
        <thead className="bg-[var(--color-bg-surface-subtle)] text-left text-xs text-muted-foreground">
          <tr>
            <th className={th}>Bill</th>
            <th className={th}>Against</th>
            <th className={th}>Status</th>
            <th className={thR}>Amount</th>
            <th className={thR}>Paid</th>
            <th className={thR}>Credited</th>
            <th className={thR}>Balance</th>
          </tr>
        </thead>
        <tbody className="divide-y divide-[var(--color-border-subtle)]">
          {bills.map((b) => {
            const s = billState(b, today);
            const diff = r2(b.amount - b.grnValue);
            return (
              <tr key={b.no} className="align-top">
                <td className="px-2 py-2">
                  <span className="font-mono text-xs">{b.no}</span>
                  <span className="block text-xs text-muted-foreground">
                    {b.supplier} · inv {b.invoiceNo}
                  </span>
                </td>
                <td className="px-2 py-2 text-xs">
                  <span className="font-mono">{b.grn}</span>
                  {Math.abs(diff) > 1 ? <span className="block text-status-warning-text">{diff > 0 ? `${kes(diff)} more than received` : `${kes(-diff)} less than received`}</span> : <span className="block text-muted-foreground">Agrees with GRN</span>}
                </td>
                <td className="px-2 py-2">
                  <span className={cn("rounded px-1.5 py-0.5 text-[11px] font-medium whitespace-nowrap", TONE[s.tone])}>{s.label}</span>
                  {b.held ? <span className="mt-0.5 block text-xs text-muted-foreground">{b.held}</span> : null}
                </td>
                <td className="px-2 py-2 text-right tabular-nums">{kes(b.amount)}</td>
                <td className="px-2 py-2 text-right tabular-nums">{b.paid ? kes(b.paid) : "–"}</td>
                <td className="px-2 py-2 text-right tabular-nums">{b.credited ? kes(b.credited) : "–"}</td>
                <td className="px-2 py-2 text-right font-medium tabular-nums">{kes(billBalance(b))}</td>
              </tr>
            );
          })}
        </tbody>
        <tfoot className="border-t-2 border-[var(--color-border-panel)] bg-muted/30 font-semibold">
          <tr>
            <td colSpan={3} className="px-2 py-2">
              {count(bills.length, "bill")}
            </td>
            <td className="px-2 py-2 text-right tabular-nums">{kes(t.amount)}</td>
            <td className="px-2 py-2 text-right tabular-nums">{kes(t.paid)}</td>
            <td className="px-2 py-2 text-right tabular-nums">{kes(t.credited)}</td>
            <td className="px-2 py-2 text-right tabular-nums">{kes(t.amount - t.paid - t.credited)}</td>
          </tr>
        </tfoot>
      </table>
    </div>
  );
}

/* ------------------------------------ Supplier credit note (AC-10) ------------------------------------ */

export type CreditNoteDraft = { bill: string; supplierCn: string; returnRef: string; amount: string; reason: string };

/** A supplier credit note reduces one bill. It can't take a bill below zero; a return reference links it to the goods sent back. */
export function creditNoteProblems(d: CreditNoteDraft, bills: SupplierBill[]) {
  const p: string[] = [];
  const b = bills.find((x) => x.no === d.bill);
  if (!b) p.push("Choose the bill this credit note reduces.");
  if (!d.supplierCn.trim()) p.push("Enter the supplier's credit note number.");
  const amt = Number(d.amount.replace(/,/g, ""));
  if (!/^\d+(\.\d{1,2})?$/.test(d.amount.replace(/,/g, "")) || amt <= 0) p.push("Enter the credit amount.");
  else if (b && amt > billBalance(b) + 0.005) p.push(`That's more than the ${kes(billBalance(b))} still owed on ${b.no}. Record the rest as credit on account with the supplier.`);
  if (!d.reason.trim()) p.push("Say what the credit is for.");
  return p;
}

export function SupplierCreditNoteForm({ d, onChange, bills, returns, problems, tried }: { d: CreditNoteDraft; onChange: (d: CreditNoteDraft) => void; bills: SupplierBill[]; returns: { ref: string; value: number }[]; problems: string[]; tried: boolean }) {
  const set = <K extends keyof CreditNoteDraft>(k: K, v: CreditNoteDraft[K]) => onChange({ ...d, [k]: v });
  const open = bills.filter((b) => billBalance(b) > 0.005);
  const b = bills.find((x) => x.no === d.bill);
  const ret = returns.find((r) => r.ref === d.returnRef);
  return (
    <div className="space-y-3">
      <div className="grid gap-3 sm:grid-cols-2">
        <div className="space-y-1">
          <label htmlFor="cn-bill" className="text-sm font-medium">
            Reduces bill
          </label>
          <NativeSelect id="cn-bill" value={d.bill} onChange={(e) => set("bill", e.target.value)}>
            <NativeSelectOption value="">Choose…</NativeSelectOption>
            {open.map((x) => (
              <NativeSelectOption key={x.no} value={x.no}>
                {x.no} · {x.supplier} · owes {kes(billBalance(x))}
              </NativeSelectOption>
            ))}
          </NativeSelect>
        </div>
        <div className="space-y-1">
          <label htmlFor="cn-no" className="text-sm font-medium">
            Supplier&apos;s credit note number
          </label>
          <Input id="cn-no" className="uppercase" value={d.supplierCn} onChange={(e) => set("supplierCn", e.target.value)} />
        </div>
        <div className="space-y-1">
          <label htmlFor="cn-ret" className="text-sm font-medium">
            For goods returned (optional)
          </label>
          <NativeSelect id="cn-ret" value={d.returnRef} onChange={(e) => set("returnRef", e.target.value)}>
            <NativeSelectOption value="">Not for a return</NativeSelectOption>
            {returns.map((r) => (
              <NativeSelectOption key={r.ref} value={r.ref}>
                {r.ref} · {kes(r.value)}
              </NativeSelectOption>
            ))}
          </NativeSelect>
        </div>
        <div className="space-y-1">
          <label htmlFor="cn-amt" className="text-sm font-medium">
            Credit amount
          </label>
          <Input id="cn-amt" className="tabular-nums" inputMode="decimal" value={d.amount} onChange={(e) => set("amount", e.target.value)} />
          <p className="text-xs text-muted-foreground">{ret && !d.amount ? `The return was worth ${kes(ret.value)}.` : b ? `${b.no} has ${kes(billBalance(b))} left to pay.` : null}</p>
        </div>
        <div className="space-y-1 sm:col-span-2">
          <label htmlFor="cn-why" className="text-sm font-medium">
            What it&apos;s for
          </label>
          <Input id="cn-why" value={d.reason} onChange={(e) => set("reason", e.target.value)} />
        </div>
      </div>
      {tried && problems.length ? (
        <ul role="alert" className="list-disc pl-5 text-sm text-status-error-text">
          {problems.map((x) => (
            <li key={x}>{x}</li>
          ))}
        </ul>
      ) : null}
    </div>
  );
}
