"use client";

/**
 * Invoices (AC-07, AC-09; plan H): the list and the design's `InvoiceReport`, a new invoice (a credit sale for a
 * registered customer: stock leaves now, Q1), and one invoice with the design's `B2BChain` (order → invoice →
 * delivery note) and printable `DocumentSheet` as the invoice or its delivery note.
 */

import * as React from "react";
import { BanIcon, EyeIcon, TruckIcon } from "lucide-react";

import { B2BChain } from "@/components/medora/admin-more";
import { RecordDrawer } from "@/components/medora/drawer";
import { DocumentSheet, PrintButton, VAT_CLASS, type DocLine, type VatClass } from "@/components/medora/print";
import { InvoiceReport, ListTable, StatusTag } from "@/components/medora/record-lists";
import { RowActions } from "@/components/medora/row-actions";
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 { Tabs, TabsList, TabsTrigger } from "@/components/ui/tabs";
import { toast } from "@/components/ui/toast";
import { InvoicesApi, type InvoiceDetail, type InvoiceRow } from "@/lib/api";
import { formatPhone, formatDate } from "@/lib/format";
import { useSettings } from "@/lib/SettingsContext";

import { apiError, kes, linesOut, linesTotal, LinesEditor, localStamp, today, type Customer, type EditLine, type Item } from "./shared";
import { Loading } from "@/components/medora/loading";


/** What a new invoice starts with: empty, or a quotation's lines at the prices chosen when converting it. */
export type InvoiceDraft = { customerId: string; lines: EditLine[]; quotationId?: number; quotationNo?: string };

export function InvoicesView({ rows, reload, items, customers, may, draft, onDraft, printedBy }: {
  rows: InvoiceRow[] | null;
  reload: () => void;
  items: Item[];
  customers: Customer[];
  may: (a: string) => boolean;
  draft: InvoiceDraft | null;
  onDraft: (d: InvoiceDraft | null) => void;
  printedBy: string;
}) {
  const [openId, setOpenId] = React.useState<number | null>(null);
  // R7 (card 037): the invoices are shown once, as the list or as the report
  const [view, setView] = React.useState<"list" | "report">("list");
  if (!rows)
    return (
      <Loading what="invoices" columns={6} rows={6} toolbar={1} numeric={[3, 4]} footer />
    );
  return (
    <div className="space-y-6">
      <Tabs value={view} onValueChange={(v) => setView(v as typeof view)}>
        <TabsList>
          <TabsTrigger value="list">List</TabsTrigger>
          <TabsTrigger value="report">Report</TabsTrigger>
        </TabsList>
      </Tabs>
      {view === "list" ? (
      <ListTable
        rows={rows}
        rowKey={(r) => String(r.id)}
        search={{ placeholder: "Invoice, customer or LPO", text: (r) => `${r.number} ${r.customer} ${r.orderRef ?? ""} ${r.receiptId}` }}
        filters={[{ label: "State", all: "Any state", value: (r) => r.state }]}
        empty="No invoices in these dates."
        cols={[
          { key: "no", header: "Invoice", cell: (r) => (<><span className="font-mono text-xs">{r.number}</span><span className="block text-xs text-muted-foreground">{formatDate(r.date)} · due {formatDate(r.dueDate)}</span></>) },
          { key: "c", header: "Customer", cell: (r) => (<>{r.customer}{r.orderRef ? <span className="block font-mono text-xs text-muted-foreground">LPO {r.orderRef}</span> : null}</>) },
          { key: "s", header: "State", cell: (r) => (<><StatusTag status={r.state} />{r.deliveryNoteNo ? <span className="block text-xs text-muted-foreground">Delivered, {r.deliveryNoteNo}</span> : null}</>) },
          { key: "t", header: "Total", numeric: true, cell: (r) => kes(r.total), total: (s) => kes(s.reduce((t, r) => t + (r.state === "Cancelled" ? 0 : r.total), 0)) },
          { key: "o", header: "Outstanding", numeric: true, cell: (r) => kes(r.outstanding), total: (s) => kes(s.reduce((t, r) => t + r.outstanding, 0)) },
          { key: "a", header: "", cell: (r) => <RowActions label={r.number} actions={[{ label: "Open", icon: EyeIcon, onSelect: () => setOpenId(r.id) }]} /> },
        ]}
        totalsLabel={(s) => (s.length === 1 ? "1 invoice" : `${s.length} invoices`)}
      />
      ) : (
        <InvoiceReport today={today()} rows={rows.filter((r) => r.state !== "Cancelled").map((r) => ({ no: r.number, date: r.date, customer: r.customer, dueDate: r.dueDate, lines: r.lines, paid: r.paid }))} />
      )}

      {draft ? <InvoiceForm draft={draft} items={items} customers={customers} onClose={() => onDraft(null)} onSaved={(inv) => (onDraft(null), reload(), setOpenId(inv.id))} /> : null}
      {openId != null ? <InvoiceDrawer id={openId} may={may} printedBy={printedBy} onClose={() => setOpenId(null)} onChanged={reload} /> : null}
    </div>
  );
}

