"use client";

/**
 * Barcode labels (IN-06), on the Medora design system and plan B (DECISIONS.md, 26 Oct 2026): label sizes are saved
 * once in the pharmacy's settings (`label_sizes`, JSON) with the design's `LabelSizes`, and the design's
 * `LabelPrintPlan` turns chosen products into labels, printed in the browser (`BarcodeLabel`, or `ShelfLabel` on sizes
 * 60 mm wide or more). Quantities default to what arrived today.
 */

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

import { LabelPrintPlan, LabelSizes, type LabelItem, type LabelSize } from "@/components/medora/label-plan";
import { PanelError } from "@/components/medora/errors";
import { MedoraPageHeader } from "@/components/medora/page-header";
import { BarcodeLabel, ShelfLabel, printElement, type Paper } from "@/components/medora/print";
import { Button } from "@/components/ui/button";
import { InputGroup, InputGroupAddon, InputGroupInput } from "@/components/ui/input-group";
import { toast } from "@/components/ui/toast";
import { InventoryApi, ProductsApi, SettingsApi } from "@/lib/api";
import { localToday } from "@/lib/format";
import { useMenuPermissions } from "@/lib/permissions";
import { Loading } from "@/components/medora/loading";

const KEY = "label_sizes";
/** Until a pharmacy saves its own: the design's three common sizes. */
const DEFAULT_SIZES: LabelSize[] = [
  { id: "roll-38x25", name: "Shelf roll 38 × 25", width: 38, height: 25, perSheet: 1 },
  { id: "sticker-70x40", name: "Price sticker 70 × 40", width: 70, height: 40, perSheet: 1 },
  { id: "a4-65", name: "A4 sheet, 65 labels", width: 38, height: 21, perSheet: 65 },
];
type P = { id: number; name: string; code: string; barcode: string | null; sellPrice: number | string; shelfLocation?: string | null; requiresRx?: boolean; unit?: string | null; type?: string };

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 parseSizes = (raw: unknown): LabelSize[] | null => {
  try {
    const v = JSON.parse(String(raw ?? ""));
    return Array.isArray(v) && v.length ? v.filter((s) => s && s.id && s.name && Number(s.width) && Number(s.height)).map((s) => ({ id: String(s.id), name: String(s.name), width: Number(s.width), height: Number(s.height), perSheet: Number(s.perSheet) || 1 })) : null;
  } catch {
    return null;
  }
};
const paperFor = (s: LabelSize): Paper => (s.perSheet > 1 ? "a4" : s.width >= 60 ? "label-70x40" : "label-38x25");

