"use client";

/**
 * Accounts → Payables (PRD 6.3, 6.4; plan I, approved 26 Oct 2026): supplier bills against goods received and their
 * credit notes (AC-10), and the payable reports (AC-11): payments with WHT and the bills each settled
 * (`PaymentRecords`, cumulative), credit notes (`SupplierCreditNotes`) and what we owe by age (`AgeingChart`).
 * Each supplier's statement stays on their record. Bills start from a switch-over date per pharmacy (Q1).
 */

import * as React from "react";
import { PlusIcon } from "lucide-react";

import { SupplierCreditNoteForm, creditNoteProblems, type CreditNoteDraft } from "@/components/medora/goods-in";
import { PanelError } from "@/components/medora/errors";
import { MedoraPageHeader } from "@/components/medora/page-header";
import { PaymentRecords, SupplierCreditNotes } from "@/components/medora/record-lists";
import { AgeingChart } from "@/components/medora/report-charts";
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 { 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, TabsList, TabsTrigger } from "@/components/ui/tabs";
import { toast } from "@/components/ui/toast";
import { PayablesApi, SuppliersApi, type PayablesAgeing, type SupplierBillRow, type SupplierCreditRow, type SupplierPaymentRow, type UnbilledGrn } from "@/lib/api";
import { useTabParam } from "@/lib/use-tab-param";
import { formatKES } from "@/lib/format";
import { useMenuPermissions } from "@/lib/permissions";

import { BillsView } from "./bills-view";
import { ChartCard } from "@/components/medora/records/reports";
import { Surface } from "@/components/medora/surface";
import { Loading } from "@/components/medora/loading";

type Tab = "bills" | "credits" | "payments" | "ageing";
const PAGES: Record<Tab, string> = { bills: "Accounts/Supplier bills", credits: "Accounts/Supplier bills", payments: "Accounts/Payables", ageing: "Accounts/Payables" };
const LABEL: Record<Tab, string> = { bills: "Bills", credits: "Credit notes", payments: "Payments made", ageing: "Ageing" };

const kes = (n: number) => formatKES(n, { decimals: true });
const pad = (x: number) => String(x).padStart(2, "0");
const localToday = () => {
  const d = new Date();
  return `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())}`;
};
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;
};

