"use client";

import * as React from "react";
import { BanknoteIcon, CreditCardIcon, LockIcon, NotebookPenIcon, PlusIcon, SmartphoneIcon, Trash2Icon, XIcon } from "lucide-react";

import { Button } from "@/components/ui/button";
import { Field, FieldError, FieldLabel } from "@/components/ui/field";
import { InputGroup, InputGroupAddon, InputGroupInput } from "@/components/ui/input-group";
import { Progress } from "@/components/ui/progress";
import type { PaymentMethod } from "@/lib/mock-data";
import { cn } from "@/lib/utils";

import { Money } from "./money";
import { CardPayment, MpesaPayment, type PaymentConfirmation } from "./payments";
import { TONE_BOX } from "./tones";

export type AppliedPayment = { id: number; method: PaymentMethod; amount: number; change?: number; reference?: string };

export type SplitState = { payments: AppliedPayment[]; paid: number; remaining: number; change: number; complete: boolean };

/** Card 012: what a live page hands the tender panels so M-Pesa and card payments are real. */
export type TenderHandlers = {
  mpesa?: Pick<React.ComponentProps<typeof MpesaPayment>, "onSendPrompt" | "onCheckStatus" | "onVerify" | "stkOnly" | "paybill" | "till">;
  card?: Pick<React.ComponentProps<typeof CardPayment>, "onCharge">;
};

const ICON: Record<PaymentMethod, React.ComponentType<{ className?: string }>> = {
  Cash: BanknoteIcon,
  "M-Pesa": SmartphoneIcon,
  Card: CreditCardIcon,
  Credit: NotebookPenIcon,
};

const r2 = (n: number) => Math.round(n * 100) / 100;

/**
 * Split tender: take one sale across several payment methods, one applied payment at a time.
 * - Each new payment defaults to what is still owed.
 * - Cash is applied immediately; cash above the remainder becomes change, and only the last payment can overpay.
 * - M-Pesa and Card run their own flow for the chosen amount and are added once confirmed (locked after that:
 *   undoing them is a refund, not an edit).
 * - Credit is limited to the customer's headroom and needs a registered customer.
 */
