"use client";

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

import { Button } from "@/components/ui/button";
import { Checkbox } from "@/components/ui/checkbox";
import { Input } from "@/components/ui/input";
import { NativeSelect, NativeSelectOption } from "@/components/ui/native-select";
import { cn } from "@/lib/utils";

/**
 * One editor for every admin setup list (IN-02 categories, sub-categories, brands, units; HR-03 employee
 * types; waste reasons; deduction categories). Items in use can't be deleted, only deactivated, so past
 * records keep their label; inactive items stay listed but are no longer offered in pickers.
 */
export type ListItem = {
  id: string;
  name: string;
  /** Short form, e.g. "tab" for Tablet. Shown when the list has `short`. */
  short?: string;
  /** Parent item name, for nested lists such as sub-categories. */
  parent?: string;
  active: boolean;
  /** Records using this item: products, employees, waste entries… */
  uses: number;
};

export type ListSpec = {
  /** Singular noun, lower case: "category", "unit". */
  noun: string;
  /** What `uses` counts, plural: "products". */
  usedBy: string;
  short?: { label: string; max: number };
  parents?: { label: string; options: string[] };
};

export function listItemProblems(draft: Pick<ListItem, "name" | "short" | "parent">, items: ListItem[], spec: ListSpec, editingId?: string): string[] {
  const p: string[] = [];
  const name = draft.name.trim();
  if (!name) p.push(`Give the ${spec.noun} a name.`);
  else if (name.length > 60) p.push("Keep the name under 60 characters.");
  // Unique per parent, ignoring case: "Antibiotics" and "antibiotics" would be the same item twice.
  const clash = items.find((i) => i.id !== editingId && i.name.trim().toLowerCase() === name.toLowerCase() && (i.parent ?? "") === (draft.parent ?? ""));
  if (name && clash) p.push(`There's already a ${spec.noun} called ${clash.name}${clash.parent ? ` under ${clash.parent}` : ""}${clash.active ? "" : " (inactive: reactivate it instead)"}.`);
  if (spec.parents && !draft.parent) p.push(`Choose the ${spec.parents.label.toLowerCase()}.`);
  if (spec.short) {
    const s = (draft.short ?? "").trim();
    if (!s) p.push(`Add the ${spec.short.label.toLowerCase()}.`);
    else if (s.length > spec.short.max) p.push(`${spec.short.label} is ${spec.short.max} characters at most.`);
  }
  return p;
}

const plural = (n: number, one: string, many: string) => `${n.toLocaleString("en-US")} ${n === 1 ? one : many}`;

