"use client";

/**
 * Inventory → By branch and Inventory log (PRD Phase 1.2, Stage 7, 26 Oct 2026): the design's `BranchStockMatrix`
 * with its cell drawer and `InventoryLog` (`/design-system/inventory`, branch-stock-demo.tsx), on the Inventory API.
 *
 * Not in the design yet, and therefore kept plain: the stock-per-branch switch and allocating from the main branch
 * (decision Q1). Allocation uses the design's transfer lines (`TransferSendLines`), one line per product, because the
 * API takes earliest expiry first. Where the demo offers "Request transfer", the live page offers allocation only while
 * it is allowed; once stock per branch is on, the drawer points to Branch transfers.
 */

import * as React from "react";
import { ArrowLeftRightIcon, PowerIcon } from "lucide-react";

import { BranchStockMatrix, branchQty, cellStatus, InventoryLog, QtyCell, type Movement, type MovementAction, type StockBranch, type StockRow } from "@/components/medora/branch-stock";
import { ConfirmDialog } from "@/components/medora/confirm-dialog";
import { DescriptionList } from "@/components/medora/description-list";
import { RecordDrawer } from "@/components/medora/drawer";
import { TransferSendLines, type TransferStockOption } from "@/components/medora/stock-ops";
import { Badge } from "@/components/ui/badge";
import { Button } from "@/components/ui/button";
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 { toast } from "@/components/ui/toast";
import { InventoryApi, type MovementRow, type StockListRow, type StockStatus } from "@/lib/api";
import { addDays, formatDate, formatKES, localToday } from "@/lib/format";
import { Loading } from "@/components/medora/loading";

const apiError = (e: unknown, fallback: string): string => {
  const m = (e as { response?: { data?: { message?: string | string[] } } })?.response?.data?.message;
  return Array.isArray(m) ? m.join(" ") : m || fallback;
};

/** The design's branches: locations keyed by id, as the API keys each row's quantities. */
const toBranches = (s: StockStatus): StockBranch[] => s.branches.map((b) => ({ id: b.id, name: b.name, locations: b.locations.map((l) => ({ id: String(l.id), name: l.name })) }));

const toRow = (r: StockListRow): StockRow => ({
  productId: r.productId,
  name: r.name,
  sub: [r.code, r.sub].filter(Boolean).join(" · ") || undefined,
  category: r.category,
  subCategory: r.subCategory,
  brand: r.brand,
  // Only roles that see cost reach Inventory; a missing cost reads as 0 rather than breaking the totals
  unitCost: r.unitCost ?? 0,
  unitPrice: r.unitPrice,
  reorderLevel: r.reorderLevel,
  qty: r.qty,
});

/** Every row: the matrix filters on the client, so the whole list is loaded a page at a time. */
async function loadAllStock() {
  const first = await InventoryApi.stock({ page: 1, pageSize: 200 });
  const rows = [...first.rows];
  for (let page = 2; rows.length < first.total; page++) {
    const next = await InventoryApi.stock({ page, pageSize: 200 });
    if (!next.rows.length) break;
    rows.push(...next.rows);
  }
  return { status: first as StockStatus, rows };
}

/* ------------------------------------- By branch ------------------------------------- */

