"use client";

import * as React from "react";
import Image from "next/image";
import { CheckIcon, ClockIcon, PauseIcon, PlayIcon, TriangleAlertIcon, XIcon } from "lucide-react";

import { Badge } from "@/components/ui/badge";
import { Button } from "@/components/ui/button";
import { Field, FieldLabel } from "@/components/ui/field";
import { RadioGroup, RadioGroupItem } from "@/components/ui/radio-group";
import { Spinner } from "@/components/ui/spinner";
import { formatDate } from "@/lib/format";
import type { PaymentMethod, Sale } from "@/lib/mock-data";
import { cn } from "@/lib/utils";

import { CurrencyInput, Money, QuantityStepper } from "./money";
import { CardPayment, MpesaPayment, type PaymentConfirmation } from "./payments";
import { EtimsBlock, type Etims } from "./print";
import { SplitTender, type SplitState, type TenderHandlers } from "./split-tender";

/** What the tender panel hands a live page (card 012): the tenders, and what goes on the customer's account. */
export type TenderResult = { payments: { method: "Cash" | "M-Pesa" | "Card"; amount: number; reference?: string; tendered?: number }[]; credit: number };
import { TONE_BOX, TONE_TEXT, type Tone } from "./tones";

export const VAT_RATE = 0.16;

export type CartItem = { id: number; name: string; strength?: string; unitPrice: number; qty: number; discount?: number; type?: "Product" | "Service"; maxQty?: number; /** Tax code letter (Settings → 9.4). */ taxCode?: string };

/* --------------------------------- Cart line ------------------------------------ */

/** One basket row: name, stepper, per-unit price, line total. `discount` is per unit, as the API stores it. */
export function CartLine({ item, disabled = false }: { item: CartItem; disabled?: boolean }) {
  const unit = item.unitPrice - (item.discount ?? 0);
  return (
    <div className={cn("grid grid-cols-[1fr_auto_5.5rem] items-center gap-3 border-b border-border px-3 py-2 last:border-b-0", disabled && "opacity-60")}>
      <div className="min-w-0">
        <p className="truncate text-sm font-medium">{item.name}</p>
        <p className="text-xs text-muted-foreground tabular-nums">
          {item.strength ? `${item.strength}, ` : ""}
          {unit.toFixed(2)} each
          {item.discount ? <span className="ml-1 text-status-success-text">(-{item.discount.toFixed(2)} discount)</span> : null}
          {item.type === "Service" ? <span className="ml-1">SERVICE</span> : null}
        </p>
      </div>
      <QuantityStepper size="sm" defaultValue={item.qty} max={item.type === "Service" ? 99 : (item.maxQty ?? 999)} disabled={disabled} />
      <Money amount={unit * item.qty} className="justify-end font-medium" />
    </div>
  );
}

/* -------------------------------- Cart summary ---------------------------------- */

export function cartTotals(lines: CartItem[], deliveryFee = 0, vatApplies = true) {
  const goods = lines.reduce((s, l) => s + (l.unitPrice - (l.discount ?? 0)) * l.qty, 0);
  // Delivery is folded into the taxable subtotal so that total = subtotal + tax.
  const subtotal = goods + deliveryFee;
  const vat = vatApplies ? Math.round(subtotal * VAT_RATE * 100) / 100 : 0;
  return { goods, subtotal, vat, total: subtotal + vat };
}

function SummaryRow({ label, children, strong }: { label: string; children: React.ReactNode; strong?: boolean }) {
  return (
    <div className={cn("flex items-baseline justify-between", strong ? "text-base font-semibold" : "text-sm")}>
      <span className={strong ? "" : "text-muted-foreground"}>{label}</span>
      {children}
    </div>
  );
}