function InvoiceForm({ draft, items, customers, onClose, onSaved }: { draft: InvoiceDraft; items: Item[]; customers: Customer[]; onClose: () => void; onSaved: (i: InvoiceDetail) => void }) {
  const [customerId, setCustomerId] = React.useState(draft.customerId);
  const [lines, setLines] = React.useState<EditLine[]>(draft.lines);
  const [terms, setTerms] = React.useState({ days: "30", options: [0, 7, 14, 30, 45, 60, 90] });
  const [orderRef, setOrderRef] = React.useState("");
  const [note, setNote] = React.useState("");
  const [tried, setTried] = React.useState(false);
  const [busy, setBusy] = React.useState(false);

  React.useEffect(() => {
    if (!customerId) return;
    InvoicesApi.terms(Number(customerId))
      .then((t) => setTerms({ days: String(t.termsDays), options: t.options }))
      .catch(() => undefined);
  }, [customerId]);

  const { out, problems } = linesOut(lines, items);
  const all = [...(customerId ? [] : ["Choose the customer. An invoice is for a registered customer approved for credit."]), ...problems];
  const t = linesTotal(lines);

  const save = async () => {
    setTried(true);
    if (all.length) return;
    setBusy(true);
    try {
      const inv = await InvoicesApi.create({ customerId: Number(customerId), items: out, termsDays: Number(terms.days), orderRef: orderRef.trim() || undefined, note: note.trim() || undefined, quotationId: draft.quotationId, total: t.total });
      toast.add({ title: `Invoice ${inv.number} raised`, description: `${kes(inv.total)} to ${inv.customer}, due ${formatDate(inv.dueDate)}. The stock has left.` });
      onSaved(inv);
    } catch (e) {
      toast.add({ type: "error", title: "Invoice not raised", description: apiError(e, "Try again.") });
    } finally {
      setBusy(false);
    }
  };

  return (
    <Dialog open onOpenChange={(o) => !o && !busy && onClose()}>
      <DialogContent className="sm:max-w-3xl">
        <DialogHeader>
          <DialogTitle>{draft.quotationNo ? `Invoice from ${draft.quotationNo}` : "New invoice"}</DialogTitle>
          <DialogDescription>The stock leaves when the invoice is raised, and the total goes on the customer&apos;s account within their credit limit.</DialogDescription>
        </DialogHeader>
        <div className="space-y-3">
          <div className="grid gap-3">
            <Field>
              <FieldLabel htmlFor="inv-cust">Customer</FieldLabel>
              <NativeSelect id="inv-cust" value={customerId} onChange={(e) => setCustomerId(e.target.value)}>
                <NativeSelectOption value="">Choose a customer</NativeSelectOption>
                {customers.map((c) => (
                  <NativeSelectOption key={c.id} value={String(c.id)}>
                    {c.name}
                    {c.phone ? ` (${formatPhone(c.phone)})` : ""}
                  </NativeSelectOption>
                ))}
              </NativeSelect>
            </Field>
            <Field>
              <FieldLabel htmlFor="inv-terms">Terms</FieldLabel>
              <NativeSelect id="inv-terms" value={terms.days} onChange={(e) => setTerms({ ...terms, days: e.target.value })}>
                {terms.options.map((d) => (
                  <NativeSelectOption key={d} value={String(d)}>
                    {d ? `${d} days` : "Due on receipt"}
                  </NativeSelectOption>
                ))}
              </NativeSelect>
              <FieldDescription>Starts from the customer&apos;s last invoice.</FieldDescription>
            </Field>
            <Field>
              <FieldLabel htmlFor="inv-lpo">Customer&apos;s order (LPO) number</FieldLabel>
              <Input id="inv-lpo" className="font-mono" value={orderRef} onChange={(e) => setOrderRef(e.target.value)} placeholder="Optional" />
            </Field>
          </div>
          <LinesEditor lines={lines} onChange={setLines} items={items} idPrefix="inv" />
          <Field>
            <FieldLabel htmlFor="inv-note">Note on the invoice (optional)</FieldLabel>
            <Input id="inv-note" value={note} onChange={(e) => setNote(e.target.value)} />
          </Field>
          {tried && all.length ? (
            <ul className="list-disc space-y-0.5 pl-5 text-sm text-status-error-text">
              {all.map((p) => (
                <li key={p}>{p}</li>
              ))}
            </ul>
          ) : null}
        </div>
        <DialogFooter>
          <Button variant="outline" disabled={busy} onClick={onClose}>
            Cancel
          </Button>
          <Button disabled={busy} onClick={() => void save()}>
            {busy ? <Spinner /> : null}
            Raise invoice for {kes(t.total)}
          </Button>
        </DialogFooter>
      </DialogContent>
    </Dialog>
  );
}

