"use client";

/**
 * Internal issues (PRD 3.6, IN-22, IN-23), on the Medora design system and plan A (DECISIONS.md, 26 Oct 2026): stock
 * moved from one location to another of the same branch (store room to shop floor), or used inside the pharmacy, which
 * costs stock and posts to Stock used internally. The list is the design's `InternalIssueReport`.
 */

import * as React from "react";
import { PackageOpenIcon, XIcon } from "lucide-react";

import { RecordDrawer } from "@/components/medora/drawer";
import { InternalIssueReport } from "@/components/medora/inventory-reports";
import { PanelError } from "@/components/medora/errors";
import { MedoraPageHeader } from "@/components/medora/page-header";
import { Stepper } from "@/components/medora/pos-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 { StockOpsApi, type IssueRow, type OpsBatchOption, type OpsLocation } from "@/lib/api";
import { formatDate, formatKES, localToday } from "@/lib/format";
import { useMenuPermissions } from "@/lib/permissions";
import { BatchPicker } from "../inventory/batch-picker";
import { Surface } from "@/components/medora/surface";
import { Loading } from "@/components/medora/loading";

const PAGE = "Inventory/Issues";
const INTERNAL = "internal";
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 IssuesPage() {
  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 [rows, setRows] = React.useState<IssueRow[] | null>(null);
  const [failed, setFailed] = React.useState(false); // R12 (card 037): a failed load says so, with Try again
  const [issuing, setIssuing] = React.useState(false);

  const load = React.useCallback(async () => {
    try {
      setRows(await StockOpsApi.issues({ from, to }));
      setFailed(false);
    } catch (e) {
      setRows([]);
      setFailed(true);
      toast.add({ type: "error", title: "Couldn't load issues", 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 internal issues.</div>;

  return (
    <div className="medora mx-auto w-full max-w-6xl space-y-4">
      <MedoraPageHeader
        title="Internal issues"
        description="Move stock between a branch's locations, or use it inside the pharmacy."
        primary={may("Create") ? { label: "Issue stock", icon: PackageOpenIcon, onSelect: () => setIssuing(true) } : undefined}
      />
      <Surface className="flex flex-wrap items-end gap-2">
        <Field className="w-auto">
          <FieldLabel htmlFor="i-from">From</FieldLabel>
          <Input id="i-from" type="date" value={from} max={to} onChange={(e) => setFrom(e.target.value)} />
        </Field>
        <Field className="w-auto">
          <FieldLabel htmlFor="i-to">To</FieldLabel>
          <Input id="i-to" type="date" value={to} min={from} onChange={(e) => setTo(e.target.value)} />
        </Field>
      </Surface>
      {failed ? (
        <PanelError framed what="internal issues" onRetry={() => void load()} />
      ) : rows === null ? (
        <Loading what="internal issues" columns={5} rows={4} toolbar={2} numeric={[3, 4]} footer />
      ) : rows.length ? (
        <InternalIssueReport
          issues={rows.map((r) => ({ no: r.no, date: formatDate(r.date, { short: true }), from: `${r.branch}, ${r.from}`, to: r.internalUse ? r.to : `${r.branch}, ${r.to}`, staff: r.staff, reference: r.reference, items: r.items.map((i) => ({ name: i.name, qty: i.qty, unitCost: i.unitCost ?? 0 })) }))}
        />
      ) : (
        <p className="rounded-lg border border-[var(--color-border-panel)] bg-card py-12 text-center text-sm text-muted-foreground">No stock issued in these dates.</p>
      )}
      {issuing ? <IssueDrawer onClose={() => setIssuing(false)} onSaved={() => { setIssuing(false); void load(); }} /> : null}
    </div>
  );
}

type Line = OpsBatchOption & { qty: number };

function IssueDrawer({ onClose, onSaved }: { onClose: () => void; onSaved: () => void }) {
  const [locations, setLocations] = React.useState<OpsLocation[] | null>(null);
  const [fromId, setFromId] = React.useState<number | null>(null);
  const [toId, setToId] = React.useState<string>(INTERNAL);
  const [reference, setReference] = React.useState("");
  const [note, setNote] = React.useState("");
  const [lines, setLines] = React.useState<Line[]>([]);
  const [saving, setSaving] = React.useState(false);

  React.useEffect(() => {
    StockOpsApi.locations()
      .then((l) => {
        setLocations(l);
        setFromId((l.find((x) => x.own && x.isDefault) ?? l[0])?.id ?? null);
      })
      .catch(() => setLocations([]));
  }, []);

  const fromLoc = locations?.find((l) => l.id === fromId);
  const targets = (locations ?? []).filter((l) => fromLoc && l.branchId === fromLoc.branchId && l.id !== fromLoc.id);
  const load = React.useCallback((search: string) => (fromId ? StockOpsApi.issueOptions(fromId, search) : Promise.resolve([])), [fromId]);
  const value = lines.reduce((s, l) => s + l.qty * (l.unitCost ?? 0), 0);

  const save = async () => {
    if (!fromId) return;
    setSaving(true);
    try {
      const r = await StockOpsApi.issue({ fromLocationId: fromId, toLocationId: toId === INTERNAL ? null : Number(toId), reference: reference.trim() || undefined, note: note.trim() || undefined, lines: lines.filter((l) => l.qty > 0).map((l) => ({ batchId: l.batchId, quantity: l.qty })) });
      toast.add({ title: `${r.no} issued`, description: r.internalUse ? "Posted to stock used internally at batch cost." : `Now at ${r.to}.` });
      onSaved();
    } catch (e) {
      toast.add({ type: "error", title: "Not issued", 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="Issue stock"
      subtitle="Within one branch. To send stock to another branch, use a branch transfer."
      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}
            Issue
          </Button>
        </>
      }
    >
      {locations === null ? (
        <Loading what="the issue form" shape="form" rows={5} />
      ) : (
        <div className="space-y-4">
          <div className="grid gap-3 sm:grid-cols-2">
            <Field>
              <FieldLabel htmlFor="i-src">From</FieldLabel>
              <NativeSelect id="i-src" value={fromId ?? ""} onChange={(e) => { setFromId(Number(e.target.value) || null); setLines([]); setToId(INTERNAL); }}>
                {locations.map((l) => (
                  <NativeSelectOption key={l.id} value={l.id}>
                    {l.branch}, {l.name}
                  </NativeSelectOption>
                ))}
              </NativeSelect>
            </Field>
            <Field>
              <FieldLabel htmlFor="i-dst">To</FieldLabel>
              <NativeSelect id="i-dst" value={toId} onChange={(e) => setToId(e.target.value)}>
                <NativeSelectOption value={INTERNAL}>Internal use (costs stock)</NativeSelectOption>
                {targets.map((l) => (
                  <NativeSelectOption key={l.id} value={String(l.id)}>
                    {l.name}
                  </NativeSelectOption>
                ))}
              </NativeSelect>
            </Field>
          </div>
          <BatchPicker load={load} taken={new Set(lines.map((l) => l.batchId))} onAdd={(o) => setLines((ls) => [...ls, { ...o, qty: 1 }])} />
          {lines.length ? (
            <ul className="divide-y divide-[var(--color-border-subtle)] rounded-lg border border-[var(--color-border-panel)] text-sm bg-card">
              {lines.map((l) => (
                <li key={l.batchId} className="flex flex-wrap items-center justify-between gap-3 px-3 py-2">
                  <span>
                    <span className="font-medium">{l.name}</span>
                    <span className="block text-xs text-muted-foreground">
                      <span className="font-mono">{l.batch || "—"}</span> · {l.available} in batch
                    </span>
                  </span>
                  <span className="flex items-center gap-2">
                    <Stepper value={l.qty} min={1} max={l.available} label={`${l.name} to issue`} onChange={(n) => setLines((ls) => ls.map((x) => (x.batchId === l.batchId ? { ...x, qty: n } : x)))} />
                    <Button size="icon-sm" variant="ghost" aria-label={`Remove ${l.name}`} onClick={() => setLines((ls) => ls.filter((x) => x.batchId !== l.batchId))}>
                      <XIcon />
                    </Button>
                  </span>
                </li>
              ))}
            </ul>
          ) : null}
          {lines.length && toId === INTERNAL ? <p className="text-sm text-muted-foreground">Costs {formatKES(value, { decimals: true })} of stock, posted to Stock used internally.</p> : null}
          <div className="grid gap-3">
            <Field>
              <FieldLabel htmlFor="i-ref">Internal reference (optional)</FieldLabel>
              <Input id="i-ref" value={reference} maxLength={80} onChange={(e) => setReference(e.target.value)} placeholder="e.g. Clinic room request 14" />
            </Field>
            <Field>
              <FieldLabel htmlFor="i-note">Note (optional)</FieldLabel>
              <Input id="i-note" value={note} maxLength={255} onChange={(e) => setNote(e.target.value)} />
              <FieldDescription>Saved with the issue.</FieldDescription>
            </Field>
          </div>
        </div>
      )}
    </RecordDrawer>
  );
}