export function CartSummary({
  items,
  deliveryFee = 0,
  vatApplies = true,
  held = false,
}: {
  items: CartItem[];
  deliveryFee?: number;
  vatApplies?: boolean;
  held?: boolean;
}) {
  const t = cartTotals(items, deliveryFee, vatApplies);
  const count = items.reduce((s, l) => s + l.qty, 0);
  return (
    <div className="space-y-2 rounded-lg border border-border bg-card p-3">
      {held ? (
        <Badge variant="outline" className={cn("rounded-md", TONE_BOX.info)}>
          <PauseIcon />
          Held, not yet paid
        </Badge>
      ) : null}
      <SummaryRow label={`Subtotal (${count} ${count === 1 ? "item" : "items"})`}>
        <Money amount={t.goods} />
      </SummaryRow>
      {deliveryFee > 0 ? (
        <SummaryRow label="Delivery fee">
          <Money amount={deliveryFee} />
        </SummaryRow>
      ) : null}
      <SummaryRow label={vatApplies ? "VAT 16%" : "VAT (not applied)"}>
        <Money amount={t.vat} />
      </SummaryRow>
      <div className="border-t border-border pt-2">
        <SummaryRow label="Total" strong>
          <Money amount={t.total} size="lg" />
        </SummaryRow>
      </div>
    </div>
  );
}

/* -------------------------------- Tender panel ---------------------------------- */

const METHODS: PaymentMethod[] = ["Cash", "M-Pesa", "Card", "Credit"];

/**
 * Payment panel. The bright teal accent button is reserved for taking the payment.
 * For Credit, pass `creditLimit` and `creditBalance` to show headroom; a sale beyond it blocks Pay.
 */