export default function PayablesPage() {
  const { can, loaded } = useMenuPermissions();
  const tabs = (Object.keys(PAGES) as Tab[]).filter((t) => loaded && can(PAGES[t], "Read"));
  const [tabParam, setTab] = useTabParam(); // page tabs change the URL (tabs spec 7.1)
  const tab = tabParam as Tab | null;
  const startCredit = React.useRef<(() => void) | null>(null);
  const current = tab && tabs.includes(tab) ? tab : (tabs[0] ?? null);
  const today = localToday();
  const [from, setFrom] = React.useState(`${today.slice(0, 8)}01`);
  const [to, setTo] = React.useState(today);
  const [setup, setSetup] = React.useState<{ billsFrom: string | null } | null>(null);
  const [bills, setBills] = React.useState<SupplierBillRow[] | null>(null);
  const [unbilled, setUnbilled] = React.useState<UnbilledGrn[]>([]);
  const [credits, setCredits] = React.useState<SupplierCreditRow[] | null>(null);
  const [payments, setPayments] = React.useState<SupplierPaymentRow[] | null>(null);
  const [ageing, setAgeing] = React.useState<PayablesAgeing | null>(null);
  const [switchDay, setSwitchDay] = React.useState(today);
  const [switching, setSwitching] = React.useState(false);

  React.useEffect(() => {
    if (!loaded || !tabs.length) return;
    PayablesApi.setup()
      .then(setSetup)
      .catch(() => setSetup({ billsFrom: null }));
  }, [loaded, tabs.length]);

  const [failed, setFailed] = React.useState<string | null>(null); // R12 (card 037): what failed to load, shown with Try again in place of the view
  const shown = failed ? null : current;
  const load = React.useCallback(() => {
    setFailed(null);
    const fail = (what: string) => (e: unknown) => (setFailed(what), toast.add({ type: "error", title: `Couldn't load ${what}`, description: apiError(e, "Try again.") }));
    if (current === "bills") {
      setBills(null);
      PayablesApi.bills({ from, to }).then(setBills).catch((e) => (setBills([]), fail("bills")(e)));
      PayablesApi.unbilled().then(setUnbilled).catch(() => setUnbilled([]));
    } else if (current === "credits") {
      setCredits(null);
      PayablesApi.credits({ from, to }).then(setCredits).catch((e) => (setCredits([]), fail("credit notes")(e)));
    } else if (current === "payments") {
      setPayments(null);
      PayablesApi.payments({ from, to }).then(setPayments).catch((e) => (setPayments([]), fail("payments")(e)));
    } else if (current === "ageing") {
      setAgeing(null);
      PayablesApi.ageing().then(setAgeing).catch(fail("ageing"));
    }
  }, [current, from, to]);
  React.useEffect(() => load(), [load]);

  const may = (t: Tab) => (a: string) => loaded && can(PAGES[t], a);

  const switchOver = async () => {
    setSwitching(true);
    try {
      setSetup(await PayablesApi.switchOver(switchDay));
      toast.add({ title: `Supplier bills from ${switchDay}`, description: "Goods received from then wait in Goods received not invoiced for their bill." });
      load();
    } catch (e) {
      toast.add({ type: "error", title: "Not switched", description: apiError(e, "Try again.") });
    } finally {
      setSwitching(false);
    }
  };

  if (!loaded)
    return (
      <Loading what="payables" columns={6} rows={6} numeric={[3, 4, 5]} />
    );

  return (
    <div className="medora mx-auto w-full max-w-6xl space-y-4">
      <MedoraPageHeader
        title="Payables"
        description="Supplier bills, credit notes, payments made and what is owed by age."
        primary={current === "credits" && may("credits")("Create") ? { label: "Record a credit note", icon: PlusIcon, onSelect: () => startCredit.current?.() } : undefined}
      />
      {!current ? (
        <p className="text-sm text-muted-foreground">Your role can&apos;t see payables.</p>
      ) : (
        <>
          {setup && !setup.billsFrom ? (
            <div className="space-y-2 rounded-lg border border-[var(--color-border-panel)] bg-card p-4 text-sm">
              <p className="font-medium">This pharmacy doesn&apos;t enter supplier bills yet</p>
              <p className="text-muted-foreground">Goods received are owed to the supplier as soon as they arrive. Once you start entering bills, goods received from that day wait in Goods received not invoiced until the supplier&apos;s bill is entered, and what you owe ages from each bill. This is set once.</p>
              {can("Accounts/General ledger", "Approve") ? (
                <div className="flex flex-wrap items-end gap-2">
                  <Field className="w-44">
                    <FieldLabel htmlFor="pb-from">Start from</FieldLabel>
                    <Input id="pb-from" type="date" min={today} value={switchDay} onChange={(e) => setSwitchDay(e.target.value)} />
                  </Field>
                  <Button disabled={switching || !switchDay} onClick={() => void switchOver()}>
                    {switching ? <Spinner /> : null}
                    Start entering bills
                  </Button>
                </div>
              ) : (
                <p className="text-xs text-muted-foreground">An administrator (Approve on the general ledger) sets the date.</p>
              )}
            </div>
          ) : null}

          <div className="flex flex-wrap items-end justify-between gap-3">
            <Tabs value={current} onValueChange={(v) => setTab(v as Tab)}>
              <TabsList variant="line">
                {tabs.map((t) => (
                  <TabsTrigger key={t} value={t} className="flex-none">
                    {LABEL[t]}
                  </TabsTrigger>
                ))}
              </TabsList>
            </Tabs>
            {current !== "ageing" ? (
              <Surface className="flex gap-2">
                <Field className="w-40">
                  <FieldLabel htmlFor="pb-rfrom">From</FieldLabel>
                  <Input id="pb-rfrom" type="date" value={from} max={to} onChange={(e) => e.target.value && setFrom(e.target.value)} />
                </Field>
                <Field className="w-40">
                  <FieldLabel htmlFor="pb-rto">To</FieldLabel>
                  <Input id="pb-rto" type="date" value={to} min={from} onChange={(e) => e.target.value && setTo(e.target.value)} />
                </Field>
              </Surface>
            ) : null}
          </div>

          {failed ? <PanelError framed what={failed} onRetry={load} /> : null}
          {shown === "bills" ? <BillsView bills={bills} unbilled={unbilled} billsFrom={setup?.billsFrom ?? null} today={today} may={may("bills")} reload={load} /> : null}
          {shown === "credits" ? <CreditsView rows={credits} billsFrom={setup?.billsFrom ?? null} reload={load} startRef={startCredit} /> : null}
          {shown === "payments" ? (
            payments ? (
              <div className="space-y-2">
                <PaymentRecords rows={payments} partyLabel="Supplier" cumulative />
                <p className="text-xs text-muted-foreground">Amounts include withholding tax kept back, which settles bills as the money does. Payments are recorded on each supplier&apos;s statement.</p>
              </div>
            ) : (
              <Loading what="payments" columns={5} rows={6} numeric={[4]} />
            )
          ) : null}
          {shown === "ageing" ? <AgeingView data={ageing} /> : null}
        </>
      )}
    </div>
  );
}

