"use client";

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

import { Input } from "@/components/ui/input";
import { NativeSelect, NativeSelectOption } from "@/components/ui/native-select";
import { cn } from "@/lib/utils";

import { Stepper } from "./pos-ops";

const n0 = (n: number) => n.toLocaleString("en-US");
const lines = (n: number) => `${n0(n)} ${n === 1 ? "line" : "lines"}`;
const n2 = (n: number) => n.toLocaleString("en-US", { minimumFractionDigits: 2, maximumFractionDigits: 2 });
const kes = (n: number) => `KES ${n2(n)}`;

/* ---------------------------------- IN-03. Price groups ---------------------------------- */

export type PricedProduct = { id: number; name: string; cost: number; retail: number };

/**
 * Price-group prices (IN-03): one column per group beside the retail price. Each cell says how it compares
 * with retail; a price under cost is flagged, since every sale would lose money. Empty means the group pays
 * retail (less any group discount).
 */
export function PriceGroupGrid({ products, groups, prices, onChange }: { products: PricedProduct[]; groups: string[]; prices: Record<string, string>; onChange: (key: string, v: string) => void }) {
  return (
    <div className="overflow-x-auto rounded-lg border border-[var(--color-border-panel)] bg-card">
      <table className="w-full border-collapse text-sm tabular-nums">
        <caption className="sr-only">Prices per price group</caption>
        <thead className="bg-[var(--color-bg-surface-subtle)] text-left text-xs text-muted-foreground">
          <tr>
            <th scope="col" className="px-3 py-2 font-medium">
              Product
            </th>
            <th scope="col" className="px-3 py-2 text-right font-medium">
              Cost
            </th>
            <th scope="col" className="px-3 py-2 text-right font-medium">
              Retail
            </th>
            {groups.map((g) => (
              <th key={g} scope="col" className="w-36 px-3 py-2 text-right font-medium">
                {g}
              </th>
            ))}
          </tr>
        </thead>
        <tbody className="divide-y divide-[var(--color-border-subtle)]">
          {products.map((p) => (
            <tr key={p.id}>
              <th scope="row" className="px-3 py-1.5 text-left font-medium">
                {p.name}
              </th>
              <td className="px-3 py-1.5 text-right text-muted-foreground">{n2(p.cost)}</td>
              <td className="px-3 py-1.5 text-right">{n2(p.retail)}</td>
              {groups.map((g) => {
                const key = `${p.id}:${g}`;
                const raw = prices[key] ?? "";
                const v = Number(raw.replace(/,/g, ""));
                const set = raw.trim() !== "" && Number.isFinite(v);
                const below = set && v < p.cost;
                const pct = set ? ((v - p.retail) / p.retail) * 100 : 0;
                return (
                  <td key={g} className="px-3 py-1">
                    <Input className={cn("h-8 text-right tabular-nums", below && "border-[var(--color-border-input-error)]")} inputMode="decimal" placeholder="Retail" aria-label={`${g} price, ${p.name}`} aria-invalid={below} value={raw} onChange={(e) => onChange(key, e.target.value)} />
                    {set ? <span className={cn("block text-right text-[11px]", below ? "font-semibold text-status-error-text" : "text-muted-foreground")}>{below ? `Below cost by ${n2(p.cost - v)}` : pct === 0 ? "Same as retail" : `${pct > 0 ? "+" : "−"}${Math.abs(pct).toFixed(1)}% vs retail`}</span> : null}
                  </td>
                );
              })}
            </tr>
          ))}
        </tbody>
      </table>
    </div>
  );
}

/* ---------------------------------- IN-07. Merge products ---------------------------------- */

export type MergeProduct = { id: number; name: string; barcode: string; stock: number; batches: number; sales: number; price: number; taxCode: string };

/**
 * Merge two duplicate products (IN-07): the kept product absorbs the other's batches, sales history and
 * barcode (kept as a second barcode, so old labels still scan). Where they differ (price, tax code), the
 * kept product's value wins unless chosen otherwise; the merged-away product is listed in the merge report.
 */