export default function LabelsPage() {
  const { can, loaded } = useMenuPermissions();
  const canRead = loaded && can("Inventory/Products", "Read");
  const canSaveSizes = loaded && can("Settings/General settings", "Update");
  const [products, setProducts] = React.useState<P[] | null>(null);
  const [failed, setFailed] = React.useState(false); // R12 (card 037): a failed load says so, with Try again
  const [attempt, setAttempt] = React.useState(0);
  const [sizes, setSizes] = React.useState<LabelSize[]>(DEFAULT_SIZES);
  const [items, setItems] = React.useState<LabelItem[]>([]);
  const [q, setQ] = React.useState("");
  const [printing, setPrinting] = React.useState<{ size: LabelSize; labels: P[] } | null>(null);
  const sheet = React.useRef<HTMLDivElement>(null);

  React.useEffect(() => {
    if (!canRead) return;
    SettingsApi.getAll()
      .then((s: Record<string, string>) => setSizes(parseSizes(s[KEY]) ?? DEFAULT_SIZES))
      .catch(() => undefined);
    Promise.all([ProductsApi.getAll() as Promise<P[]>, InventoryApi.movements({ from: localToday(), to: localToday(), action: "Received", pageSize: 500 }).catch(() => ({ rows: [] }))])
      .then(([ps, received]) => {
        const stocked = ps.filter((p) => p.type !== "Service" && p.barcode);
        setProducts(stocked);
        setFailed(false);
        // Quantities default to what arrived today (GRNs and receipts), as the design's plan does
        const arrived = new Map<number, number>();
        for (const m of received.rows as { productId: number; change: number }[]) if (m.change > 0) arrived.set(m.productId, (arrived.get(m.productId) ?? 0) + m.change);
        setItems(stocked.filter((p) => arrived.has(p.id)).map((p) => ({ id: p.id, name: p.name, barcode: p.barcode!, suggested: Math.min(500, arrived.get(p.id)!), why: `${arrived.get(p.id)} received today` })));
      })
      .catch((e) => {
        setProducts([]);
        setFailed(true);
        toast.add({ type: "error", title: "Couldn't load products", description: apiError(e, "Try again.") });
      });
  }, [canRead, attempt]);

  // Print once the labels are on the page (hidden), then clear them
  React.useEffect(() => {
    if (!printing || !sheet.current) return;
    printElement(sheet.current, paperFor(printing.size));
    setPrinting(null);
  }, [printing]);

  const saveSizes = async (next: LabelSize[]) => {
    const before = sizes;
    setSizes(next);
    try {
      await SettingsApi.save({ [KEY]: JSON.stringify(next) });
      toast.add({ title: "Label sizes saved" });
    } catch (e) {
      setSizes(before);
      toast.add({ type: "error", title: "Label sizes not saved", description: apiError(e, "Try again.") });
    }
  };

  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 products.</div>;
  const term = q.trim().toLowerCase();
  const matches = term && products ? products.filter((p) => !items.some((i) => i.id === p.id) && `${p.name} ${p.code} ${p.barcode}`.toLowerCase().includes(term)).slice(0, 8) : [];
  const today = localToday();

  return (
    <div className="medora mx-auto w-full max-w-6xl space-y-4">
      <MedoraPageHeader title="Barcode labels" description="Print shelf and barcode labels." />
      <div className="grid gap-4 xl:grid-cols-2">
        <section aria-label="Print labels" className="space-y-3 rounded-lg border border-[var(--color-border-panel)] p-4 bg-card">
          <h2 className="text-sm font-semibold">Print labels</h2>
          <InputGroup>
            <InputGroupAddon>
              <SearchIcon />
            </InputGroupAddon>
            <InputGroupInput placeholder="Add a product: name, code or barcode" value={q} onChange={(e) => setQ(e.target.value)} aria-label="Find a product" />
          </InputGroup>
          {matches.length ? (
            <ul className="divide-y divide-[var(--color-border-subtle)] rounded-md border border-[var(--color-border-panel)] text-sm bg-card">
              {matches.map((p) => (
                <li key={p.id} className="flex items-center gap-3 px-3 py-1.5">
                  <span className="flex-1">
                    {p.name} <span className="font-mono text-xs text-muted-foreground">{p.barcode}</span>
                  </span>
                  <Button size="xs" variant="outline" onClick={() => { setItems((xs) => [...xs, { id: p.id, name: p.name, barcode: p.barcode!, suggested: 1, why: "added" }]); setQ(""); }}>
                    <PlusIcon data-icon="inline-start" />
                    Add
                  </Button>
                </li>
              ))}
            </ul>
          ) : null}
          {failed ? (
            <PanelError framed what="products" onRetry={() => setAttempt((n) => n + 1)} />
          ) : products === null ? (
            <Loading what="products" shape="list" rows={5} />
          ) : items.length ? (
            <>
              <LabelPrintPlan
                key={items.map((i) => i.id).join(",") + sizes.map((s) => s.id).join(",")}
                items={items}
                sizes={sizes}
                onPrint={({ size, quantities }) => {
                  const labels = items.flatMap((i) => Array.from({ length: quantities[i.id] ?? 0 }, () => products.find((p) => p.id === i.id)!)).filter(Boolean);
                  setPrinting({ size, labels });
                }}
              />
              <Button size="sm" variant="ghost" onClick={() => setItems([])}>
                <XIcon data-icon="inline-start" />
                Clear the list
              </Button>
            </>
          ) : (
            <p className="py-6 text-center text-sm text-muted-foreground">Nothing received today. Add products to print their labels.</p>
          )}
        </section>
        <section aria-label="Label sizes" className="space-y-3 rounded-lg border border-[var(--color-border-panel)] p-4 bg-card">
          <h2 className="text-sm font-semibold">Label sizes</h2>
          {canSaveSizes ? (
            <LabelSizes sizes={sizes} onChange={(s) => void saveSizes(s)} />
          ) : (
            <ul className="space-y-1 text-sm">
              {sizes.map((s) => (
                <li key={s.id}>
                  {s.name} <span className="text-muted-foreground tabular-nums">({s.width} × {s.height} mm{s.perSheet > 1 ? `, ${s.perSheet} a sheet` : ""})</span>
                </li>
              ))}
              <li className="text-xs text-muted-foreground">Sizes are changed by someone who can change settings.</li>
            </ul>
          )}
        </section>
      </div>

      {printing ? (
        <div className="hidden" aria-hidden>
          <div ref={sheet} className="flex flex-wrap gap-[2mm]">
            {printing.labels.map((p, i) =>
              printing.size.width >= 60 ? (
                <ShelfLabel key={i} printedOn={today} product={{ name: p.name, price: Number(p.sellPrice) || 0, barcode: p.barcode!, shelf: p.shelfLocation ?? undefined, rx: !!p.requiresRx, pack: p.unit ?? undefined }} />
              ) : (
                <BarcodeLabel key={i} product={{ name: p.name, price: Number(p.sellPrice) || 0, barcode: p.barcode! }} />
              ),
            )}
          </div>
        </div>
      ) : null}
    </div>
  );
}