export function ManagedList({
  spec,
  items,
  onChange,
  onNotice,
}: {
  spec: ListSpec;
  items: ListItem[];
  onChange: (items: ListItem[]) => void;
  /** Toast hook for saved or blocked actions. */
  onNotice?: (title: string, description?: string) => void;
}) {
  const [q, setQ] = React.useState("");
  const [showInactive, setShowInactive] = React.useState(false);
  const [editing, setEditing] = React.useState<string | null>(null);
  const [draft, setDraft] = React.useState<Pick<ListItem, "name" | "short" | "parent">>({ name: "" });
  const [tried, setTried] = React.useState(false);

  const problems = editing ? listItemProblems(draft, items, spec, editing === "new" ? undefined : editing) : [];
  const visible = items
    .filter((i) => showInactive || i.active)
    .filter((i) => !q.trim() || `${i.name} ${i.short ?? ""} ${i.parent ?? ""}`.toLowerCase().includes(q.trim().toLowerCase()))
    .sort((a, b) => (a.parent ?? "").localeCompare(b.parent ?? "") || a.name.localeCompare(b.name));
  const inactive = items.filter((i) => !i.active).length;

  const start = (i?: ListItem) => {
    setEditing(i?.id ?? "new");
    setDraft(i ? { name: i.name, short: i.short, parent: i.parent } : { name: "", short: "", parent: "" });
    setTried(false);
  };
  const save = () => {
    setTried(true);
    if (problems.length) return;
    const clean = { name: draft.name.trim(), short: draft.short?.trim() || undefined, parent: draft.parent || undefined };
    if (editing === "new") {
      onChange([...items, { id: `n${Date.now()}`, ...clean, active: true, uses: 0 }]);
      onNotice?.(`${clean.name} added`);
    } else {
      const before = items.find((i) => i.id === editing);
      onChange(items.map((i) => (i.id === editing ? { ...i, ...clean } : i)));
      if (before && before.name !== clean.name && before.uses) onNotice?.(`Renamed to ${clean.name}`, `The ${plural(before.uses, spec.usedBy.replace(/s$/, ""), spec.usedBy)} using it show the new name.`);
      else onNotice?.(`${clean.name} saved`);
    }
    setEditing(null);
  };
  const toggle = (i: ListItem) => {
    onChange(items.map((x) => (x.id === i.id ? { ...x, active: !x.active } : x)));
    onNotice?.(i.active ? `${i.name} deactivated` : `${i.name} reactivated`, i.active ? `No longer offered for new ${spec.usedBy}; existing ones keep it.` : undefined);
  };
  const remove = (i: ListItem) => {
    if (i.uses) {
      onNotice?.(`Can't delete ${i.name}`, `${plural(i.uses, spec.usedBy.replace(/s$/, ""), spec.usedBy)} use it. Deactivate it instead, so they keep their ${spec.noun}.`);
      return;
    }
    onChange(items.filter((x) => x.id !== i.id));
    onNotice?.(`${i.name} deleted`);
  };

  const form = (
    <div className="space-y-2 rounded-md border border-[var(--color-border-panel)] bg-muted/30 p-3">
      <div className="flex flex-wrap gap-2">
        {spec.parents ? (
          <NativeSelect aria-label={spec.parents.label} value={draft.parent ?? ""} onChange={(e) => setDraft({ ...draft, parent: e.target.value })} aria-invalid={tried && !draft.parent}>
            <NativeSelectOption value="">{spec.parents.label}…</NativeSelectOption>
            {spec.parents.options.map((o) => (
              <NativeSelectOption key={o} value={o}>
                {o}
              </NativeSelectOption>
            ))}
          </NativeSelect>
        ) : null}
        <Input
          aria-label={`${spec.noun} name`}
          placeholder="Name"
          className="min-w-40 flex-1"
          autoFocus
          value={draft.name}
          onChange={(e) => setDraft({ ...draft, name: e.target.value })}
          onKeyDown={(e) => {
            if (e.key === "Enter") save();
            if (e.key === "Escape") setEditing(null);
          }}
          aria-invalid={tried && problems.length > 0}
        />
        {spec.short ? (
          <Input aria-label={spec.short.label} placeholder={spec.short.label} className="w-28" value={draft.short ?? ""} onChange={(e) => setDraft({ ...draft, short: e.target.value })} />
        ) : null}
        <Button size="sm" className="self-center" onClick={save}>
          {editing === "new" ? "Add" : "Save"}
        </Button>
        <Button size="sm" variant="ghost" className="self-center" onClick={() => setEditing(null)}>
          Cancel
        </Button>
      </div>
      {tried && problems.length ? (
        <ul className="list-disc pl-5 text-xs text-status-error-text">
          {problems.map((x) => (
            <li key={x}>{x}</li>
          ))}
        </ul>
      ) : null}
    </div>
  );

  return (
    <div className="space-y-3">
      <div className="flex flex-wrap items-center gap-2">
        <div className="relative min-w-48 flex-1">
          <SearchIcon className="pointer-events-none absolute top-1/2 left-2.5 size-4 -translate-y-1/2 text-muted-foreground" aria-hidden />
          <Input aria-label={`Search ${spec.noun} list`} placeholder="Search" className="pl-8" value={q} onChange={(e) => setQ(e.target.value)} />
        </div>
        {inactive ? (
          <label className="flex items-center gap-1.5 text-xs text-muted-foreground">
            <Checkbox checked={showInactive} onCheckedChange={(v) => setShowInactive(!!v)} />
            Show {inactive} inactive
          </label>
        ) : null}
        <Button size="sm" variant="outline" onClick={() => start()} disabled={editing === "new"}>
          <PlusIcon data-icon="inline-start" />
          Add {spec.noun}
        </Button>
      </div>
      {editing === "new" ? form : null}
      <ul className="divide-y divide-[var(--color-border-subtle)] rounded-md border border-[var(--color-border-panel)] bg-card">
        {visible.map((i) =>
          editing === i.id ? (
            <li key={i.id} className="p-2">
              {form}
            </li>
          ) : (
            <li key={i.id} className={cn("flex flex-wrap items-center gap-x-3 gap-y-1 px-3 py-2 text-sm", !i.active && "text-muted-foreground")}>
              <span className="min-w-0 flex-1">
                {i.parent ? <span className="text-xs text-muted-foreground">{i.parent} › </span> : null}
                <span className="font-medium">{i.name}</span>
                {i.short ? <span className="ml-1.5 font-mono text-xs text-muted-foreground">{i.short}</span> : null}
                {!i.active ? <span className="ml-2 text-xs">Inactive</span> : null}
              </span>
              <span className="text-xs text-muted-foreground tabular-nums">{i.uses ? plural(i.uses, spec.usedBy.replace(/s$/, ""), spec.usedBy) : "Not used"}</span>
              <span className="flex gap-1">
                <Button size="icon-sm" variant="ghost" aria-label={`Rename ${i.name}`} onClick={() => start(i)}>
                  <PencilIcon />
                </Button>
                <Button size="sm" variant="ghost" onClick={() => toggle(i)}>
                  {i.active ? "Deactivate" : "Reactivate"}
                </Button>
                <Button size="sm" variant="ghost" className="text-status-error-text" onClick={() => remove(i)}>
                  Delete
                </Button>
              </span>
            </li>
          ),
        )}
        {!visible.length ? <li className="px-3 py-6 text-center text-sm text-muted-foreground">{q ? `No ${spec.noun} matches “${q}”.` : `No ${spec.noun} yet. Add the first one.`}</li> : null}
      </ul>
    </div>
  );
}