function CreditsView({ rows, billsFrom, reload, startRef }: { rows: SupplierCreditRow[] | null; billsFrom: string | null; reload: () => void; startRef: React.RefObject<(() => void) | null> }) {
  const [open, setOpen] = React.useState(false);
  const [suppliers, setSuppliers] = React.useState<{ id: number; name: string }[]>([]);
  const [supplierId, setSupplierId] = React.useState("");
  const [bills, setBills] = React.useState<SupplierBillRow[]>([]);
  const [returns, setReturns] = React.useState<{ id: number; ref: string; supplierId: number; value: number }[]>([]);
  const [d, setD] = React.useState<CreditNoteDraft>({ bill: "", supplierCn: "", returnRef: "", amount: "", reason: "" });
  const [tried, setTried] = React.useState(false);
  const [busy, setBusy] = React.useState(false);

  const start = () => {
    setOpen(true);
    setTried(false);
    setSupplierId("");
    setD({ bill: "", supplierCn: "", returnRef: "", amount: "", reason: "" });
    (SuppliersApi.getAll() as Promise<{ id: number; name: string }[]>).then((s) => setSuppliers(s.map((x) => ({ id: x.id, name: x.name })).sort((a, b) => a.name.localeCompare(b.name)))).catch(() => setSuppliers([]));
    PayablesApi.bills({ view: "open" }).then(setBills).catch(() => setBills([]));
    PayablesApi.returnsAwaiting().then(setReturns).catch(() => setReturns([]));
  };
  // R2 (card 037): "Record a credit note" is in the page header and starts the form through this
  React.useEffect(() => {
    startRef.current = start;
    return () => {
      startRef.current = null;
    };
  });

  const sid = Number(supplierId);
  const theirBills = bills.filter((b) => b.supplierId === sid).map((b) => ({ no: b.no, supplier: b.supplier, invoiceNo: b.invoiceNo, grn: b.grn, invoiceDate: b.invoiceDate, dueDate: b.dueDate, amount: b.amount, grnValue: b.grnValue, paid: b.paid, credited: b.credited, held: b.held }));
  const theirReturns = returns.filter((r) => r.supplierId === sid);
  // Without bills (before the switch-over) a credit note goes on the supplier's account: the design's bill check doesn't apply
  const problems = [...(sid ? [] : ["Choose the supplier."]), ...(billsFrom ? creditNoteProblems(d, theirBills) : creditNoteProblems({ ...d, bill: "x" }, [{ no: "x", supplier: "", invoiceNo: "", grn: "", invoiceDate: "", dueDate: "", amount: 1e12, grnValue: 0, paid: 0, credited: 0 }]))];

  const save = async () => {
    setTried(true);
    if (problems.length) return;
    setBusy(true);
    try {
      const bill = bills.find((b) => b.no === d.bill);
      const ret = returns.find((r) => r.ref === d.returnRef);
      const c = await PayablesApi.createCredit({ supplierId: sid, billId: bill?.id, returnId: ret?.id, supplierCn: d.supplierCn, amount: d.amount, reason: d.reason });
      toast.add({ title: `${c.no} recorded`, description: `${kes(c.amount)} off ${c.bill === "On account" ? "the supplier's account" : c.bill}.` });
      setOpen(false);
      reload();
    } catch (e) {
      toast.add({ type: "error", title: "Credit note not recorded", description: apiError(e, "Try again.") });
    } finally {
      setBusy(false);
    }
  };

  if (!rows)
    return (
      <Loading what="credit notes" columns={6} rows={5} numeric={[5]} />
    );
  return (
    <div className="space-y-4">
      <SupplierCreditNotes rows={rows} />
      <Dialog open={open} onOpenChange={(o) => !o && !busy && setOpen(false)}>
        <DialogContent className="sm:max-w-2xl">
          <DialogHeader>
            <DialogTitle>Supplier credit note</DialogTitle>
            <DialogDescription>{billsFrom ? "It reduces one bill. For goods sent back, choose the return: it then reads Credited." : "It reduces what the supplier is owed. For goods sent back, choose the return: it then reads Credited."}</DialogDescription>
          </DialogHeader>
          <Field>
            <FieldLabel htmlFor="cn-sup">Supplier</FieldLabel>
            <NativeSelect id="cn-sup" value={supplierId} onChange={(e) => (setSupplierId(e.target.value), setD({ ...d, bill: "", returnRef: "" }))}>
              <NativeSelectOption value="">Choose…</NativeSelectOption>
              {suppliers.map((s) => (
                <NativeSelectOption key={s.id} value={String(s.id)}>
                  {s.name}
                </NativeSelectOption>
              ))}
            </NativeSelect>
            {!billsFrom ? <FieldDescription>This pharmacy doesn&apos;t enter bills yet, so the bill can be left empty.</FieldDescription> : null}
          </Field>
          {sid ? <SupplierCreditNoteForm d={d} onChange={setD} bills={theirBills} returns={theirReturns.map((r) => ({ ref: r.ref, value: r.value }))} problems={problems} tried={tried} /> : null}
          {tried && !sid ? <p className="text-sm text-status-error-text">Choose the supplier.</p> : null}
          <DialogFooter>
            <Button variant="outline" disabled={busy} onClick={() => setOpen(false)}>
              Cancel
            </Button>
            <Button disabled={busy} onClick={() => void save()}>
              {busy ? <Spinner /> : null}
              Record credit note
            </Button>
          </DialogFooter>
        </DialogContent>
      </Dialog>
    </div>
  );
}