/** `barcodeAfter` (Stage 7, plan B Q1): what the kept product scans as afterwards, when that isn't both barcodes. */
export function MergePreview({ keep, merge, take, onTake, barcodeAfter }: { keep: MergeProduct; merge: MergeProduct; take: Record<"price" | "taxCode", "keep" | "merge">; onTake: (k: "price" | "taxCode", v: "keep" | "merge") => void; barcodeAfter?: string }) {
  const row = (label: string, a: React.ReactNode, b: React.ReactNode, after: React.ReactNode, choose?: "price" | "taxCode") => (
    <tr>
      <th scope="row" className="px-3 py-2 text-left font-normal text-muted-foreground">
        {label}
      </th>
      <td className="px-3 py-2">{a}</td>
      <td className="px-3 py-2">{b}</td>
      <td className="px-3 py-2 font-semibold">
        {choose ? (
          <NativeSelect size="sm" aria-label={`${label} after merging`} value={take[choose]} onChange={(e) => onTake(choose, e.target.value as "keep" | "merge")}>
            <NativeSelectOption value="keep">{choose === "price" ? n2(keep.price) : keep.taxCode} (kept product)</NativeSelectOption>
            <NativeSelectOption value="merge">{choose === "price" ? n2(merge.price) : merge.taxCode} (merged product)</NativeSelectOption>
          </NativeSelect>
        ) : (
          after
        )}
      </td>
    </tr>
  );
  return (
    <div className="overflow-x-auto rounded-lg border border-[var(--color-border-panel)] bg-card">
      <table className="w-full border-collapse text-sm tabular-nums">
        <caption className="sr-only">What the merge does</caption>
        <thead className="bg-[var(--color-bg-surface-subtle)] text-left text-xs text-muted-foreground">
          <tr>
            <th scope="col" className="px-3 py-2" />
            <th scope="col" className="px-3 py-2 font-medium">
              Keep: {keep.name}
            </th>
            <th scope="col" className="px-3 py-2 font-medium">
              Merge away: {merge.name}
            </th>
            <th scope="col" className="px-3 py-2 font-medium">
              <span className="inline-flex items-center gap-1">
                <ArrowRightIcon className="size-3.5" aria-hidden /> After
              </span>
            </th>
          </tr>
        </thead>
        <tbody className="divide-y divide-[var(--color-border-subtle)]">
          {row("Stock", n0(keep.stock), n0(merge.stock), n0(keep.stock + merge.stock))}
          {row("Batches", keep.batches, merge.batches, `${keep.batches + merge.batches}, each keeping its expiry`)}
          {row("Sales history", lines(keep.sales), lines(merge.sales), lines(keep.sales + merge.sales))}
          {row("Barcodes", <span className="font-mono text-xs">{keep.barcode}</span>, <span className="font-mono text-xs">{merge.barcode}</span>, <span className="font-mono text-xs font-normal">{barcodeAfter ?? `${keep.barcode} + ${merge.barcode}`}</span>)}
          {row("Selling price", n2(keep.price), n2(merge.price), null, keep.price !== merge.price ? "price" : undefined)}
          {row("Tax code", keep.taxCode, merge.taxCode, null, keep.taxCode !== merge.taxCode ? "taxCode" : undefined)}
        </tbody>
      </table>
    </div>
  );
}

/* --------------------------------- IN-18. Batch correction --------------------------------- */

/** Problems and consequences of correcting a batch's number or expiry after receiving (IN-18). */
export function batchCorrectionCheck(d: { batch: string; expiry: string; reason: string }, before: { batch: string; expiry: string }, today: string) {
  const problems: string[] = [];
  if (!d.batch.trim()) problems.push("Enter the batch number.");
  if (!d.expiry) problems.push("Enter the expiry date.");
  if (d.batch.trim() === before.batch && d.expiry === before.expiry) problems.push("Nothing has changed.");
  if (!d.reason.trim()) problems.push("Say why it's being corrected, e.g. typed wrong at receiving.");
  const warnings: string[] = [];
  if (d.expiry && d.expiry <= today) warnings.push("The new expiry is today or earlier: the batch stops selling and moves to the expired list.");
  else if (d.expiry && d.expiry < before.expiry) warnings.push("The batch now expires sooner, so it's sold before later batches (FEFO).");
  return { problems, warnings };
}