export function BranchStockView({ canAllocate, canSwitch, onChanged }: { canAllocate: boolean; canSwitch: boolean; onChanged?: () => void }) {
  const [data, setData] = React.useState<{ status: StockStatus; rows: StockListRow[] } | null>(null);
  const [failed, setFailed] = React.useState(false);
  const [cell, setCell] = React.useState<{ row: StockRow; branch: StockBranch } | null>(null);
  const [allocating, setAllocating] = React.useState<{ toBranchId: number | null; productId?: number } | null>(null);
  const [switching, setSwitching] = React.useState(false);

  const load = React.useCallback(async () => {
    setFailed(false);
    try {
      setData(await loadAllStock());
    } catch (e) {
      setFailed(true);
      toast.add({ type: "error", title: "Couldn't load stock by branch", description: apiError(e, "Try again.") });
    }
  }, []);
  React.useEffect(() => void load(), [load]);

  if (failed && !data) {
    return (
      <div className="flex flex-col items-center gap-2 py-12 text-sm text-muted-foreground">
        Stock by branch didn&apos;t load.
        <Button size="sm" variant="outline" onClick={() => void load()}>
          Try again
        </Button>
      </div>
    );
  }
  if (!data) {
    return (
      <Loading what="stock by branch" columns={5} rows={8} toolbar={1} numeric={[1, 2, 3, 4]} />
    );
  }

  const { status } = data;
  const branches = toBranches(status);
  const rows = data.rows.map(toRow);
  const main = status.branches.find((b) => b.isMain);
  const mayAllocate = canAllocate && !status.stockByBranch && !!main && status.branches.length > 1;
  // Branches that sold in the last 30 days with nothing on the shelf: turning the switch on is refused until they have stock
  const empty = status.branches.filter((b) => b.recentSales > 0 && b.units <= 0);

  return (
    <div className="space-y-4">
      {status.branches.length > 1 || status.stockByBranch ? (
        <section className="flex flex-wrap items-start gap-3 rounded-lg border border-[var(--color-border-panel)] bg-card px-4 py-3 text-sm">
          <div className="min-w-0 flex-1 space-y-1">
            <p className="flex flex-wrap items-center gap-2 font-medium">
              Stock per branch <Badge variant={status.stockByBranch ? "default" : "outline"}>{status.stockByBranch ? "On" : "Off"}</Badge>
            </p>
            <p className="text-muted-foreground">
              {status.stockByBranch
                ? "Each till sells only its own branch's stock. Stock arrives in the branch that receives it."
                : `Tills still sell from the whole pharmacy's stock. Existing stock is in ${main?.name ?? "the main branch"}; allocate each selling branch its share, then turn this on.`}
            </p>
            {!status.stockByBranch && empty.length ? (
              <p className="text-muted-foreground">
                Still without stock: {empty.map((b) => b.name).join(", ")}.
              </p>
            ) : null}
          </div>
          <div className="flex flex-wrap gap-2">
            {mayAllocate ? (
              <Button size="sm" variant="outline" onClick={() => setAllocating({ toBranchId: empty.find((b) => !b.isMain)?.id ?? status.branches.find((b) => !b.isMain)?.id ?? null })}>
                <ArrowLeftRightIcon data-icon="inline-start" />
                Allocate stock
              </Button>
            ) : null}
            {canSwitch ? (
              <Button size="sm" variant={status.stockByBranch ? "outline" : "default"} onClick={() => setSwitching(true)}>
                <PowerIcon data-icon="inline-start" />
                {status.stockByBranch ? "Turn off" : "Turn on"}
              </Button>
            ) : null}
          </div>
        </section>
      ) : null}

      <BranchStockMatrix
        caption="Stock by branch"
        rows={rows}
        branches={branches}
        scopeBranchId={branches.length === 1 && !canAllocate ? branches[0].id : undefined}
        onOpenCell={(row, branch) => setCell({ row, branch })}
      />

      {cell ? (
        <CellDrawer
          row={cell.row}
          branch={cell.branch}
          branches={branches}
          mainBranchId={main?.id ?? null}
          onClose={() => setCell(null)}
          onAllocate={mayAllocate && cell.branch.id !== main?.id ? () => { setAllocating({ toBranchId: cell.branch.id, productId: cell.row.productId }); setCell(null); } : undefined}
        />
      ) : null}

      {allocating && main ? (
        <AllocateDrawer
          main={main}
          branches={status.branches.filter((b) => !b.isMain)}
          rows={data.rows}
          initial={allocating}
          onClose={() => setAllocating(null)}
          onDone={async () => {
            setAllocating(null);
            await load();
            onChanged?.();
          }}
        />
      ) : null}

      {switching ? (
        <ConfirmDialog
          open
          onOpenChange={(o) => !o && setSwitching(false)}
          level="A"
          tone={status.stockByBranch ? "danger" : "primary"}
          title={status.stockByBranch ? "Turn stock per branch off?" : "Turn stock per branch on?"}
          consequences={
            status.stockByBranch
              ? ["Every till sells from the whole pharmacy's stock again, whichever branch holds it.", "Stock stays where it is, and you can turn this back on."]
              : ["Each till sells only what its own branch holds; a sale its branch can't cover is refused.", "A user without a branch sells from the main branch.", "Moving stock between branches after this is a branch transfer (Inventory → Branch transfers)."]
          }
          actionLabel={status.stockByBranch ? "Turn off" : "Turn on"}
          busyLabel="Saving…"
          onConfirm={async () => {
            try {
              await InventoryApi.setStockByBranch(!status.stockByBranch);
            } catch (e) {
              throw new Error(apiError(e, "Not changed. Try again."));
            }
            setSwitching(false);
            toast.add({ title: status.stockByBranch ? "Stock per branch is off" : "Stock per branch is on" });
            await load();
            onChanged?.();
          }}
        />
      ) : null}
    </div>
  );
}

