"use client";

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

import { Button } from "@/components/ui/button";
import { Checkbox } from "@/components/ui/checkbox";
import { Input } from "@/components/ui/input";
import { NativeSelect, NativeSelectOption } from "@/components/ui/native-select";
import { Switch } from "@/components/ui/switch";
import { cn } from "@/lib/utils";

import { AsyncBadge } from "./async-status";
import { kraPinOk } from "./catalog-ops";
import { downloadText } from "./file-upload";
import { count } from "@/lib/format";

const n2 = (n: number) => n.toLocaleString("en-US", { minimumFractionDigits: 2, maximumFractionDigits: 2 });

/* -------------------------------- ST-01/02. Branch and money -------------------------------- */

export type BranchDraft = { name: string; address: string; postal: string; town: string; kraPin: string; phone: string; email: string; till: string };
export type MoneyLink = { role: "Cash drawer" | "M-Pesa till" | "Card terminal" | "Bank"; account: string };

export function branchProblems(d: BranchDraft, names: string[], editing?: string): Partial<Record<keyof BranchDraft, string>> {
  const p: Partial<Record<keyof BranchDraft, string>> = {};
  if (!d.name.trim()) p.name = "Enter the branch name.";
  else if (names.some((n) => n.toLowerCase() === d.name.trim().toLowerCase() && n !== editing)) p.name = "Another branch has this name.";
  if (!d.town.trim()) p.town = "Enter the town.";
  if (!kraPinOk(d.kraPin) || d.kraPin[0] !== "P") p.kraPin = "The pharmacy's KRA PIN: P, 9 digits and a letter. It's printed on every receipt.";
  if (!/^(\+?254|0)\d{9}$/.test(d.phone.replace(/\s/g, ""))) p.phone = "Enter the branch phone, e.g. 0712 345 678.";
  if (d.email && !/^[^@\s]+@[^@\s]+\.[^@\s]+$/.test(d.email)) p.email = "This isn't an email address.";
  if (d.till && !/^\d{5,7}$/.test(d.till)) p.till = "An M-Pesa till or paybill number has 5 to 7 digits.";
  return p;
}

/**
 * A branch's money accounts (ST-02): which ledger accounts its cash drawer, M-Pesa till, card terminals and
 * bank are. Every branch needs a cash drawer; an account can't belong to two branches, or their takings
 * would mix.
 */
export function moneyLinkProblems(links: MoneyLink[], takenElsewhere: Record<string, string>) {
  const p: string[] = [];
  if (!links.some((l) => l.role === "Cash drawer" && l.account)) p.push("Link a cash drawer: the till can't take cash without one.");
  for (const l of links) if (l.account && takenElsewhere[l.account]) p.push(`${l.account} is already ${takenElsewhere[l.account]}'s ${l.role.replace(/^\w/, (c) => c.toLowerCase())}.`);
  const used = links.map((l) => l.account).filter(Boolean);
  if (new Set(used).size !== used.length) p.push("The same account is linked twice.");
  return p;
}

export function MoneyLinks({ links, accounts, onChange }: { links: MoneyLink[]; accounts: { code: string; name: string }[]; onChange: (l: MoneyLink[]) => void }) {
  return (
    <div className="space-y-2">
      {links.map((l, i) => (
        <div key={i} className="grid grid-cols-[9rem_1fr_auto] items-center gap-2 text-sm">
          <span className="text-muted-foreground">{l.role}</span>
          <NativeSelect size="sm" aria-label={`${l.role} account`} value={l.account} onChange={(e) => onChange(links.map((x, j) => (j === i ? { ...x, account: e.target.value } : x)))}>
            <NativeSelectOption value="">Choose a ledger account</NativeSelectOption>
            {accounts.map((a) => (
              <NativeSelectOption key={a.code} value={`${a.code} ${a.name}`}>
                {a.code} {a.name}
              </NativeSelectOption>
            ))}
          </NativeSelect>
          <button type="button" className="cursor-pointer text-xs text-muted-foreground hover:text-foreground" aria-label={`Remove ${l.role}`} onClick={() => onChange(links.filter((_, j) => j !== i))}>
            Remove
          </button>
        </div>
      ))}
      <div className="flex flex-wrap gap-2">
        {(["Cash drawer", "Card terminal", "Bank"] as const).map((r) => (
          <button key={r} type="button" className="cursor-pointer rounded-md border border-dashed border-[var(--color-border-panel)] px-2 py-1 text-xs hover:border-[var(--color-border-strong)]" onClick={() => onChange([...links, { role: r, account: "" }])}>
            + {r}
          </button>
        ))}
      </div>
    </div>
  );
}