/* --------------------------------- SU-06. Supplier return --------------------------------- */

export type ReturnableBatch = { id: string; name: string; batch: string; expiry: string; onHand: number; unitCost: number; grn: string };

/**
 * Return to supplier (SU-06): from batches bought from that supplier, no more than is on hand; each line has
 * a reason; the credit expected is quantity × the batch's cost, which goes to Accounts as a credit note owed.
 */
export function SupplierReturnLines({ batches, qty, reasons, reason, onQty, onReason }: { batches: ReturnableBatch[]; qty: Record<string, number>; reasons: string[]; reason: Record<string, string>; onQty: (id: string, n: number) => void; onReason: (id: string, r: string) => void }) {
  const credit = batches.reduce((s, b) => s + (qty[b.id] ?? 0) * b.unitCost, 0);
  return (
    <div className="overflow-x-auto rounded-lg border border-[var(--color-border-panel)] bg-card">
      <table className="w-full border-collapse text-sm tabular-nums">
        <caption className="sr-only">Batches to return</caption>
        <thead className="bg-[var(--color-bg-surface-subtle)] text-left text-xs text-muted-foreground">
          <tr>
            {["Item and batch", "On hand", "Return", "Reason", "Credit"].map((h) => (
              <th key={h} scope="col" className={cn("px-3 py-2 font-medium", (h === "On hand" || h === "Credit") && "text-right")}>
                {h}
              </th>
            ))}
          </tr>
        </thead>
        <tbody className="divide-y divide-[var(--color-border-subtle)]">
          {batches.map((b) => {
            const q = qty[b.id] ?? 0;
            return (
              <tr key={b.id}>
                <th scope="row" className="px-3 py-2 text-left font-medium">
                  {b.name}
                  <span className="block text-xs font-normal text-muted-foreground">
                    <span className="font-mono">{b.batch}</span> · exp {b.expiry} · from {b.grn}
                  </span>
                </th>
                <td className="px-3 py-2 text-right">{n0(b.onHand)}</td>
                <td className="px-3 py-2">
                  <Stepper value={q} max={b.onHand} label={`${b.name} ${b.batch} to return`} onChange={(n) => onQty(b.id, n)} />
                </td>
                <td className="px-3 py-2">
                  {q > 0 ? (
                    <NativeSelect size="sm" aria-label={`Reason, ${b.name}`} value={reason[b.id] ?? ""} onChange={(e) => onReason(b.id, e.target.value)} aria-invalid={!reason[b.id]}>
                      <NativeSelectOption value="">Choose…</NativeSelectOption>
                      {reasons.map((r) => (
                        <NativeSelectOption key={r}>{r}</NativeSelectOption>
                      ))}
                    </NativeSelect>
                  ) : null}
                </td>
                <td className="px-3 py-2 text-right">{q ? n2(q * b.unitCost) : ""}</td>
              </tr>
            );
          })}
        </tbody>
        <tfoot className="border-t-2 border-[var(--color-border-panel)] font-semibold">
          <tr>
            <th scope="row" colSpan={4} className="px-3 py-2 text-left">
              Credit expected from the supplier
            </th>
            <td className="px-3 py-2 text-right">{kes(credit)}</td>
          </tr>
        </tfoot>
      </table>
    </div>
  );
}

/* ------------------------------- SU-04. Supplier suggestion ------------------------------- */

export type SupplierQuote = { supplier: string; cost: number; lastReceived: string; usual?: boolean; stale?: boolean };

/**
 * Supplier choice on a PO line (SU-04): suggests the cheapest current price, and says when the usual
 * supplier differs. Stale prices (over 90 days) are labelled and never suggested.
 */
