"use client";

import * as React from "react";
import { BanIcon, InfoIcon, TriangleAlertIcon } from "lucide-react";

import { Field, FieldLabel } from "@/components/ui/field";
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select";
import { ToggleGroup, ToggleGroupItem } from "@/components/ui/toggle-group";
import { daysBetween, formatDate, localToday } from "@/lib/format";
import { cn } from "@/lib/utils";

import { BatchChip } from "./stock";
import { TONE_BOX } from "./tones";

export type PickerBatch = {
  id: number;
  batchNo: string;
  expiryDate: string;
  /** On hand, in base units. */
  stock: number;
  /** A batch under recall or quarantine cannot be sold whatever its expiry. */
  hold?: string;
};

export type BatchAllocation = { batchId: number; batchNo: string; qty: number; expiryDate: string };

export type BatchSelection = {
  mode: "auto" | "manual";
  allocations: BatchAllocation[];
  /** Units that could not be allocated. Above 0 means the line cannot be sold as it stands. */
  short: number;
  /** Required when a manual choice skips an earlier-expiring sellable batch; written to the audit log. */
  reason?: string;
  valid: boolean;
};

export const OVERRIDE_REASONS = ["Customer asked for a longer expiry", "Earlier batch not on the shelf", "Earlier batch damaged", "Pharmacist's decision"];

const sellable = (b: PickerBatch, today: string) => !b.hold && b.stock > 0 && daysBetween(today, b.expiryDate) >= 0;
const byExpiry = (a: PickerBatch, b: PickerBatch) => a.expiryDate.localeCompare(b.expiryDate) || a.id - b.id;

/** First expiry, first out: earliest sellable batch first, splitting across batches when one cannot cover the quantity. */
export function allocateFefo(batches: PickerBatch[], qty: number, today = localToday()): { allocations: BatchAllocation[]; short: number } {
  let left = qty;
  const allocations: BatchAllocation[] = [];
  for (const b of [...batches].filter((x) => sellable(x, today)).sort(byExpiry)) {
    if (left <= 0) break;
    const take = Math.min(left, b.stock);
    allocations.push({ batchId: b.id, batchNo: b.batchNo, qty: take, expiryDate: b.expiryDate });
    left -= take;
  }
  return { allocations, short: Math.max(0, left) };
}

/**
 * Batch picker for a till line. Auto (FEFO) is the default and matches how stock is deducted. "Choose batch"
 * lets the cashier override; skipping an earlier sellable batch needs a reason. Expired and held batches are
 * listed but cannot be chosen. Units expiring within 90 days come with a "tell the customer" note.
 */