/* ---------------------------- ST-04/05/06. Locations, shelves, zones ---------------------------- */

/** `details`: free notes staff need to find or use the place (where it is, who holds the key, opening hours). */
export type PlaceLocation = { id: string; name: string; kind: "Physical" | "Virtual"; inPos: boolean; units: number; details?: string };

/** Why a location can't be removed or hidden from the till, or undefined. */
export function locationBlock(l: PlaceLocation, all: PlaceLocation[], action: "delete" | "hide") {
  if (action === "delete" && l.units > 0) return `${l.name} holds ${l.units.toLocaleString("en-US")} units. Transfer or adjust them out first.`;
  if (action === "hide" && all.filter((x) => x.inPos).length === 1 && l.inPos) return "At least one location must sell at the till.";
  return undefined;
}

/** Locations of a branch (ST-04): physical or virtual (e.g. "Online orders"), and which ones the till sells from. */
export function LocationsEditor({ locations, onChange, onBlocked }: { locations: PlaceLocation[]; onChange: (l: PlaceLocation[]) => void; onBlocked: (msg: string) => void }) {
  return (
    <ul className="divide-y divide-[var(--color-border-subtle)] rounded-lg border border-[var(--color-border-panel)] text-sm bg-card">
      {locations.map((l) => (
        <li key={l.id} className="flex flex-wrap items-center gap-3 px-3 py-2">
          <span className="min-w-0 flex-1">
            <span className="font-medium">{l.name}</span>
            <span className="block text-xs text-muted-foreground tabular-nums">
              {l.kind} · {l.units.toLocaleString("en-US")} units
            </span>
            <Input aria-label={`${l.name} details`} placeholder="Details, e.g. back room, key with the branch admin" className="mt-1 h-8 text-xs" value={l.details ?? ""} onChange={(e) => onChange(locations.map((x) => (x.id === l.id ? { ...x, details: e.target.value } : x)))} />
          </span>
          <label className="flex items-center gap-1.5 text-xs">
            <Switch
              checked={l.inPos}
              onCheckedChange={(v) => {
                const b = !v ? locationBlock(l, locations, "hide") : undefined;
                if (b) return onBlocked(b);
                onChange(locations.map((x) => (x.id === l.id ? { ...x, inPos: v } : x)));
              }}
            />
            Sells at the till
          </label>
          <button
            type="button"
            className="cursor-pointer text-xs text-status-error-text"
            onClick={() => {
              const b = locationBlock(l, locations, "delete");
              if (b) return onBlocked(b);
              onChange(locations.filter((x) => x.id !== l.id));
            }}
          >
            Delete
          </button>
        </li>
      ))}
    </ul>
  );
}

export type Shelf = { code: string; description: string; products: number };