function AgeingView({ data }: { data: PayablesAgeing | null }) {
  if (!data)
    return (
      <Loading what="ageing" shape="chart" />
    );
  if (!data.rows.length) return <p className="py-10 text-center text-sm text-muted-foreground">Nothing is owed to suppliers.</p>;
  return (
    <div className="space-y-4">
      {/* The design's chart container (chart spec: one white surface per chart) */}
      <ChartCard title="Ageing by supplier" height="">
        <AgeingChart rows={data.rows.slice(0, 12).map((r) => ({ label: r.label, buckets: r.buckets }))} />
      </ChartCard>
      <div className="overflow-x-auto rounded-lg border border-[var(--color-border-panel)] bg-card">
        <Table>
          <TableHeader>
            <TableRow>
              <TableHead>Supplier</TableHead>
              <TableHead className="text-right">0–30 days</TableHead>
              <TableHead className="text-right">31–60</TableHead>
              <TableHead className="text-right">61–90</TableHead>
              <TableHead className="text-right">Over 90</TableHead>
              <TableHead className="text-right">Owed</TableHead>
            </TableRow>
          </TableHeader>
          <TableBody>
            {data.rows.map((r) => (
              <TableRow key={r.id}>
                <TableCell>
                  <a className="underline-offset-2 hover:underline" href={`/suppliers?id=${r.id}`}>
                    {r.label}
                  </a>
                  {r.creditOnAccount ? <span className="block text-xs text-muted-foreground">{kes(r.creditOnAccount)} paid ahead</span> : null}
                </TableCell>
                {r.buckets.map((b, i) => (
                  <TableCell key={i} className="text-right tabular-nums">
                    {b ? kes(b) : "–"}
                  </TableCell>
                ))}
                <TableCell className="text-right font-medium tabular-nums">{kes(r.balance)}</TableCell>
              </TableRow>
            ))}
          </TableBody>
        </Table>
      </div>
      <p className="text-xs text-muted-foreground">
        Total owed {kes(data.total)}. Bills age from their due date (the supplier&apos;s terms){data.billsFrom ? `; goods received since ${data.billsFrom} are owed once their bill is entered` : ""}. Each supplier&apos;s statement is on their record.
      </p>
    </div>
  );
}