export function TenderPanel({
  total,
  defaultMethod = "Cash",
  defaultSplit = false,
  hasCustomer = false,
  defaultTendered = null,
  creditLimit = 0,
  creditBalance = 0,
  receiptNo = "RCP-005201",
  state = "default",
  mpesa,
  card,
  onComplete,
}: {
  total: number;
  /** Card 012: real M-Pesa and card handlers from a live page (STK push only until C2B); without them the previews run. */
  mpesa?: TenderHandlers["mpesa"];
  card?: TenderHandlers["card"];
  /**
   * Card 012: called by "Complete sale" with the tenders taken and the part put on account. Without it
   * (the demo) the button does nothing, as before.
   */
  onComplete?: (r: TenderResult) => void;
  defaultMethod?: PaymentMethod;
  defaultTendered?: number | null;
  creditLimit?: number;
  creditBalance?: number;
  /** The sale's receipt number: used as the Paybill account number so the payment matches the sale. */
  receiptNo?: string;
  /** Start in split mode (several methods for one sale). */
  defaultSplit?: boolean;
  /** Credit needs a registered customer; split mode uses this to allow a credit part. */
  hasCustomer?: boolean;
  state?: "default" | "processing" | "disabled";
}) {
  const [method, setMethod] = React.useState<PaymentMethod>(defaultMethod);
  const [tendered, setTendered] = React.useState<number>(defaultTendered ?? 0);
  const [confirmed, setConfirmed] = React.useState<PaymentConfirmation | null>(null);
  const [split, setSplit] = React.useState(defaultSplit);
  const [splitState, setSplitState] = React.useState<SplitState | null>(null);
  const busy = state === "processing";
  const off = state === "disabled" || busy;

  const change = method === "Cash" ? tendered - total : 0;
  const headroom = creditLimit - creditBalance;
  const creditShort = method === "Credit" && total > headroom;
  const cashShort = method === "Cash" && tendered < total;
  // M-Pesa and card are taken inside their own panels; the sale completes only once one is confirmed.
  const awaitingPayment = (method === "M-Pesa" || method === "Card") && confirmed?.method !== method;
  const blocked = split ? off || !splitState?.complete : off || creditShort || cashShort || awaitingPayment;
  const ready = split ? !!splitState?.complete : confirmed?.method === method;

  return (
    <div className="w-full max-w-sm space-y-4 rounded-lg border border-border bg-card p-4">
      <div className="flex items-end justify-between border-b border-border pb-3">
        <span className="text-xs whitespace-nowrap text-muted-foreground">Amount due</span>
        <Money amount={total} size="till" />
      </div>

      <div className="-mt-1 flex justify-end">
        <Button
          variant="link"
          size="sm"
          className="h-auto p-0 text-xs"
          disabled={off || (split && !!splitState?.payments.length)}
          title={split && splitState?.payments.length ? "Remove the applied payments first" : undefined}
          onClick={() => {
            setSplit((s) => !s);
            setSplitState(null);
            setConfirmed(null);
          }}
        >
          {split ? "Pay with one method" : "Split payment across methods"}
        </Button>
      </div>

      {split ? (
        <SplitTender total={total} accountRef={receiptNo} creditHeadroom={Math.max(0, headroom)} hasCustomer={hasCustomer} disabled={off} onChange={setSplitState} mpesa={mpesa} card={card} />
      ) : (
      <>
      <RadioGroup
        value={method}
        onValueChange={(v) => {
          setMethod(v as PaymentMethod);
          setConfirmed(null);
        }}
        className="grid grid-cols-2 gap-2"
        aria-label="Payment method"
      >
        {METHODS.map((m) => (
          <label
            key={m}
            className={cn(
              "flex cursor-pointer items-center justify-center gap-1.5 rounded-lg border border-border px-2 py-2 text-xs font-medium has-data-checked:border-primary has-data-checked:bg-muted",
              off && "cursor-not-allowed opacity-50",
            )}
          >
            <RadioGroupItem value={m} disabled={off} />
            {m}
          </label>
        ))}
      </RadioGroup>

      {method === "Cash" ? (
        <div className="space-y-2">
          <Field>
            <FieldLabel>Amount tendered</FieldLabel>
            <div
              onChange={(e) => {
                const n = Number((e.target as HTMLInputElement).value.replace(/[^0-9.]/g, ""));
                setTendered(Number.isFinite(n) ? n : 0);
              }}
            >
              <CurrencyInput size="till" aria-label="Amount tendered" disabled={off} defaultValue={defaultTendered} />
            </div>
          </Field>
          <div className="flex items-baseline justify-between text-sm">
            <span className="text-muted-foreground">{change < 0 ? "Still to pay" : "Change due"}</span>
            <Money amount={Math.abs(change)} size="lg" className={cn(change < 0 ? TONE_TEXT.error : TONE_TEXT.success)} />
          </div>
        </div>
      ) : null}

      {method === "M-Pesa" ? <MpesaPayment amount={total} accountRef={receiptNo} disabled={off} onConfirmed={setConfirmed} {...(mpesa ?? {})} /> : null}

      {method === "Card" ? <CardPayment amount={total} disabled={off} onConfirmed={setConfirmed} {...(card ?? {})} /> : null}

      {method === "Credit" ? (
        <div className={cn("space-y-1 rounded-lg border p-3 text-xs tabular-nums", creditShort ? TONE_BOX.error : TONE_BOX.neutral)}>
          <div className="flex justify-between">
            <span>Credit limit</span>
            <Money amount={creditLimit} size="sm" />
          </div>
          <div className="flex justify-between">
            <span>Current balance</span>
            <Money amount={creditBalance} size="sm" />
          </div>
          <div className="flex justify-between font-medium">
            <span>Headroom</span>
            <Money amount={Math.max(0, headroom)} size="sm" />
          </div>
          {creditShort ? (
            <p className="flex items-start gap-1.5 border-t border-status-error-border pt-2 font-medium">
              <TriangleAlertIcon className="mt-px size-3.5 shrink-0" />
              <span>
                This sale exceeds the credit limit by KES <span className="tabular-nums">{(total - headroom).toFixed(2)}</span>. Collect part payment or raise the limit.
              </span>
            </p>
          ) : null}
        </div>
      ) : null}
      </>
      )}

      <Button
        size="lg"
        disabled={blocked}
        onClick={() => {
          if (!onComplete || blocked) return;
          if (split && splitState) {
            const credit = splitState.payments.filter((p) => p.method === "Credit").reduce((s, p) => s + p.amount, 0);
            onComplete({
              credit: Math.round(credit * 100) / 100,
              payments: splitState.payments
                .filter((p) => p.method !== "Credit")
                .map((p) => ({ method: p.method as "Cash" | "M-Pesa" | "Card", amount: p.amount, reference: p.reference, tendered: p.method === "Cash" && p.change ? Math.round((p.amount + p.change) * 100) / 100 : undefined })),
            });
          } else if (method === "Credit") onComplete({ credit: total, payments: [] });
          else if (method === "Cash") onComplete({ credit: 0, payments: [{ method: "Cash", amount: total, tendered }] });
          else onComplete({ credit: 0, payments: [{ method, amount: total, reference: confirmed?.reference }] });
        }}
        className="h-12 w-full border-transparent bg-[var(--color-action-accent-default)] text-base font-semibold text-foreground hover:bg-[var(--color-action-accent-hover)]"
      >
        {busy ? <Spinner /> : null}
        {busy ? (
          "Processing"
        ) : ready ? (
          "Complete sale"
        ) : (
          <>
            Pay <Money amount={total} className="text-base" />
          </>
        )}
      </Button>
      {split && !ready && !off ? <p className="-mt-2 text-center text-xs text-muted-foreground">Completes once the remaining balance is 0.</p> : null}
      {!split && awaitingPayment && !off ? (
        <p className="-mt-2 text-center text-xs text-muted-foreground">{method === "M-Pesa" ? "Completes once M-Pesa confirms the payment." : "Completes once the card payment is approved or recorded."}</p>
      ) : null}
    </div>
  );
}

