"use client";

/**
 * Waste (PRD 3.4, IN-20), on the Medora design system and plan A (DECISIONS.md, 26 Oct 2026): the design's
 * `WasteLines` to record a write-off and `WasteReport` to list them. A write-off posts at once unless it is worth more
 * than the pharmacy's approval threshold and the person recording it can't approve (Q1, Q2); those wait here for
 * someone with Approve. Reasons are a managed list (Inventory → Setup lists → Waste reasons).
 */

import * as React from "react";
import { CheckIcon, PlusIcon, Trash2Icon, XIcon } from "lucide-react";

import { ConfirmDialog } from "@/components/medora/confirm-dialog";
import { RecordDrawer } from "@/components/medora/drawer";
import { PanelError } from "@/components/medora/errors";
import { MedoraPageHeader } from "@/components/medora/page-header";
import { WasteReport } from "@/components/medora/record-lists";
import { WasteLines, type WasteLine } from "@/components/medora/stock-ops";
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, StockOpsApi, type OpsBatchOption, type WasteRow } from "@/lib/api";
import { formatDate, formatKES, localDate, localToday } from "@/lib/format";
import { useMenuPermissions } from "@/lib/permissions";
import { BatchPicker } from "../inventory/batch-picker";
import { Loading } from "@/components/medora/loading";

const PAGE = "Inventory/Waste";
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;
};
const monthStart = () => `${localToday().slice(0, 8)}01`;

export default function WastePage() {
  const { can, loaded } = useMenuPermissions();
  const may = (a: string) => loaded && can(PAGE, a);
  const [from, setFrom] = React.useState(monthStart());
  const [to, setTo] = React.useState(localToday());
  const [data, setData] = React.useState<{ rows: WasteRow[]; approvalAbove: number } | null>(null);
  const [failed, setFailed] = React.useState(false); // R12 (card 037): a failed load says so, with Try again
  const [recording, setRecording] = React.useState(false);
  const [deciding, setDeciding] = React.useState<{ row: WasteRow; approve: boolean } | null>(null);

  const load = React.useCallback(async () => {
    try {
      setData(await StockOpsApi.waste({ from, to }));
      setFailed(false);
    } catch (e) {
      setData({ rows: [], approvalAbove: 0 });
      setFailed(true);
      toast.add({ type: "error", title: "Couldn't load waste", description: apiError(e, "Try again.") });
    }
  }, [from, to]);
  const canRead = may("Read");
  React.useEffect(() => {
    if (canRead) void load();
  }, [canRead, load]);

  if (loaded && !canRead) return <div className="medora mx-auto w-full max-w-6xl py-10 text-center text-sm text-muted-foreground">Your role can&apos;t see waste.</div>;
  const waiting = (data?.rows ?? []).filter((r) => r.status === "Awaiting approval");

  return (
    <div className="medora mx-auto w-full max-w-6xl space-y-4">
      <MedoraPageHeader
        title="Waste"
        description="Write off expired, damaged or recalled stock, at batch cost."
        primary={may("Create") ? { label: "Record waste", icon: Trash2Icon, onSelect: () => setRecording(true) } : undefined}
      />
      {data && data.approvalAbove > 0 ? (
        <p className="text-sm text-muted-foreground">Write-offs worth more than {formatKES(data.approvalAbove, { decimals: true })} at cost wait for someone who can approve them.</p>
      ) : null}

      {waiting.length ? (
        <section aria-label="Awaiting approval" className="space-y-2 rounded-lg border border-[var(--color-border-panel)] p-4 bg-card">
          <h2 className="text-sm font-semibold">Awaiting approval</h2>
          <ul className="divide-y divide-[var(--color-border-subtle)] text-sm">
            {waiting.map((r) => (
              <li key={r.id} className="flex flex-wrap items-center justify-between gap-3 py-2">
                <span>
                  <span className="font-mono text-xs">{r.no}</span> · {r.branch} · {r.reason}
                  <span className="block text-xs text-muted-foreground">
                    {r.items.length} {r.items.length === 1 ? "line" : "lines"} recorded by {r.staff || "—"} on {formatDate(r.date, { short: true })}
                    {r.value != null ? `, ${formatKES(r.value, { decimals: true })} at cost` : ""}
                    {r.note ? ` · ${r.note}` : ""}
                  </span>
                </span>
                {may("Approve") ? (
                  <span className="flex gap-2">
                    <Button size="sm" variant="outline" onClick={() => setDeciding({ row: r, approve: false })}>
                      <XIcon />
                      Reject
                    </Button>
                    <Button size="sm" onClick={() => setDeciding({ row: r, approve: true })}>
                      <CheckIcon />
                      Approve
                    </Button>
                  </span>
                ) : null}
              </li>
            ))}
          </ul>
        </section>
      ) : null}

      {/* One container (data table spec §1): the dates sit in the list's own toolbar */}
      <WasteReport
        rows={data ? data.rows.map((r) => ({ no: r.no, date: r.date, branch: r.branch, staff: r.staff, reason: r.reason, status: r.status, approver: r.approver, items: r.items.map((i) => ({ name: i.name, qty: i.qty, cost: i.cost ?? 0 })) })) : []}
        toolbar={
          <>
            <Input aria-label="From" type="date" className="w-40" value={from} max={to} onChange={(e) => setFrom(e.target.value)} />
            <Input aria-label="To" type="date" className="w-40" value={to} min={from} onChange={(e) => setTo(e.target.value)} />
          </>
        }
        body={failed ? <PanelError what="waste records" onRetry={() => void load()} /> : data === null ? <Loading what="waste" columns={4} rows={4} numeric={[3]} bare /> : undefined}
        empty="No waste recorded in these dates."
      />

      {recording ? <RecordWasteDrawer onClose={() => setRecording(false)} onSaved={() => { setRecording(false); void load(); }} /> : null}

      {deciding ? (
        <ConfirmDialog
          open
          onOpenChange={(o) => !o && setDeciding(null)}
          level={deciding.approve ? "neutral" : "B"}
          title={`${deciding.approve ? "Approve" : "Reject"} ${deciding.row.no}?`}
          consequences={
            deciding.approve
              ? ["The stock leaves its batches now and is posted to stock write-offs at batch cost.", "If some of it has been sold since, approving is refused: reject it and record the waste again."]
              : ["Nothing leaves stock.", "The reason is kept with the record and in the activity log."]
          }
          reasonHelp="Saved with the write-off."
          actionLabel={deciding.approve ? "Approve" : "Reject"}
          busyLabel={deciding.approve ? "Approving…" : "Rejecting…"}
          onConfirm={async ({ reason }) => {
            try {
              if (deciding.approve) await StockOpsApi.approveWaste(deciding.row.id);
              else await StockOpsApi.rejectWaste(deciding.row.id, reason ?? "");
            } catch (e) {
              throw new Error(apiError(e, "Not saved. Try again."));
            }
            toast.add({ title: `${deciding.row.no} ${deciding.approve ? "approved" : "rejected"}` });
            setDeciding(null);
            await load();
          }}
        />
      ) : null}
    </div>
  );
}