export function SupplierChoice({ product, quotes, value, onChange }: { product: string; quotes: SupplierQuote[]; value: string; onChange: (s: string) => void }) {
  const current = quotes.filter((q) => !q.stale);
  const cheapest = (current.length ? current : quotes).reduce((a, b) => (b.cost < a.cost ? b : a));
  const usual = quotes.find((q) => q.usual);
  const chosen = quotes.find((q) => q.supplier === value);
  return (
    <div className="space-y-1">
      <NativeSelect size="sm" aria-label={`Supplier for ${product}`} value={value} onChange={(e) => onChange(e.target.value)}>
        {[...quotes]
          .sort((a, b) => a.cost - b.cost)
          .map((q) => (
            <NativeSelectOption key={q.supplier} value={q.supplier}>
              {q.supplier} · {n2(q.cost)}
              {q === cheapest ? " · cheapest" : ""}
              {q.usual ? " · usual" : ""}
              {q.stale ? " · price may be out of date" : ""}
            </NativeSelectOption>
          ))}
      </NativeSelect>
      {chosen && chosen !== cheapest ? (
        <p className="text-[11px] text-status-warning-text">
          {cheapest.supplier} is {n2(chosen.cost - cheapest.cost)} cheaper per unit.
        </p>
      ) : usual && usual !== cheapest && chosen === cheapest ? (
        <p className="text-[11px] text-muted-foreground">Usually from {usual.supplier}.</p>
      ) : null}
    </div>
  );
}

/* --------------------------------- RP-03. Branch totals --------------------------------- */

export type BranchTotal = { branch: string; items: number; units: number; cost: number; selling: number };

/** Branch inventory totals (RP-03): value at cost and at selling price with margin %, and a grand total. */
export function BranchTotals({ rows }: { rows: BranchTotal[] }) {
  const tot = rows.reduce((s, r) => ({ items: s.items + r.items, units: s.units + r.units, cost: s.cost + r.cost, selling: s.selling + r.selling }), { items: 0, units: 0, cost: 0, selling: 0 });
  const margin = (r: { cost: number; selling: number }) => (r.selling ? ((r.selling - r.cost) / r.selling) * 100 : 0);
  return (
    <div className="overflow-x-auto rounded-lg border border-[var(--color-border-panel)] bg-card">
      <table className="w-full border-collapse text-sm tabular-nums">
        <caption className="sr-only">Stock value by branch</caption>
        <thead className="bg-[var(--color-bg-surface-subtle)] text-left text-xs text-muted-foreground">
          <tr>
            {["Branch", "Items", "Units", "At cost", "At selling price", "Margin"].map((h) => (
              <th key={h} scope="col" className={cn("px-3 py-2 font-medium", h !== "Branch" && "text-right")}>
                {h}
              </th>
            ))}
          </tr>
        </thead>
        <tbody className="divide-y divide-[var(--color-border-subtle)]">
          {rows.map((r) => (
            <tr key={r.branch}>
              <th scope="row" className="px-3 py-2 text-left font-medium">
                {r.branch}
              </th>
              <td className="px-3 py-2 text-right">{n0(r.items)}</td>
              <td className="px-3 py-2 text-right">{n0(r.units)}</td>
              <td className="px-3 py-2 text-right">{n2(r.cost)}</td>
              <td className="px-3 py-2 text-right">{n2(r.selling)}</td>
              <td className="px-3 py-2 text-right">{margin(r).toFixed(1)}%</td>
            </tr>
          ))}
        </tbody>
        <tfoot className="border-t-2 border-[var(--color-border-panel)] font-semibold">
          <tr>
            <th scope="row" className="px-3 py-2 text-left">
              All branches
            </th>
            <td className="px-3 py-2 text-right">{n0(tot.items)}</td>
            <td className="px-3 py-2 text-right">{n0(tot.units)}</td>
            <td className="px-3 py-2 text-right">{n2(tot.cost)}</td>
            <td className="px-3 py-2 text-right">{n2(tot.selling)}</td>
            <td className="px-3 py-2 text-right">{margin(tot).toFixed(1)}%</td>
          </tr>
        </tfoot>
      </table>
    </div>
  );
}