/** The design's cell drawer (branch-stock-demo.tsx) on live figures; allocation stands in for transfers (Q1). */
function CellDrawer({ row, branch, branches, mainBranchId, onClose, onAllocate }: { row: StockRow; branch: StockBranch; branches: StockBranch[]; mainBranchId: number | null; onClose: () => void; onAllocate?: () => void }) {
  const here = branchQty(row, branch);
  const others = branches.filter((b) => b.id !== branch.id).map((b) => ({ b, q: branchQty(row, b) })).sort((a, z) => z.q - a.q);
  const short = cellStatus(here, row.reorderLevel) !== "ok";
  const mainQty = others.find((o) => o.b.id === mainBranchId)?.q ?? 0;
  return (
    <RecordDrawer open onOpenChange={(o) => !o && onClose()} title={`${row.name} at ${branch.name}`} subtitle={`${row.sub ? `${row.sub} · ` : ""}reorder level ${row.reorderLevel} per branch`}>
      <div className="space-y-5">
        <section className="space-y-2">
          <h3 className="text-sm font-semibold">By location</h3>
          <DescriptionList
            variant="summary"
            density="compact"
            items={[...branch.locations.map((l) => ({ label: l.name, value: <QtyCell q={row.qty[l.id] ?? 0} status={(row.qty[l.id] ?? 0) < 0 ? "negative" : "ok"} /> })), { label: `${branch.name} total`, value: <QtyCell q={here} status={cellStatus(here, row.reorderLevel)} strong /> }]}
          />
        </section>
        {others.length ? (
          <section className="space-y-2">
            <h3 className="text-sm font-semibold">Other branches</h3>
            <ul className="divide-y divide-[var(--color-border-subtle)] rounded-lg border border-[var(--color-border-panel)] text-sm bg-card">
              {others.map(({ b, q }) => (
                <li key={b.id} className="flex items-center justify-between px-3 py-2">
                  {b.name}
                  {b.id === mainBranchId ? " (main)" : ""}
                  <QtyCell q={q} status={cellStatus(q, row.reorderLevel)} />
                </li>
              ))}
            </ul>
            {short && onAllocate && mainQty > 0 ? (
              <div className="flex flex-wrap items-center justify-between gap-2 rounded-lg border border-[var(--color-border-panel)] px-3 py-2 text-sm bg-card">
                <p>
                  The main branch holds <span className="font-semibold tabular-nums">{mainQty.toLocaleString("en-US")}</span>.
                </p>
                <Button size="sm" variant="outline" onClick={onAllocate}>
                  <ArrowLeftRightIcon data-icon="inline-start" />
                  Allocate from main branch
                </Button>
              </div>
            ) : short ? (
              <p className="text-sm text-muted-foreground">Send it from another branch on Branch transfers, or add it to a purchase order.</p>
            ) : null}
          </section>
        ) : null}
        <section className="space-y-2">
          <h3 className="text-sm font-semibold">Value here</h3>
          <DescriptionList
            variant="inline"
            density="compact"
            items={[
              { label: "At cost (from batches)", value: formatKES(Math.max(0, here) * row.unitCost, { decimals: true }), money: true },
              { label: "At selling price", value: formatKES(Math.max(0, here) * row.unitPrice, { decimals: true }), money: true },
            ]}
          />
        </section>
      </div>
    </RecordDrawer>
  );
}