/** Recording a write-off: pick batches, set quantity and reason per line (the design's `WasteLines`). */
function RecordWasteDrawer({ onClose, onSaved }: { onClose: () => void; onSaved: () => void }) {
  const [branches, setBranches] = React.useState<{ id: number; name: string }[]>([]);
  const [branchId, setBranchId] = React.useState<number | undefined>(undefined);
  const [reasons, setReasons] = React.useState<string[]>([]);
  const [lines, setLines] = React.useState<WasteLine[]>([]);
  const [note, setNote] = React.useState("");
  const [saving, setSaving] = React.useState(false);
  const { can } = useMenuPermissions();
  const allBranches = can(PAGE, "All branches");

  React.useEffect(() => {
    StockOpsApi.wasteReasons().then(setReasons).catch(() => setReasons([]));
    if (allBranches) InventoryApi.status().then((s) => setBranches(s.branches.map((b) => ({ id: b.id, name: b.name })))).catch(() => setBranches([]));
  }, [allBranches]);

  const load = React.useCallback((search: string) => StockOpsApi.wasteOptions({ branchId, search }), [branchId]);
  const add = (o: OpsBatchOption) =>
    setLines((l) => [...l, { id: String(o.batchId), name: o.name, batch: o.batch || "—", expiry: o.expiryDate ? localDate(o.expiryDate) : "", qty: 1, available: o.available, unitCost: o.unitCost ?? 0, reason: reasons.includes("Expired") ? "Expired" : reasons[0] ?? "" }]);

  const save = async () => {
    if (!lines.length) return;
    setSaving(true);
    try {
      const w = await StockOpsApi.recordWaste({ branchId, note: note.trim() || undefined, lines: lines.filter((l) => l.qty > 0).map((l) => ({ batchId: Number(l.id), quantity: l.qty, reason: l.reason })) });
      toast.add({ title: `${w.no} ${w.status === "Approved" ? "written off" : "recorded"}`, description: w.status === "Approved" ? "The stock has left its batches." : "It waits for approval before stock changes." });
      onSaved();
    } catch (e) {
      toast.add({ type: "error", title: "Not saved", description: apiError(e, "Try again.") });
      setSaving(false);
    }
  };

  return (
    <RecordDrawer
      open
      onOpenChange={(o) => !o && onClose()}
      variant="edit"
      width="lg"
      saving={saving}
      dirty={lines.length > 0}
      title="Record waste"
      subtitle="Choose each batch, how many units and why."
      footer={
        <>
          <Button variant="outline" disabled={saving} onClick={onClose}>
            Cancel
          </Button>
          <Button disabled={saving || !lines.some((l) => l.qty > 0)} onClick={() => void save()}>
            {saving ? <Spinner /> : null}
            Write off
          </Button>
        </>
      }
    >
      <div className="space-y-4">
        {allBranches && branches.length > 1 ? (
          <Field>
            <FieldLabel htmlFor="w-branch">Branch</FieldLabel>
            <NativeSelect id="w-branch" value={branchId ?? ""} onChange={(e) => { setBranchId(Number(e.target.value) || undefined); setLines([]); }}>
              <NativeSelectOption value="">My branch</NativeSelectOption>
              {branches.map((b) => (
                <NativeSelectOption key={b.id} value={b.id}>
                  {b.name}
                </NativeSelectOption>
              ))}
            </NativeSelect>
          </Field>
        ) : null}
        <BatchPicker load={load} taken={new Set(lines.map((l) => Number(l.id)))} onAdd={add} />
        {lines.length ? <WasteLines lines={lines} reasons={reasons} onChange={(id, patch) => setLines((ls) => ls.map((l) => (l.id === id ? { ...l, ...patch } : l)))} /> : null}
        {lines.length ? (
          <Button size="sm" variant="ghost" onClick={() => setLines((ls) => ls.filter((l) => l.qty > 0))}>
            <PlusIcon className="rotate-45" />
            Remove lines set to 0
          </Button>
        ) : null}
        <Field>
          <FieldLabel htmlFor="w-note">Note (optional)</FieldLabel>
          <Input id="w-note" value={note} maxLength={255} onChange={(e) => setNote(e.target.value)} placeholder="e.g. Fridge failed overnight" />
          <FieldDescription>Reasons are managed under Inventory → Setup lists → Waste reasons.</FieldDescription>
        </Field>
      </div>
    </RecordDrawer>
  );
}