/* --------------------------------- M-Pesa status -------------------------------- */

/** `timeout` never offers a resend: the status is checked first (`checking`), and only a confirmed `unpaid` may resend (IG-05). */
export type MpesaState = "waiting" | "confirmed" | "failed" | "timeout" | "checking" | "unpaid";

export function MpesaStatus({
  state,
  phone,
  amount,
  receiptNo,
  reason,
  onCancel,
  onResend,
  onCheck,
  onOtherMethod,
}: {
  state: MpesaState;
  phone: string;
  amount: number;
  receiptNo?: string;
  reason?: string;
  onCancel?: () => void;
  onResend?: () => void;
  onCheck?: () => void;
  onOtherMethod?: () => void;
}) {
  const cfg: Record<MpesaState, { tone: Tone; title: string; body: string }> = {
    waiting: { tone: "info", title: "Waiting for customer", body: `A payment prompt was sent to ${phone}. Ask the customer to enter their M-Pesa PIN.` },
    confirmed: { tone: "success", title: "Payment confirmed", body: "Safaricom has confirmed the payment." },
    failed: { tone: "error", title: "Payment failed", body: reason ?? "The customer cancelled the prompt or the PIN was wrong." },
    timeout: { tone: "warning", title: "No response yet", body: "No answer from the phone after 60 seconds. The customer may still have paid, so check with M-Pesa before sending the prompt again." },
    checking: { tone: "info", title: "Checking with M-Pesa", body: "Asking Safaricom whether this payment went through." },
    unpaid: { tone: "warning", title: "Not paid", body: "Safaricom confirms no money was taken. It is safe to send the prompt again or take another method." },
  };
  const c = cfg[state];
  const Icon = state === "confirmed" ? CheckIcon : state === "failed" ? XIcon : state === "timeout" || state === "unpaid" ? ClockIcon : null;
  return (
    <div className={cn("w-full max-w-sm space-y-3 rounded-lg border p-4", TONE_BOX[c.tone])} role={state === "failed" ? "alert" : "status"}>
      <div className="flex items-center gap-2 text-sm font-semibold">
        {Icon ? <Icon className="size-4" /> : <Spinner className="size-4" />}
        {c.title}
        <span className="ml-auto tabular-nums">
          <Money amount={amount} />
        </span>
      </div>
      <p className="text-xs">{c.body}</p>
      {state === "confirmed" && receiptNo ? (
        <p className="flex justify-between border-t border-status-success-border pt-2 text-xs">
          <span>M-Pesa receipt</span>
          <span className="font-semibold tabular-nums">{receiptNo}</span>
        </p>
      ) : null}
      {state === "timeout" ? (
        <Button size="sm" variant="outline" className="bg-background" onClick={onCheck}>
          Check payment status
        </Button>
      ) : null}
      {state === "failed" || state === "unpaid" ? (
        <div className="flex gap-2">
          <Button size="sm" variant="outline" className="bg-background" onClick={onResend}>
            Resend prompt
          </Button>
          <Button size="sm" variant="ghost" onClick={onOtherMethod}>
            Use another method
          </Button>
        </div>
      ) : null}
      {state === "waiting" ? (
        <Button size="sm" variant="outline" className="bg-background" onClick={onCancel}>
          Cancel
        </Button>
      ) : null}
    </div>
  );
}

/* ----------------------------------- Receipt ------------------------------------ */