export function SplitTender({
  total,
  accountRef,
  creditHeadroom = 0,
  hasCustomer = false,
  disabled,
  onChange,
  mpesa,
  card,
}: {
  total: number;
  accountRef: string;
  /** Card 012: real M-Pesa and card handlers from a live page; without them the panels' previews run. */
  mpesa?: TenderHandlers["mpesa"];
  card?: TenderHandlers["card"];
  /** Credit limit minus current balance; 0 hides nothing but blocks credit. */
  creditHeadroom?: number;
  /** Credit needs a registered customer on the sale. */
  hasCustomer?: boolean;
  disabled?: boolean;
  onChange?: (s: SplitState) => void;
}) {
  const [payments, setPayments] = React.useState<AppliedPayment[]>([]);
  const [method, setMethod] = React.useState<PaymentMethod>("Cash");
  const [amountText, setAmountText] = React.useState("");
  const [pending, setPending] = React.useState<{ method: "M-Pesa" | "Card"; amount: number } | null>(null);
  const [error, setError] = React.useState("");
  const nextId = React.useRef(1);

  const paid = r2(payments.reduce((s, p) => s + p.amount, 0));
  const remaining = r2(Math.max(0, total - paid));
  const change = r2(payments.reduce((s, p) => s + (p.change ?? 0), 0));
  const complete = remaining === 0 && payments.length > 0;
  const creditUsed = payments.filter((p) => p.method === "Credit").reduce((s, p) => s + p.amount, 0);

  React.useEffect(() => {
    onChange?.({ payments, paid, remaining, change, complete });
  }, [payments, paid, remaining, change, complete, onChange]);

  const amount = amountText === "" ? remaining : Number(amountText);

  const add = (p: Omit<AppliedPayment, "id">) => {
    setPayments((xs) => [...xs, { ...p, id: nextId.current++ }]);
    setAmountText("");
    setError("");
  };

  const apply = () => {
    if (!Number.isFinite(amount) || amount <= 0) return setError("Enter an amount above 0");
    if (method === "Cash") {
      const applied = Math.min(amount, remaining);
      return add({ method: "Cash", amount: r2(applied), change: amount > remaining ? r2(amount - remaining) : undefined });
    }
    if (amount > remaining) return setError(`${method} cannot be more than the KES ${remaining.toFixed(2)} still owed. Only cash gives change.`);
    if (method === "Credit") {
      if (!hasCustomer) return setError("Credit needs a registered customer on the sale.");
      if (creditUsed + amount > creditHeadroom) return setError(`Only KES ${Math.max(0, creditHeadroom - creditUsed).toFixed(2)} of credit is available.`);
      return add({ method: "Credit", amount: r2(amount) });
    }
    setError("");
    setPending({ method, amount: r2(amount) });
  };

  const confirmed = (c: PaymentConfirmation) => {
    if (!pending) return;
    add({ method: pending.method, amount: pending.amount, reference: c.method === "Card" ? `${c.brand} ••${c.last4} · ${c.reference}` : c.reference });
    setPending(null);
  };

  return (
    <div className="space-y-3">
      {/* Progress */}
      <div className="space-y-1.5">
        <div className="flex items-baseline justify-between text-sm">
          <span className="text-muted-foreground">
            Paid <Money amount={paid} className="text-foreground" />
          </span>
          {complete ? (
            <span className="font-medium text-status-success-text">Fully paid</span>
          ) : (
            <span className="text-muted-foreground">
              Remaining <Money amount={remaining} className="font-semibold text-foreground" />
            </span>
          )}
        </div>
        <Progress value={total > 0 ? Math.min(100, (paid / total) * 100) : 0} aria-label="Amount paid" />
      </div>

      {/* Applied payments */}
      {payments.length ? (
        <ul className="divide-y divide-border rounded-lg border border-border" aria-label="Payments applied">
          {payments.map((p) => {
            const Icon = ICON[p.method];
            const locked = p.method === "M-Pesa" || p.method === "Card";
            return (
              <li key={p.id} className="flex items-center gap-2.5 px-3 py-2">
                <Icon className="size-4 shrink-0 text-muted-foreground" />
                <div className="min-w-0 flex-1">
                  <p className="text-sm font-medium">{p.method}</p>
                  {p.reference ? <p className="truncate font-mono text-xs text-muted-foreground">{p.reference}</p> : null}
                  {p.change ? (
                    <p className="text-xs text-status-success-text">
                      Change due <span className="font-semibold tabular-nums">KES {p.change.toFixed(2)}</span>
                    </p>
                  ) : null}
                </div>
                <Money amount={p.amount} className="font-semibold" />
                {locked ? (
                  <span className="flex size-7 items-center justify-center text-muted-foreground" title="Confirmed. Undo by refunding after the sale.">
                    <LockIcon className="size-3.5" aria-label="Confirmed, cannot be removed" />
                  </span>
                ) : (
                  <Button variant="ghost" size="icon-sm" aria-label={`Remove ${p.method} payment of KES ${p.amount.toFixed(2)}`} disabled={disabled} onClick={() => setPayments((xs) => xs.filter((x) => x.id !== p.id))}>
                    <Trash2Icon />
                  </Button>
                )}
              </li>
            );
          })}
        </ul>
      ) : null}

      {/* Electronic payment in progress */}
      {pending ? (
        <div className="space-y-2 rounded-lg border border-border p-3">
          <div className="flex items-center justify-between">
            <p className="text-sm font-medium">
              {pending.method} for <Money amount={pending.amount} className="font-semibold" />
            </p>
            <Button variant="ghost" size="icon-xs" aria-label={`Cancel ${pending.method} payment`} onClick={() => setPending(null)}>
              <XIcon />
            </Button>
          </div>
          {pending.method === "M-Pesa" ? (
            <MpesaPayment amount={pending.amount} accountRef={accountRef} onConfirmed={confirmed} {...(mpesa ?? {})} />
          ) : (
            <CardPayment amount={pending.amount} onConfirmed={confirmed} {...(card ?? {})} />
          )}
        </div>
      ) : null}

      {/* Add a payment */}
      {!complete && !pending ? (
        <div className="space-y-2 rounded-lg border border-dashed border-input p-3">
          <p className="text-xs font-medium text-muted-foreground">{payments.length ? "Add another payment" : "First payment"}</p>
          <div role="radiogroup" aria-label="Method for this payment" className="grid grid-cols-4 gap-1.5">
            {(Object.keys(ICON) as PaymentMethod[]).map((m) => {
              const Icon = ICON[m];
              const on = method === m;
              return (
                <button
                  key={m}
                  type="button"
                  role="radio"
                  aria-checked={on}
                  disabled={disabled}
                  onClick={() => {
                    setMethod(m);
                    setError("");
                  }}
                  className={cn(
                    "flex cursor-pointer flex-col items-center gap-1 rounded-md border px-1 py-1.5 text-xs font-medium outline-none focus-visible:ring-2 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50",
                    on ? "border-primary bg-accent text-accent-foreground" : "border-border hover:bg-muted",
                  )}
                >
                  <Icon className="size-4" />
                  {m}
                </button>
              );
            })}
          </div>
          <Field data-invalid={!!error}>
            <FieldLabel htmlFor="split-amount" className="sr-only">
              Amount
            </FieldLabel>
            <div className="flex gap-2">
              <InputGroup className="flex-1">
                <InputGroupAddon>KES</InputGroupAddon>
                <InputGroupInput
                  id="split-amount"
                  inputMode="decimal"
                  className="tabular-nums"
                  placeholder={remaining.toFixed(2)}
                  value={amountText}
                  disabled={disabled}
                  aria-invalid={!!error}
                  onChange={(e) => {
                    setAmountText(e.target.value.replace(/[^0-9.]/g, ""));
                    setError("");
                  }}
                  onKeyDown={(e) => e.key === "Enter" && apply()}
                />
              </InputGroup>
              <Button variant="secondary" disabled={disabled} onClick={apply}>
                <PlusIcon data-icon="inline-start" />
                {method === "Cash" || method === "Credit" ? "Apply" : "Take payment"}
              </Button>
            </div>
            {error ? <FieldError>{error}</FieldError> : <p className="text-xs text-muted-foreground">Leave blank to pay the remaining KES {remaining.toFixed(2)}.</p>}
          </Field>
        </div>
      ) : null}

      {complete && change > 0 ? (
        <div className={cn("flex items-baseline justify-between rounded-lg border px-3 py-2", TONE_BOX.success)}>
          <span className="text-sm font-medium">Give change</span>
          <Money amount={change} size="lg" />
        </div>
      ) : null}
    </div>
  );
}