/* --------------------------------- CU-01. Customer form --------------------------------- */

export type CustomerDraft = { kind: "Individual" | "Company"; name: string; contact: string; phone: string; email: string; kraPin: string; etimsId: string; vat: "Vatable" | "Exempt"; group: string; city: string; address: string; postal: string; country: string; bankName: string; bankAccount: string };

/** KRA PIN: A (individual) or P (company/non-individual), 9 digits, a letter. */
export const kraPinOk = (pin: string) => /^[AP]\d{9}[A-Z]$/.test(pin);

export function customerProblems(d: CustomerDraft, existingPhones: string[]): Partial<Record<keyof CustomerDraft, string>> {
  const p: Partial<Record<keyof CustomerDraft, string>> = {};
  if (!d.name.trim()) p.name = d.kind === "Company" ? "Enter the company name." : "Enter the customer's name.";
  if (!/^(\+?254|0)[17]\d{8}$/.test(d.phone.replace(/\s/g, ""))) p.phone = "Enter a mobile number, e.g. 0712 345 678.";
  else if (existingPhones.includes(d.phone.replace(/\D/g, "").replace(/^254/, "0"))) p.phone = "Another customer has this number.";
  if (d.email && !/^[^@\s]+@[^@\s]+\.[^@\s]+$/.test(d.email)) p.email = "This isn't an email address.";
  if (d.kraPin) {
    if (!kraPinOk(d.kraPin)) p.kraPin = "A KRA PIN is A or P, 9 digits and a letter, e.g. P051924221B.";
    else if (d.kind === "Company" && d.kraPin[0] !== "P") p.kraPin = "A company's PIN starts with P.";
  } else if (d.kind === "Company") p.kraPin = "A company needs its KRA PIN for tax invoices.";
  if (d.kind === "Company" && !d.contact.trim()) p.contact = "Who do we deal with there?";
  if (d.vat === "Exempt" && !d.kraPin) p.vat = "VAT-exempt customers need a KRA PIN on record.";
  if (d.bankAccount && !/^\d{6,20}$/.test(d.bankAccount.replace(/\s/g, ""))) p.bankAccount = "Account numbers are 6 to 20 digits.";
  else if (d.bankAccount && !d.bankName.trim()) p.bankName = "Which bank is the account with?";
  return p;
}

/* -------------------------------- CU-04. Customer groups -------------------------------- */

export type CustomerGroup = { name: string; priceGroup: string; discountPct: number; members: number };

export function groupProblem(g: CustomerGroup, groups: CustomerGroup[], editing?: string) {
  if (!g.name.trim()) return "Enter a name.";
  if (groups.some((x) => x.name.toLowerCase() === g.name.trim().toLowerCase() && x.name !== editing)) return `There's already a group called ${g.name.trim()}.`;
  if (g.discountPct < 0 || g.discountPct > 50) return "A default discount is between 0% and 50%; bigger discounts need approval at the till.";
  return undefined;
}

/** A deleted product (IN-08): who deleted it and when; restoring checks the barcode isn't now used elsewhere. */
export type DeletedProduct = { id: number; name: string; barcode: string; deletedBy: string; deletedAt: string; reason?: string };

export function restoreBlock(p: DeletedProduct, activeBarcodes: string[]) {
  return activeBarcodes.includes(p.barcode) ? `Barcode ${p.barcode} now belongs to another product. Change that product's barcode first, or merge the two.` : undefined;
}


/**
 * Customer form (CU-01): an individual (patient) or a company. A company needs a KRA PIN starting with P and a
 * contact person; VAT-exempt customers need a PIN on record. The group sets price group and discount.
 */