export type ReceiptData = {
  receiptNo: string;
  date: string;
  time: string;
  cashier: string;
  branch: string;
  items: CartItem[];
  deliveryFee?: number;
  method: PaymentMethod;
  tendered?: number;
  mpesaRef?: string;
  /** KRA signing details; without them the receipt says they will follow. */
  etims?: Etims;
  /** A reprint says so at the top. */
  reprint?: { at: string; by: string };
  /**
   * Tax codes (Settings → 9.4). With them, each line prints its code letter and VAT is worked out per code
   * (PS-07); delivery uses `deliveryTaxCode`. Without them the receipt falls back to a flat 16%.
   */
  taxCodes?: { code: string; name: string; rate: number }[];
  deliveryTaxCode?: string;
  /**
   * Card 015: the pharmacy's own details and receipt settings (General settings, ST-16). Without them the receipt
   * shows the design's sample header and footer, as in the demos.
   */
  issuer?: { name: string; tagline?: string; address?: string; phone?: string; email?: string; kraPin?: string; title?: string; header?: string; footer?: string };
  /** Hide "Served by" (receipt setting). Default shown. */
  hideCashier?: boolean;
  customer?: string;
  /** Card 015: each tender of a split payment (PS-06), and what went on account. Replaces the single method line. */
  payments?: { method: "Cash" | "M-Pesa" | "Card"; amount: number; reference?: string; tendered?: number }[];
  credit?: number;
  /** How the `credit` line reads. Default "On account"; a collected preorder's deposit (PS-14) was paid before. */
  creditLabel?: string;
  /** Card 015: where a delivery goes and who carries it (PS-13); plan E adds a courier company and the pickup point. */
  delivery?: { locality?: string; address?: string; rider?: string; riderPhone?: string; courier?: string; pickup?: string };
  /**
   * Plan E (PS-07, Q2): promotions given on the sale (already inside the line discounts, named so the customer sees
   * them), and what set the discounts when it wasn't a promotion ("Discount given by the cashier", "Group prices").
   */
  promotions?: { name: string; amount: number }[];
  discountNote?: string;
  /** Card 015: false for a sale that carries no VAT (not sent to eTIMS): no VAT lines, total = subtotal. Default true. */
  vatApplies?: boolean;
  /** Card 015: `false` leaves out the eTIMS block, for a sale not sent to KRA (or while eTIMS isn't connected, IG-09). */
  etimsBlock?: boolean;
};

/** VAT per tax code for a receipt; lines without a code use B (standard), as migrated products do. */
function receiptTax(data: ReceiptData) {
  const codes = data.taxCodes!;
  const rows = new Map<string, number>();
  for (const l of data.items) {
    const c = l.taxCode ?? "B";
    rows.set(c, (rows.get(c) ?? 0) + (l.unitPrice - (l.discount ?? 0)) * l.qty);
  }
  if (data.deliveryFee) rows.set(data.deliveryTaxCode ?? "B", (rows.get(data.deliveryTaxCode ?? "B") ?? 0) + data.deliveryFee);
  const byCode = [...rows.entries()]
    .sort(([a], [b]) => a.localeCompare(b))
    .map(([code, taxable]) => {
      const c = codes.find((x) => x.code === code);
      return { code, label: c ? (c.rate ? `${c.name} ${c.rate}%` : c.name) : code, taxable, vat: Math.round(taxable * ((c?.rate ?? 0) / 100) * 100) / 100 };
    });
  const vat = byCode.reduce((s, r) => s + r.vat, 0);
  const subtotal = byCode.reduce((s, r) => s + r.taxable, 0);
  return { byCode, vat, total: Math.round((subtotal + vat) * 100) / 100 };
}