/** Shelves and racks (ST-05): search, paging, products per shelf; a shelf with products can't be deleted. */
/** `canExport`: off for a role without Export on the page showing it. */
export function ShelvesList({ shelves, pageSize = 5, canExport = true }: { shelves: Shelf[]; pageSize?: number; canExport?: boolean }) {
  const [q, setQ] = React.useState("");
  const [page, setPage] = React.useState(0);
  const shown = shelves.filter((s) => !q.trim() || `${s.code} ${s.description}`.toLowerCase().includes(q.trim().toLowerCase()));
  const pages = Math.max(1, Math.ceil(shown.length / pageSize));
  const slice = shown.slice(page * pageSize, page * pageSize + pageSize);
  return (
    <div className="space-y-2">
      <div className="flex flex-wrap items-center gap-2">
        <Input
          value={q}
          onChange={(e) => {
            setQ(e.target.value);
            setPage(0);
          }}
          placeholder="Find a shelf"
          aria-label="Find a shelf"
          className="max-w-56"
        />
        {/* ST-05: exports what the search shows, every page of it, not just the page on screen. */}
        {canExport ? (
          <Button size="sm" variant="outline" disabled={!shown.length} onClick={() => downloadText("shelves.csv", ["Shelf,Description,Products", ...shown.map((s) => `${s.code},"${s.description.replace(/"/g, '""')}",${s.products}`)].join("\n"))}>
            Export {shown.length === shelves.length ? "all" : shown.length}
          </Button>
        ) : null}
      </div>
      <ul className="divide-y divide-[var(--color-border-subtle)] rounded-lg border border-[var(--color-border-panel)] text-sm bg-card">
        {slice.map((s) => (
          <li key={s.code} className="flex items-center justify-between gap-2 px-3 py-1.5">
            <span>
              <span className="font-mono font-medium">{s.code}</span> <span className="text-muted-foreground">{s.description}</span>
            </span>
            <span className="text-xs text-muted-foreground tabular-nums">{count(s.products, "product")}</span>
          </li>
        ))}
        {!slice.length ? <li className="px-3 py-4 text-center text-muted-foreground">No shelves match.</li> : null}
      </ul>
      <div className="flex items-center justify-between text-xs text-muted-foreground tabular-nums">
        <span>
          {shown.length} shelves · page {page + 1} of {pages}
        </span>
        <span className="flex gap-2">
          <button type="button" className="cursor-pointer disabled:cursor-default disabled:opacity-40" disabled={page === 0} onClick={() => setPage((p) => p - 1)}>
            Previous
          </button>
          <button type="button" className="cursor-pointer disabled:cursor-default disabled:opacity-40" disabled={page >= pages - 1} onClick={() => setPage((p) => p + 1)}>
            Next
          </button>
        </span>
      </div>
    </div>
  );
}

export type Zone = { name: string; fee: string };

export function zoneProblems(zones: Zone[]) {
  const p: string[] = [];
  const names = zones.map((z) => z.name.trim().toLowerCase()).filter(Boolean);
  if (zones.some((z) => !z.name.trim())) p.push("Every zone needs a name.");
  if (new Set(names).size !== names.length) p.push("Two zones have the same name.");
  if (zones.some((z) => !/^\d+(\.\d{1,2})?$/.test(z.fee))) p.push("Fees are amounts in shillings, 0 or more.");
  return p;
}

/* ------------------------------- IG-06. Unmatched M-Pesa payments ------------------------------- */

export type C2BPayment = { code: string; at: string; amount: number; phone: string; payer: string; account: string; suggestion?: { kind: "sale" | "customer"; ref: string; label: string } };

/** A paybill/till payment nobody's matched yet (IG-06): who paid, the account text they typed, and a suggestion. */
export function C2BRow({ p, onMatch, onSkip }: { p: C2BPayment; onMatch: (ref: string) => void; onSkip: () => void }) {
  return (
    <li className="flex flex-wrap items-start gap-3 px-3 py-2 text-sm">
      <span className="min-w-0 flex-1">
        <span className="font-semibold tabular-nums">KES {n2(p.amount)}</span> <span className="font-mono text-xs">{p.code}</span>
        <span className="block text-xs text-muted-foreground">
          {p.at} · {p.payer} · {p.phone} · account typed: &ldquo;{p.account || "(blank)"}&rdquo;
        </span>
        {p.suggestion ? <span className="mt-0.5 block text-xs">Looks like {p.suggestion.label}</span> : <span className="mt-0.5 block text-xs text-status-warning-text">No likely match: search for the sale or customer.</span>}
      </span>
      {p.suggestion ? (
        <button type="button" className="inline-flex h-7 cursor-pointer items-center gap-1 rounded-md border border-[var(--color-border-panel)] px-2 text-xs font-medium hover:bg-muted" onClick={() => onMatch(p.suggestion!.ref)}>
          <CheckIcon className="size-3.5" aria-hidden /> Match to {p.suggestion.ref}
        </button>
      ) : null}
      <button type="button" className="h-7 cursor-pointer px-1 text-xs text-muted-foreground hover:text-foreground" onClick={onSkip}>
        Not ours (refund)
      </button>
    </li>
  );
}