export function CustomerForm({ d, onChange, groups, problems }: { d: CustomerDraft; onChange: (d: CustomerDraft) => void; groups: string[]; problems: Partial<Record<keyof CustomerDraft, string>> }) {
  const set = <K extends keyof CustomerDraft>(k: K, v: CustomerDraft[K]) => onChange({ ...d, [k]: v });
  const field = (k: keyof CustomerDraft, label: string, node: React.ReactNode, hint?: string, wide = false) => (
    <div className={cn("space-y-1", wide && "sm:col-span-2")} data-invalid={!!problems[k] || undefined}>
      <label htmlFor={`cf-${k}`} className="text-sm font-medium">
        {label}
      </label>
      {node}
      {problems[k] ? <p className="text-xs text-status-error-text">{problems[k]}</p> : hint ? <p className="text-xs text-muted-foreground">{hint}</p> : null}
    </div>
  );
  const input = (k: keyof CustomerDraft, extra?: React.ComponentProps<typeof Input>) => <Input id={`cf-${k}`} value={String(d[k])} onChange={(e) => set(k, e.target.value as never)} aria-invalid={!!problems[k]} {...extra} />;
  return (
    <div className="space-y-4">
      <div role="radiogroup" aria-label="Customer type" className="inline-flex overflow-hidden rounded-md border border-[var(--color-border-panel)] text-sm bg-card">
        {(["Individual", "Company"] as const).map((k) => (
          <button key={k} type="button" role="radio" aria-checked={d.kind === k} onClick={() => set("kind", k)} className={cn("cursor-pointer px-3 py-1.5", d.kind === k ? "bg-[var(--color-bg-selected)] font-medium" : "text-muted-foreground hover:bg-muted")}>
            {k === "Individual" ? "Individual or patient" : "Company"}
          </button>
        ))}
      </div>
      <div className="grid gap-4 sm:grid-cols-2">
        {field("name", d.kind === "Company" ? "Company name" : "Full name", input("name"))}
        {d.kind === "Company" ? field("contact", "Contact person", input("contact")) : null}
        {field("phone", "Mobile number", input("phone", { inputMode: "tel" }))}
        {field("email", "Email (optional)", input("email", { type: "email" }))}
        {field("kraPin", d.kind === "Company" ? "KRA PIN" : "KRA PIN (optional)", input("kraPin", { className: "font-mono uppercase", onChange: (e) => set("kraPin", e.target.value.toUpperCase().replace(/\s/g, "")) }), "Printed on tax invoices.")}
        {field("etimsId", "eTIMS ID (optional)", input("etimsId", { className: "font-mono" }), "If the customer is registered on eTIMS themselves.")}
        {field(
          "vat",
          "VAT status",
          <NativeSelect id="cf-vat" value={d.vat} onChange={(e) => set("vat", e.target.value as CustomerDraft["vat"])} aria-invalid={!!problems.vat}>
            <NativeSelectOption>Vatable</NativeSelectOption>
            <NativeSelectOption>Exempt</NativeSelectOption>
          </NativeSelect>,
          d.vat === "Exempt" ? "Their sales carry no VAT; keep the exemption letter on file." : undefined,
        )}
        {field(
          "group",
          "Customer group",
          <NativeSelect id="cf-group" value={d.group} onChange={(e) => set("group", e.target.value)}>
            <NativeSelectOption value="">None (retail prices)</NativeSelectOption>
            {groups.map((g) => (
              <NativeSelectOption key={g}>{g}</NativeSelectOption>
            ))}
          </NativeSelect>,
        )}
        {field("city", "Town or city", input("city"))}
        {field("address", "Physical address", input("address"))}
        {field("postal", "Postal address (optional)", input("postal"), "e.g. PO Box 1220-00502.")}
        {field("country", "Country", input("country"))}
        {field("bankName", "Bank (optional)", input("bankName"), "For refunds and credit balances paid back by transfer.")}
        {field("bankAccount", "Account number (optional)", input("bankAccount", { inputMode: "numeric", className: "font-mono" }))}
      </div>
    </div>
  );
}