/** Thermal receipt preview. Width follows the paper: 58mm is about 220px, 80mm about 300px. Print with printElement(el, "receipt-80"). */
export const Receipt = React.forwardRef<HTMLDivElement, { data: ReceiptData; paper?: "58mm" | "80mm" }>(function Receipt({ data, paper = "80mm" }, ref) {
  const noVat = data.vatApplies === false;
  const flat = cartTotals(data.items, data.deliveryFee ?? 0, !noVat);
  const coded = data.taxCodes && !noVat ? receiptTax(data) : null;
  const t = coded ? { goods: flat.goods, vat: coded.vat, total: coded.total } : flat;
  const narrow = paper === "58mm";
  return (
    <div ref={ref} className={cn("mx-auto border border-border bg-white p-3 text-[11px] leading-4 text-black", narrow ? "w-[220px]" : "w-[302px]")} aria-label={`Receipt ${data.receiptNo}`}>
      {data.reprint ? (
        <p className="mb-2 border border-black py-0.5 text-center font-bold uppercase">
          Copy. Reprinted {formatDate(data.reprint.at.slice(0, 10), { short: true })} {data.reprint.at.slice(11, 16)}, {data.reprint.by}
        </p>
      ) : null}
      <div className="flex flex-col items-center gap-0.5 text-center">
        <Image src="/medora-logo.svg" alt="Medora" width={884} height={238} unoptimized className="h-6 w-auto grayscale contrast-[1.8]" />
        {data.issuer ? (
          <>
            <p className="font-semibold">{data.issuer.name}</p>
            {data.issuer.tagline ? <p>{data.issuer.tagline}</p> : null}
            {data.branch ? <p>{data.branch}</p> : null}
            {data.issuer.address ? <p>{data.issuer.address}</p> : null}
            {data.issuer.phone ? <p>Tel {data.issuer.phone}</p> : null}
            {data.issuer.email ? <p>{data.issuer.email}</p> : null}
            {data.issuer.kraPin ? <p>KRA PIN {data.issuer.kraPin}</p> : null}
            {data.issuer.title ? <p className="mt-1 font-bold uppercase">{data.issuer.title}</p> : null}
            {data.issuer.header ? <p>{data.issuer.header}</p> : null}
          </>
        ) : (
          <>
            <p className="font-semibold">{data.branch}</p>
            <p>Tel +254 712 345 678</p>
            <p>KRA PIN P000000000X</p>
          </>
        )}
      </div>
      <Rule />
      <div className="tabular-nums">
        <p className="flex justify-between">
          <span>Receipt</span>
          <span>{data.receiptNo}</span>
        </p>
        <p className="flex justify-between">
          <span>{formatDate(data.date)}</span>
          <span>{data.time}</span>
        </p>
        {data.hideCashier ? null : (
          <p className="flex justify-between">
            <span>Served by</span>
            <span>{data.cashier}</span>
          </p>
        )}
        {data.customer ? (
          <p className="flex justify-between gap-2">
            <span>Customer</span>
            <span className="text-right">{data.customer}</span>
          </p>
        ) : null}
      </div>
      <Rule />
      <div className="space-y-1 tabular-nums">
        {data.items.map((l) => {
          const unit = l.unitPrice - (l.discount ?? 0);
          return (
            <div key={l.id}>
              <p>{l.name}</p>
              <p className="flex justify-between pl-2">
                <span>
                  {l.qty} x {unit.toFixed(2)}
                </span>
                <span>
                  {(unit * l.qty).toFixed(2)}
                  {coded ? <span className="ml-1 font-semibold">{l.taxCode ?? "B"}</span> : null}
                </span>
              </p>
            </div>
          );
        })}
      </div>
      <Rule />
      <div className="space-y-0.5 tabular-nums">
        <Line label="Subtotal" value={t.goods} />
        {data.promotions?.length ? (
          <div className="text-[11px]">
            <p>Promotions (in the prices above):</p>
            {data.promotions.map((p, i) => (
              <p key={i} className="flex justify-between pl-2">
                <span>{p.name}</span>
                <span>-{p.amount.toFixed(2)}</span>
              </p>
            ))}
          </div>
        ) : null}
        {data.discountNote ? <p className="text-[11px]">{data.discountNote}</p> : null}
        {data.deliveryFee ? <Line label="Delivery" value={data.deliveryFee} /> : null}
        {noVat ? null : coded ? (
          coded.byCode.map((r) => <Line key={r.code} label={`${r.code} ${r.label} on ${r.taxable.toFixed(2)}`} value={r.vat} />)
        ) : (
          <Line label="VAT 16%" value={t.vat} />
        )}
        <p className="flex justify-between text-sm font-bold">
          <span>TOTAL KES</span>
          <span>{t.total.toFixed(2)}</span>
        </p>
        {data.payments ? (
          <>
            {data.payments.map((p, i) => (
              <React.Fragment key={i}>
                <Line label={p.method === "Cash" && p.tendered && p.tendered > p.amount ? "Cash tendered" : p.method} value={p.method === "Cash" && p.tendered && p.tendered > p.amount ? p.tendered : p.amount} />
                {p.method === "Cash" && p.tendered && p.tendered > p.amount ? <Line label="Change" value={p.tendered - p.amount} /> : null}
                {p.reference ? (
                  <p className="flex justify-between">
                    <span>{p.method === "M-Pesa" ? "M-Pesa ref" : "Approval code"}</span>
                    <span>{p.reference}</span>
                  </p>
                ) : null}
              </React.Fragment>
            ))}
            {data.credit ? <Line label={data.creditLabel ?? "On account"} value={data.credit} /> : null}
          </>
        ) : (
          <>
            <Line label={data.method} value={data.tendered ?? t.total} />
            {data.tendered && data.tendered > t.total ? <Line label="Change" value={data.tendered - t.total} /> : null}
            {data.mpesaRef ? (
              <p className="flex justify-between">
                <span>M-Pesa ref</span>
                <span>{data.mpesaRef}</span>
              </p>
            ) : null}
          </>
        )}
      </div>
      {data.delivery ? (
        <>
          <Rule />
          <div className="space-y-0.5">
            <p className="font-semibold">Delivery</p>
            {data.delivery.locality ? <p>{data.delivery.locality}</p> : null}
            {data.delivery.address ? <p>{data.delivery.address}</p> : null}
            {data.delivery.pickup ? <p>From: {data.delivery.pickup}</p> : null}
            {data.delivery.rider ? (
              <p>
                Rider: {data.delivery.rider}
                {data.delivery.riderPhone ? `, ${data.delivery.riderPhone}` : ""}
              </p>
            ) : null}
            {data.delivery.courier ? <p>Courier: {data.delivery.courier}</p> : null}
          </div>
        </>
      ) : null}
      <Rule />
      <div className="space-y-1 text-center">
        {data.etimsBlock === false ? null : <EtimsBlock etims={data.etims ?? { state: "queued" }} compact />}
        {data.issuer ? (
          data.issuer.footer ? <p className="pt-1">{data.issuer.footer}</p> : null
        ) : (
          <>
            <p className="pt-1">Thank you for choosing Medora.</p>
            <p className="text-[10px]">Goods once sold are not returnable without this receipt.</p>
          </>
        )}
      </div>
    </div>
  );
});