/* ---------------------------------- IG-10. eTIMS queue ---------------------------------- */

export type EtimsQueueItem = { ref: string; kind: "Sale" | "Credit note" | "Purchase" | "Stock movement"; at: string; amount: number; status: "Queued" | "Rejected"; attempts: number; reason?: string; fix?: string };

/** Unsent and rejected eTIMS items (IG-10, IG-11): sales, credit notes, purchases and stock movements. Queued ones resend themselves; rejected ones say what to fix. */
export function EtimsQueue({ items, onResend }: { items: EtimsQueueItem[]; onResend: (ref: 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">
        <caption className="sr-only">eTIMS items not yet signed</caption>
        <thead className="bg-[var(--color-bg-surface-subtle)] text-left text-xs text-muted-foreground">
          <tr>
            {["Document", "Amount", "Status", ""].map((h, i) => (
              <th key={i} scope="col" className={cn("px-3 py-2 font-medium", h === "Amount" && "text-right")}>
                {h || <span className="sr-only">Actions</span>}
              </th>
            ))}
          </tr>
        </thead>
        <tbody className="divide-y divide-[var(--color-border-subtle)]">
          {items.map((i) => (
            <tr key={i.ref}>
              <th scope="row" className="px-3 py-2 text-left font-medium">
                <span className="font-mono">{i.ref}</span>
                <span className="block text-xs font-normal text-muted-foreground">
                  {i.kind} · {i.at}
                </span>
              </th>
              <td className="px-3 py-2 text-right tabular-nums">{n2(i.amount)}</td>
              <td className="px-3 py-2">
                <AsyncBadge state={i.status === "Queued" ? "queued" : "attention"} severe={i.status === "Rejected"} label={i.status === "Queued" ? `Queued, ${i.attempts} ${i.attempts === 1 ? "try" : "tries"}` : "Rejected by KRA"} />
                {i.reason ? <span className="mt-0.5 block text-xs text-muted-foreground">{i.reason}</span> : null}
                {i.fix ? <span className="block text-xs font-medium">{i.fix}</span> : null}
              </td>
              <td className="px-2 py-2 text-right">
                <button type="button" className="cursor-pointer text-xs font-medium text-primary underline-offset-2 hover:underline" onClick={() => onResend(i.ref)}>
                  {i.status === "Queued" ? "Send now" : "Fix and resend"}
                </button>
              </td>
            </tr>
          ))}
          {!items.length ? (
            <tr>
              <td colSpan={4} className="px-3 py-6 text-center text-sm text-muted-foreground">
                Everything is signed by KRA.
              </td>
            </tr>
          ) : null}
        </tbody>
      </table>
    </div>
  );
}

/* --------------------------------- IG-12. Online shop sync --------------------------------- */

export type OnlineProduct = { id: number; name: string; online: boolean; price: number; onlinePrice?: number; available: number; buffer: number; lastSync?: string; error?: string; rx?: boolean };

/**
 * Online shop products (IG-12): which products are online, at what price, and the stock the shop sees: what's
 * available less a safety buffer, so the last units aren't sold twice. Prescription medicines show online
 * but need an uploaded prescription to order.
 */
export function OnlineProducts({ rows, onChange }: { rows: OnlineProduct[]; onChange: (id: number, patch: Partial<OnlineProduct>) => 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">Products on the online shop</caption>
        <thead className="bg-[var(--color-bg-surface-subtle)] text-left text-xs text-muted-foreground">
          <tr>
            {["Online", "Product", "Price online", "Shop sees", "Sync"].map((h) => (
              <th key={h} scope="col" className={cn("px-3 py-2 font-medium", (h === "Price online" || h === "Shop sees") && "text-right")}>
                {h}
              </th>
            ))}
          </tr>
        </thead>
        <tbody className="divide-y divide-[var(--color-border-subtle)]">
          {rows.map((r) => {
            const shown = Math.max(0, r.available - r.buffer);
            return (
              <tr key={r.id} className={cn(!r.online && "text-muted-foreground")}>
                <td className="px-3 py-2">
                  <Checkbox aria-label={`${r.name} online`} checked={r.online} onCheckedChange={(v) => onChange(r.id, { online: !!v })} />
                </td>
                <th scope="row" className="px-3 py-2 text-left font-medium">
                  {r.name}
                  {r.rx ? <span className="block text-xs font-normal text-muted-foreground">Prescription needed to order</span> : null}
                </th>
                <td className="px-3 py-2 text-right">{n2(r.onlinePrice ?? r.price)}</td>
                <td className="px-3 py-2 text-right">
                  {shown}
                  <span className="block text-[11px] text-muted-foreground">
                    {r.available} less {r.buffer} buffer
                  </span>
                  {r.online && shown === 0 ? <span className="block text-[11px] font-medium text-status-warning-text">Shows as out of stock</span> : null}
                </td>
                <td className="px-3 py-2 text-xs">{!r.online ? "Not listed" : r.error ? <span className="font-medium text-status-error-text">{r.error}</span> : r.lastSync ? `Up to date, ${r.lastSync}` : "Waiting to send"}</td>
              </tr>
            );
          })}
        </tbody>
      </table>
    </div>
  );
}

/* -------------------------------- IG-15. Marketing consent -------------------------------- */

export type Consent = { customer: string; sms: boolean; email: boolean; source: string; on: string; optedOut?: string };

/** Who can receive promotions (IG-15): opted-in only, with where and when they agreed; opt-outs are kept, not deleted. */
export function ConsentList({ rows, channel }: { rows: Consent[]; channel: "sms" | "email" }) {
  const yes = rows.filter((r) => r[channel] && !r.optedOut);
  return (
    <div className="space-y-2">
      <p className="text-sm">
        <span className="font-semibold tabular-nums">{yes.length}</span> of {rows.length} customers can receive {channel === "sms" ? "SMS" : "email"} promotions.
      </p>
      <ul className="divide-y divide-[var(--color-border-subtle)] rounded-lg border border-[var(--color-border-panel)] text-sm bg-card">
        {rows.map((r) => (
          <li key={r.customer} className="flex items-center justify-between gap-2 px-3 py-1.5">
            <span>
              {r.customer}
              <span className="block text-xs text-muted-foreground">{r.optedOut ? `Opted out ${r.optedOut}` : r.on ? `Agreed ${r.on}, ${r.source}` : "Never asked"}</span>
            </span>
            <span className={cn("text-xs font-medium", r[channel] && !r.optedOut ? "text-status-success-text" : "text-muted-foreground")}>{r[channel] && !r.optedOut ? "Opted in" : "Not opted in"}</span>
          </li>
        ))}
      </ul>
    </div>
  );
}

/* ------------------------------------ AC-07. B2B flow ------------------------------------ */

export type B2BDoc = { kind: "Order" | "Invoice" | "Delivery note"; ref?: string; status: string; amount?: number; paid?: number };

/**
 * B2B documents (AC-07): order → invoice → delivery note, each linked to the next, with what's been paid.
 * The next step is the only primary action, so the flow can't be skipped.
 */
export function B2BChain({ docs }: { docs: B2BDoc[] }) {
  return (
    <ol className="grid gap-2 sm:grid-cols-3">
      {docs.map((d, i) => {
        const done = !!d.ref;
        return (
          <li key={d.kind} className={cn("rounded-lg border p-3 text-sm", done ? "border-[var(--color-border-panel)] bg-card" : "border-dashed border-[var(--color-border-panel)] text-muted-foreground")}>
            <p className="text-xs font-medium tracking-wide uppercase">
              {i + 1}. {d.kind}
            </p>
            <p className="mt-1 font-mono font-semibold">{d.ref ?? "Not yet"}</p>
            <p className="text-xs">{d.status}</p>
            {d.amount != null ? (
              <p className="mt-1 text-xs tabular-nums">
                KES {n2(d.amount)}
                {d.paid != null ? ` · paid ${n2(d.paid)} · due ${n2(d.amount - d.paid)}` : ""}
              </p>
            ) : null}
          </li>
        );
      })}
    </ol>
  );
}