function InvoiceDrawer({ id, may, printedBy, onClose, onChanged }: { id: number; may: (a: string) => boolean; printedBy: string; onClose: () => void; onChanged: () => void }) {
  const { settings } = useSettings();
  const [inv, setInv] = React.useState<InvoiceDetail | null>(null);
  const [failed, setFailed] = React.useState(false);
  const [doc, setDoc] = React.useState<"invoice" | "delivery-note">("invoice");
  const [act, setAct] = React.useState<null | "deliver" | "cancel">(null);
  const [text, setText] = React.useState("");
  const [busy, setBusy] = React.useState(false);
  const sheet = React.useRef<HTMLDivElement>(null);

  const load = React.useCallback(() => {
    setFailed(false);
    InvoicesApi.get(id)
      .then(setInv)
      .catch(() => setFailed(true));
  }, [id]);
  React.useEffect(() => load(), [load]);

  const run = async () => {
    if (!inv || !act) return;
    setBusy(true);
    try {
      const next = act === "deliver" ? await InvoicesApi.deliver(inv.id, text.trim() || undefined) : await InvoicesApi.cancel(inv.id, text.trim());
      setInv(next);
      toast.add({ title: act === "deliver" ? `Delivery note ${next.deliveryNoteNo} issued` : `${next.number} cancelled`, description: act === "cancel" ? "Its stock is back and its posting reversed." : undefined });
      if (act === "deliver") setDoc("delivery-note");
      setAct(null);
      setText("");
      onChanged();
    } catch (e) {
      toast.add({ type: "error", title: act === "deliver" ? "Delivery note not issued" : "Not cancelled", description: apiError(e, "Try again.") });
    } finally {
      setBusy(false);
    }
  };

  const cancelled = inv?.state === "Cancelled";
  const lines: DocLine[] = (inv?.items ?? []).map((l) => ({ code: l.code, name: l.name, unit: l.unit ?? undefined, qty: l.qty, price: l.price, discount: l.discount || undefined, vat: (l.taxCode && l.taxCode in VAT_CLASS ? l.taxCode : "B") as VatClass, ordered: doc === "delivery-note" ? l.qty : undefined }));

  return (
    <RecordDrawer open onOpenChange={(o) => !o && onClose()} width="lg" title={inv?.number ?? "Invoice"} subtitle={inv ? `${inv.customer} · ${inv.state}` : undefined} state={failed ? "error" : inv ? "ready" : "loading"} onRetry={load}>
      {inv ? (
        <div className="space-y-4">
          <B2BChain
            docs={[
              { kind: "Order", ref: inv.orderRef ?? undefined, status: inv.orderRef ? "The customer's LPO" : "No LPO given" },
              { kind: "Invoice", ref: inv.number, status: `${inv.state}, due ${formatDate(inv.dueDate)}`, amount: cancelled ? undefined : inv.total, paid: cancelled ? undefined : inv.paid },
              { kind: "Delivery note", ref: inv.deliveryNoteNo ?? undefined, status: inv.deliveryNoteNo ? `Delivered ${inv.deliveredAt ? formatDate(inv.deliveredAt) : ""}${inv.receivedBy ? `, signed by ${inv.receivedBy}` : ""}` : "Not issued" },
            ]}
          />
          {inv.cancelReason ? <p className="text-sm text-muted-foreground">Cancelled: {inv.cancelReason}</p> : null}
          <div className="flex flex-wrap items-center gap-2">
            <NativeSelect aria-label="Document" value={doc} onChange={(e) => setDoc(e.target.value as typeof doc)} className="w-48">
              <NativeSelectOption value="invoice">Invoice</NativeSelectOption>
              <NativeSelectOption value="delivery-note" disabled={!inv.deliveryNoteNo}>
                Delivery note
              </NativeSelectOption>
            </NativeSelect>
            <PrintButton target={sheet} paper="a4" label={doc === "invoice" ? "Print invoice" : "Print delivery note"} />
            {may("Update") && !inv.deliveryNoteNo && !cancelled ? (
              <Button variant="outline" onClick={() => (setAct("deliver"), setText(""))}>
                <TruckIcon data-icon="inline-start" />
                Issue delivery note
              </Button>
            ) : null}
            {may("Delete") && !cancelled && inv.paid === 0 ? (
              <Button variant="ghost" onClick={() => (setAct("cancel"), setText(""))}>
                <BanIcon data-icon="inline-start" />
                Cancel invoice
              </Button>
            ) : null}
          </div>
          <div className="max-h-[60svh] overflow-auto rounded-md bg-muted/40 p-3">
            <DocumentSheet
              ref={sheet}
              kind={doc}
              issuer={{ name: settings.store_name || "Pharmacy", branch: inv.branch ?? "", address: settings.store_address ? [settings.store_address] : [], phone: settings.store_phone || undefined, email: settings.store_email || undefined }}
              party={{ name: inv.party.name, kraPin: inv.party.kraPin ?? undefined, phone: inv.party.phone ?? undefined, address: inv.party.address ? [inv.party.address] : undefined }}
              reference={doc === "invoice" ? inv.number : (inv.deliveryNoteNo ?? "")}
              date={doc === "invoice" ? inv.date : (inv.deliveredAt ?? "").slice(0, 10)}
              lines={lines}
              printedBy={printedBy}
              printedAt={localStamp(new Date())}
              status={doc === "invoice" && !cancelled ? (inv.state === "Paid" ? "Paid" : inv.paid > 0 ? "Part paid" : "Unpaid") : undefined}
              dueDate={doc === "invoice" ? inv.dueDate : undefined}
              paid={doc === "invoice" ? inv.paid : undefined}
              related={[...(inv.orderRef ? [{ label: "Your order", value: inv.orderRef }] : []), ...(doc === "delivery-note" ? [{ label: "Invoice", value: inv.number }] : []), ...(inv.quotation ? [{ label: "Quotation", value: inv.quotation }] : [])]}
              notes={inv.cancelReason ? [`Cancelled: ${inv.cancelReason}`] : []}
            />
          </div>
        </div>
      ) : null}

      <Dialog open={!!act} onOpenChange={(o) => !o && !busy && setAct(null)}>
        <DialogContent>
          <DialogHeader>
            <DialogTitle>{act === "deliver" ? `Delivery note for ${inv?.number}` : `Cancel ${inv?.number}?`}</DialogTitle>
            <DialogDescription>{act === "deliver" ? "Numbered once. Print it for the customer to sign." : "Its sale is voided: the stock goes back and the posting is reversed. Not possible once anything is paid or returned."}</DialogDescription>
          </DialogHeader>
          <Field>
            <FieldLabel htmlFor="inv-act">{act === "deliver" ? "Received by (optional)" : "Why"}</FieldLabel>
            <Input id="inv-act" value={text} onChange={(e) => setText(e.target.value)} placeholder={act === "deliver" ? "Who signed for the goods" : "e.g. Raised for the wrong customer"} />
          </Field>
          <DialogFooter>
            <Button variant="outline" disabled={busy} onClick={() => setAct(null)}>
              Back
            </Button>
            <Button variant={act === "cancel" ? "destructive" : "default"} disabled={busy || (act === "cancel" && text.trim().length < 3)} onClick={() => void run()}>
              {busy ? <Spinner /> : null}
              {act === "deliver" ? "Issue delivery note" : "Cancel invoice"}
            </Button>
          </DialogFooter>
        </DialogContent>
      </Dialog>
    </RecordDrawer>
  );
}