function Rule() {
  return <div className="my-2 border-t border-dashed border-neutral-400" />;
}
function Line({ label, value }: { label: string; value: number }) {
  return (
    <p className="flex justify-between">
      <span>{label}</span>
      <span>{value.toFixed(2)}</span>
    </p>
  );
}

/* ------------------------------- Held sales list -------------------------------- */

export function HeldSalesList({ sales, customerNames = {}, loading = false }: { sales: Sale[]; customerNames?: Record<number, string>; loading?: boolean }) {
  if (loading) {
    return (
      <div className="space-y-2 p-3" aria-busy>
        {[0, 1].map((i) => (
          <div key={i} className="h-12 animate-pulse rounded-md bg-muted" />
        ))}
      </div>
    );
  }
  if (sales.length === 0) {
    return (
      <div className="flex flex-col items-center gap-1 p-8 text-center">
        <PauseIcon className="size-5 text-muted-foreground" />
        <p className="text-sm font-medium">No held sales</p>
        <p className="text-xs text-muted-foreground">Park a basket from the till and it will wait here.</p>
      </div>
    );
  }
  return (
    <ul>
      {sales.map((s) => (
        <li key={s.id} className="flex items-center gap-3 border-b border-border px-3 py-2 last:border-b-0">
          <div className="min-w-0 flex-1">
            <p className="text-sm font-medium tabular-nums">
              {s.receipt}
              <span className="ml-2 text-xs font-normal text-muted-foreground">
                {s.customerId != null ? (customerNames[s.customerId] ?? "Customer") : "Walk-in"}
              </span>
            </p>
            <p className="text-xs text-muted-foreground tabular-nums">
              {formatDate(s.createdAt, { short: true })}, {s.time}, {s.items} {s.items === 1 ? "item" : "items"}
            </p>
          </div>
          <Money amount={s.total} className="font-medium" />
          <Button size="sm" variant="outline">
            <PlayIcon data-icon="inline-start" />
            Resume
          </Button>
          <Button size="sm" variant="ghost" aria-label={`Discard ${s.receipt}`}>
            <XIcon />
          </Button>
        </li>
      ))}
    </ul>
  );
}