export function BatchPicker({
  batches,
  qty,
  unitLabel = "units",
  today = localToday(),
  onChange,
}: {
  batches: PickerBatch[];
  /** Quantity needed, in base units. */
  qty: number;
  unitLabel?: string;
  today?: string;
  onChange?: (s: BatchSelection) => void;
}) {
  const [mode, setMode] = React.useState<"auto" | "manual">("auto");
  const [chosen, setChosen] = React.useState<number | null>(null);
  const [reason, setReason] = React.useState("");

  const sorted = React.useMemo(() => [...batches].sort(byExpiry), [batches]);
  const fefo = React.useMemo(() => allocateFefo(batches, qty, today), [batches, qty, today]);
  const firstSellable = sorted.find((b) => sellable(b, today));
  const chosenBatch = sorted.find((b) => b.id === chosen) ?? null;
  const skipsEarlier = mode === "manual" && !!chosenBatch && !!firstSellable && byExpiry(chosenBatch, firstSellable) > 0;

  const selection: BatchSelection = React.useMemo(() => {
    if (mode === "auto") return { mode, allocations: fefo.allocations, short: fefo.short, valid: fefo.short === 0 && qty > 0 };
    if (!chosenBatch) return { mode, allocations: [], short: qty, valid: false };
    const take = Math.min(qty, chosenBatch.stock);
    return {
      mode,
      allocations: take ? [{ batchId: chosenBatch.id, batchNo: chosenBatch.batchNo, qty: take, expiryDate: chosenBatch.expiryDate }] : [],
      short: qty - take,
      reason: skipsEarlier ? reason || undefined : undefined,
      valid: qty - take === 0 && (!skipsEarlier || !!reason),
    };
  }, [mode, fefo, chosenBatch, qty, skipsEarlier, reason]);

  React.useEffect(() => {
    onChange?.(selection);
  }, [selection, onChange]);

  const allocFor = (id: number) => selection.allocations.find((a) => a.batchId === id)?.qty ?? 0;
  const soon = selection.allocations.filter((a) => daysBetween(today, a.expiryDate) < 90);
  const totalSellable = sorted.filter((b) => sellable(b, today)).reduce((s, b) => s + b.stock, 0);

  return (
    <div className="space-y-3">
      <div className="flex flex-wrap items-center justify-between gap-2">
        <ToggleGroup
          variant="outline"
          size="sm"
          spacing={0}
          aria-label="How to choose the batch"
          value={[mode]}
          onValueChange={(v: string[]) => {
            if (!v[0]) return;
            setMode(v[0] as "auto" | "manual");
            setReason("");
            if (v[0] === "manual" && chosen === null && firstSellable) setChosen(firstSellable.id);
          }}
        >
          <ToggleGroupItem value="auto">Auto (first expiry)</ToggleGroupItem>
          <ToggleGroupItem value="manual">Choose batch</ToggleGroupItem>
        </ToggleGroup>
        <span className="text-xs text-muted-foreground tabular-nums">
          Need {qty.toLocaleString("en-US")} {unitLabel} · {totalSellable.toLocaleString("en-US")} sellable
        </span>
      </div>

      <div role={mode === "manual" ? "radiogroup" : "list"} aria-label="Batches" className="divide-y divide-border rounded-lg border border-border">
        {sorted.map((b) => {
          const days = daysBetween(today, b.expiryDate);
          const blocked = b.hold ? `On hold: ${b.hold}` : days < 0 ? "Expired, cannot be sold" : b.stock === 0 ? "None left" : "";
          const take = allocFor(b.id);
          const on = mode === "manual" && chosen === b.id;
          const row = (
            <>
              <span className="min-w-0 flex-1">
                <span className="flex flex-wrap items-center gap-2">
                  <span className="font-mono text-sm font-medium">{b.batchNo}</span>
                  <BatchChip expiryDate={b.expiryDate} today={today} />
                </span>
                <span className="mt-0.5 block text-xs text-muted-foreground">
                  Expires {formatDate(b.expiryDate)} · {b.stock.toLocaleString("en-US")} on hand
                  {blocked ? (
                    <span className="ml-1 inline-flex items-center gap-1 font-medium text-status-error-text">
                      · <BanIcon className="size-3" aria-hidden /> {blocked}
                    </span>
                  ) : null}
                </span>
              </span>
              {take ? (
                <span className="shrink-0 rounded-full bg-accent px-2 py-0.5 text-xs font-semibold text-accent-foreground tabular-nums">
                  {take.toLocaleString("en-US")} from here
                </span>
              ) : null}
            </>
          );
          return mode === "manual" ? (
            <button
              key={b.id}
              type="button"
              role="radio"
              aria-checked={on}
              aria-disabled={!!blocked}
              disabled={!!blocked}
              onClick={() => {
                setChosen(b.id);
                setReason("");
              }}
              className={cn(
                "flex w-full items-center gap-3 px-3 py-2.5 text-left outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-inset",
                on ? "bg-accent/60" : "hover:bg-muted/60",
                blocked ? "cursor-not-allowed opacity-60 hover:bg-transparent" : "cursor-pointer",
              )}
            >
              <span className={cn("flex size-4 shrink-0 items-center justify-center rounded-full border", on ? "border-primary" : "border-input")} aria-hidden>
                {on ? <span className="size-2 rounded-full bg-primary" /> : null}
              </span>
              {row}
            </button>
          ) : (
            <div key={b.id} role="listitem" className={cn("flex items-center gap-3 px-3 py-2.5", blocked && "opacity-60")}>
              {row}
            </div>
          );
        })}
      </div>

      {skipsEarlier ? (
        <Field>
          <FieldLabel>Reason for not using {firstSellable?.batchNo} (expires first)</FieldLabel>
          <Select value={reason || null} onValueChange={(v) => setReason(String(v ?? ""))}>
            <SelectTrigger className="w-full" aria-invalid={!reason}>
              <SelectValue placeholder="Choose a reason; it is recorded" />
            </SelectTrigger>
            <SelectContent>
              {OVERRIDE_REASONS.map((r) => (
                <SelectItem key={r} value={r}>
                  {r}
                </SelectItem>
              ))}
            </SelectContent>
          </Select>
        </Field>
      ) : null}

      {selection.short > 0 && qty > 0 ? (
        <p role="alert" className={cn("flex gap-2 rounded-lg border px-3 py-2 text-sm", TONE_BOX.error)}>
          <TriangleAlertIcon className="mt-0.5 size-4 shrink-0" aria-hidden />
          {mode === "manual" && chosenBatch
            ? `This batch has ${chosenBatch.stock.toLocaleString("en-US")}; ${selection.short.toLocaleString("en-US")} more needed. Lower the quantity or use Auto to take the rest from the next batch.`
            : `Only ${totalSellable.toLocaleString("en-US")} ${unitLabel} can be sold; ${selection.short.toLocaleString("en-US")} short. Expired and held batches are excluded.`}
        </p>
      ) : null}

      {selection.valid && soon.length ? (
        <p role="note" className={cn("flex gap-2 rounded-lg border px-3 py-2 text-sm", TONE_BOX.warning)}>
          <InfoIcon className="mt-0.5 size-4 shrink-0" aria-hidden />
          <span>
            Tell the customer:{" "}
            {soon
              .map((a) => `${a.qty.toLocaleString("en-US")} ${a.qty === 1 ? unitLabel.replace(/s$/, "") : unitLabel} expire ${formatDate(a.expiryDate)}`)
              .join("; ")}
            .
          </span>
        </p>
      ) : null}
    </div>
  );
}