/** Q1: from the main branch to one other, before stock per branch is on. One line per product; the API takes earliest expiry first. */
function AllocateDrawer({ main, branches, rows, initial, onClose, onDone }: { main: StockStatus["branches"][number]; branches: StockStatus["branches"]; rows: StockListRow[]; initial: { toBranchId: number | null; productId?: number }; onClose: () => void; onDone: () => Promise<void> }) {
  const mainLocs = main.locations.map((l) => String(l.id));
  const options: TransferStockOption[] = rows
    .map((r) => ({ r, available: mainLocs.reduce((s, id) => s + Math.max(0, r.qty[id] ?? 0), 0) }))
    .filter((o) => o.available > 0 || o.r.productId === initial.productId)
    .map(({ r, available }) => ({ id: String(r.productId), name: r.name, batch: r.code ?? "", expiry: "Earliest first", available }));
  const [to, setTo] = React.useState<number | null>(initial.toBranchId);
  const [qty, setQty] = React.useState<Record<string, number>>({});
  const [note, setNote] = React.useState("");
  const [saving, setSaving] = React.useState(false);
  const lines = Object.entries(qty).filter(([, n]) => n > 0).map(([id, n]) => ({ productId: Number(id), quantity: n }));
  const target = branches.find((b) => b.id === to);

  const save = async () => {
    if (!target || !lines.length) return;
    setSaving(true);
    try {
      const res = await InventoryApi.allocate({ toBranchId: target.id, lines, note: note.trim() || undefined });
      toast.add({ title: `${res.reference}: stock allocated`, description: `${lines.reduce((s, l) => s + l.quantity, 0)} units to ${target.name}.` });
      await onDone();
    } catch (e) {
      toast.add({ type: "error", title: "Not allocated", description: apiError(e, "Try again.") });
    } finally {
      setSaving(false);
    }
  };

  return (
    <RecordDrawer
      open
      onOpenChange={(o) => !o && onClose()}
      variant="edit"
      width="lg"
      saving={saving}
      dirty={lines.length > 0 || !!note}
      title="Allocate stock"
      subtitle={`From ${main.name}, the main branch`}
      footer={
        <>
          <Button variant="outline" disabled={saving} onClick={onClose}>
            Cancel
          </Button>
          <Button disabled={saving || !target || !lines.length} onClick={() => void save()}>
            {saving ? <Spinner /> : null}
            Allocate
          </Button>
        </>
      }
    >
      <div className="space-y-4">
        <div className="grid gap-4">
          <Field>
            <FieldLabel htmlFor="alc-to">To branch</FieldLabel>
            <NativeSelect id="alc-to" value={to ?? ""} onChange={(e) => setTo(Number(e.target.value) || null)}>
              <NativeSelectOption value="">Choose a branch…</NativeSelectOption>
              {branches.map((b) => (
                <NativeSelectOption key={b.id} value={b.id}>
                  {b.name}
                  {b.units > 0 ? ` (holds ${b.units.toLocaleString("en-US")})` : ""}
                </NativeSelectOption>
              ))}
            </NativeSelect>
          </Field>
          <Field>
            <FieldLabel htmlFor="alc-note">Note (optional)</FieldLabel>
            <Input id="alc-note" value={note} maxLength={255} onChange={(e) => setNote(e.target.value)} placeholder="Opening stock for the branch" />
          </Field>
        </div>
        {options.length ? (
          <TransferSendLines options={options} qty={qty} onQty={(id, n) => setQty((q) => ({ ...q, [id]: n }))} />
        ) : (
          <p className="text-sm text-muted-foreground">The main branch holds no stock to allocate.</p>
        )}
        <FieldDescription>Stock moves at once, earliest expiry first, with each batch&apos;s number, expiry and cost. It is recorded in the inventory log as Transfer out and Transfer in.</FieldDescription>
      </div>
    </RecordDrawer>
  );
}

/* ----------------------------------- Inventory log ----------------------------------- */

const pad = (n: number) => String(n).padStart(2, "0");
/** Local date and time, as the till's receipts show it. */
const formatAt = (iso: string) => {
  const d = new Date(iso);
  return `${formatDate(`${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())}`, { short: true })}, ${pad(d.getHours())}:${pad(d.getMinutes())}`;
};

const toMovement = (m: MovementRow): Movement => ({
  id: String(m.id),
  at: m.at,
  product: m.product,
  action: m.action as MovementAction,
  change: m.change,
  newQty: m.newQty,
  branch: m.branch || "—",
  location: m.location,
  user: m.user || "—",
  reference: m.reference || (m.note ?? ""),
});

const LOG_PAGE = 200;

export function InventoryLogView() {
  const today = localToday();
  const [from, setFrom] = React.useState(addDays(today, -6));
  const [to, setTo] = React.useState(today);
  const [data, setData] = React.useState<{ total: number; rows: MovementRow[] } | null>(null);
  const [loading, setLoading] = React.useState(false);

  const load = React.useCallback(async (page = 1) => {
    setLoading(true);
    try {
      const res = await InventoryApi.movements({ from, to, page, pageSize: LOG_PAGE });
      setData((d) => ({ total: res.total, rows: page === 1 || !d ? res.rows : [...d.rows, ...res.rows] }));
    } catch (e) {
      toast.add({ type: "error", title: "Couldn't load the inventory log", description: apiError(e, "Try again.") });
    } finally {
      setLoading(false);
    }
  }, [from, to]);
  React.useEffect(() => void load(1), [load]);

  return (
    <div className="space-y-3">
      <div className="flex flex-wrap items-end gap-3">
        <Field className="w-auto">
          <FieldLabel htmlFor="log-from">From</FieldLabel>
          <Input id="log-from" type="date" value={from} max={to} onChange={(e) => e.target.value && setFrom(e.target.value)} />
        </Field>
        <Field className="w-auto">
          <FieldLabel htmlFor="log-to">To</FieldLabel>
          <Input id="log-to" type="date" value={to} min={from} max={today} onChange={(e) => e.target.value && setTo(e.target.value)} />
        </Field>
        {/* Refreshing keeps the log on screen (loading spec 8, refreshing existing data) */}
        {loading && data !== null ? (
          <span role="status" className="mb-2 flex items-center gap-2 text-xs text-muted-foreground">
            <Spinner /> Updating…
          </span>
        ) : null}
      </div>
      {data === null ? (
        <Loading what="the inventory log" columns={6} rows={8} numeric={[3]} />
      ) : data.rows.length ? (
        <>
          <InventoryLog movements={data.rows.map(toMovement)} formatAt={formatAt} />
          {data.rows.length < data.total ? (
            <div className="flex items-center justify-center gap-3 text-sm text-muted-foreground">
              Showing {data.rows.length.toLocaleString("en-US")} of {data.total.toLocaleString("en-US")}
              <Button size="sm" variant="outline" disabled={loading} onClick={() => void load(Math.floor(data.rows.length / LOG_PAGE) + 1)}>
                Show more
              </Button>
            </div>
          ) : null}
        </>
      ) : (
        <p className="py-10 text-center text-sm text-muted-foreground">
          No stock moved between {formatDate(from, { short: true })} and {formatDate(to, { short: true })}. The log starts on 26 Oct 2026; earlier movements are in the stock closing report.
        </p>
      )}
    </div>
  );
}
