"use client";

import { useEffect, useRef, useState, useTransition } from "react";
import { addItem, removeItem, renameItem, updatePerson } from "@/app/actions";
import type { Item, ItemKind } from "@/db/schema";
import { KNOWN_ALLERGENS, suggestionsFor } from "@/lib/allergens";
import { KIND_ORDER, KINDS } from "@/lib/kinds";
import { norm } from "@/lib/text";
import { Chip, TONE } from "./Chip";

type Ref = { personId: string } | { token: string };
type Props = {
  personRef: Ref;
  name: string;
  notes: string;
  items: Item[];
  mode: "casa" | "amico";
  editing: boolean;
};

const tempId = () => `tmp-${Math.random().toString(36).slice(2)}`;

export function PersonCard({ personRef, name: initialName, notes: initialNotes, items: initialItems, mode, editing }: Props) {
  const [items, setItems] = useState(initialItems);
  const [name, setName] = useState(initialName);
  const [notes, setNotes] = useState(initialNotes);
  const [toast, setToast] = useState<{ text: string; error?: boolean } | null>(null);
  const [, startTransition] = useTransition();

  // Quando il server manda dati aggiornati, ripartiamo da quelli
  const [synced, setSynced] = useState({ initialItems, initialName, initialNotes });
  if (synced.initialItems !== initialItems || synced.initialName !== initialName || synced.initialNotes !== initialNotes) {
    setSynced({ initialItems, initialName, initialNotes });
    setItems(initialItems);
    setName(initialName);
    setNotes(initialNotes);
  }

  useEffect(() => {
    if (!toast) return;
    const t = setTimeout(() => setToast(null), toast.error ? 4000 : 1400);
    return () => clearTimeout(t);
  }, [toast]);

  function run(task: () => Promise<unknown>, rollback: () => void) {
    startTransition(async () => {
      try {
        await task();
        setToast({ text: "Salvato" });
      } catch {
        rollback();
        setToast({ text: "Non sono riuscito a salvare. Controlla la connessione e riprova.", error: true });
      }
    });
  }

  function add(kind: ItemKind, label: string, parentId: string | null = null) {
    const clean = label.trim();
    if (!clean) return;
    const siblings = items.filter((i) => i.kind === kind && i.parentId === parentId);
    if (siblings.some((i) => norm(i.label) === norm(clean))) return;
    const id = tempId();
    const draft: Item = { id, personId: "", kind, label: clean, parentId, sort: Number.MAX_SAFE_INTEGER };
    setItems((cur) => [...cur, draft]);
    run(
      async () => {
        const realId = await addItem(personRef, { kind, label: clean, parentId });
        setItems((cur) => cur.map((i) => (i.id === id ? { ...i, id: realId } : i)));
      },
      () => setItems((cur) => cur.filter((i) => i.id !== id)),
    );
  }

  function remove(item: Item) {
    const before = items;
    setItems((cur) => cur.filter((i) => i.id !== item.id && i.parentId !== item.id));
    run(() => removeItem(personRef, item.id), () => setItems(before));
  }

  function rename(item: Item, label: string) {
    const clean = label.trim();
    if (!clean || clean === item.label) return;
    const before = items;
    setItems((cur) => cur.map((i) => (i.id === item.id ? { ...i, label: clean } : i)));
    run(() => renameItem(personRef, item.id, clean), () => setItems(before));
  }

  function savePerson(data: { name?: string; notes?: string }) {
    if (data.name !== undefined && (!data.name.trim() || data.name.trim() === initialName)) {
      if (!data.name.trim()) setName(initialName);
      return;
    }
    if (data.notes !== undefined && data.notes.trim() === initialNotes) return;
    run(
      () => updatePerson(personRef, data),
      () => {
        setName(initialName);
        setNotes(initialNotes);
      },
    );
  }

  const friend = mode === "amico";

  return (
    <div className="flex flex-col gap-4">
      {editing && (
        <div className="flex flex-col gap-3 rounded-card bg-surface p-4">
          <label className="flex flex-col gap-1">
            <span className="text-xs font-semibold tracking-wider text-muted uppercase">{friend ? "Il tuo nome" : "Nome"}</span>
            <input
              id="person-name"
              value={name}
              onChange={(e) => setName(e.target.value)}
              onBlur={() => savePerson({ name })}
              maxLength={60}
              className="rounded-field border border-line bg-bg px-3 py-2.5 text-lg font-semibold"
            />
          </label>
          <label className="flex flex-col gap-1">
            <span className="text-xs font-semibold tracking-wider text-muted uppercase">Note</span>
            <textarea
              id="person-notes"
              value={notes}
              onChange={(e) => setNotes(e.target.value)}
              onBlur={() => savePerson({ notes })}
              maxLength={500}
              rows={2}
              placeholder={friend ? "Qualcos'altro da sapere? (es. niente carne, poco piccante)" : "Es. vegetariano, mangia di tutto…"}
              className="resize-none rounded-field border border-line bg-bg px-3 py-2.5"
            />
          </label>
        </div>
      )}

      {!editing && notes && <p className="rounded-card bg-surface px-4 py-3 text-[15px] text-ink">{notes}</p>}

      {KIND_ORDER.map((kind) => (
        <Section
          key={kind}
          kind={kind}
          title={friend ? KINDS[kind].friendTitle : KINDS[kind].title}
          hint={friend ? KINDS[kind].friendHint : KINDS[kind].hint}
          items={items.filter((i) => i.kind === kind)}
          editing={editing}
          onAdd={add}
          onRemove={remove}
          onRename={rename}
        />
      ))}

      <div
        role="status"
        aria-live="polite"
        className={`safe-bottom pointer-events-none fixed inset-x-0 bottom-4 z-50 flex justify-center px-4 transition-all duration-200 ${
          toast ? "translate-y-0 opacity-100" : "translate-y-3 opacity-0"
        }`}
      >
        {toast && (
          <span
            className={`rounded-chip px-4 py-2 text-sm font-semibold shadow-lg ${toast.error ? "bg-all text-white" : "bg-accent text-accent-ink"}`}
          >
            {toast.text}
          </span>
        )}
      </div>
    </div>
  );
}

function Section({
  kind,
  title,
  hint,
  items,
  editing,
  onAdd,
  onRemove,
  onRename,
}: {
  kind: ItemKind;
  title: string;
  hint: string;
  items: Item[];
  editing: boolean;
  onAdd: (kind: ItemKind, label: string, parentId?: string | null) => void;
  onRemove: (item: Item) => void;
  onRename: (item: Item, label: string) => void;
}) {
  const meta = KINDS[kind];
  const tops = items.filter((i) => !i.parentId);
  const empty = tops.length === 0;

  if (!editing && empty) {
    return (
      <section className="flex items-center gap-3 px-1 text-sm text-muted">
        <span className={`size-2 rounded-full ${TONE[kind].dot} opacity-40`} />
        <span>
          {title}: <span className="italic">nessuna voce</span>
        </span>
      </section>
    );
  }

  return (
    <section
      className={`flex flex-col gap-3 rounded-card bg-surface p-4 ${kind === "allergy" && !empty ? "ring-2 ring-all" : ""}`}
      aria-labelledby={`sec-${kind}`}
    >
      <h2 id={`sec-${kind}`} className={`flex items-center gap-2 text-[13px] font-bold tracking-wider uppercase ${TONE[kind].text}`}>
        <span className={`size-2.5 rounded-full ${TONE[kind].dot}`} />
        {title}
        {!empty && <span className="font-semibold text-muted tabular-nums">{tops.length}</span>}
      </h2>

      {editing && empty && <p className="text-sm text-muted">{hint}</p>}

      {meta.grouped ? (
        <div className="flex flex-col gap-4">
          {tops.map((group) => (
            <Group
              key={group.id}
              kind={kind}
              group={group}
              foods={items.filter((i) => i.parentId === group.id)}
              editing={editing}
              onAdd={onAdd}
              onRemove={onRemove}
              onRename={onRename}
            />
          ))}
        </div>
      ) : (
        !empty && (
          <div className="flex flex-wrap gap-2">
            {tops.map((i) => (
              <Chip key={i.id} kind={kind} onRemove={editing ? () => onRemove(i) : undefined} pending={i.id.startsWith("tmp-")}>
                {i.label}
              </Chip>
            ))}
          </div>
        )
      )}

      {editing && meta.grouped && !empty && <hr className="border-t-2 border-dashed border-line" />}
      {editing && meta.grouped && !empty && (
        <p className="-mb-1 text-xs font-extrabold tracking-wider text-muted uppercase">{kind === "allergy" ? "Un'altra allergia" : "Un'altra intolleranza"}</p>
      )}
      {editing && (
        <AddInput
          id={`add-${kind}`}
          placeholder={meta.grouped ? (kind === "allergy" ? "Aggiungi allergia, es. Nichel" : "Aggiungi intolleranza, es. Lattosio") : "Aggiungi un cibo o un piatto"}
          list={meta.grouped ? "known-allergens" : undefined}
          onSubmit={(v) => v.split(",").forEach((part) => onAdd(kind, part))}
        />
      )}
      {editing && meta.grouped && (
        <datalist id="known-allergens">
          {KNOWN_ALLERGENS.map((a) => (
            <option key={a} value={a} />
          ))}
        </datalist>
      )}
    </section>
  );
}

function Group({
  kind,
  group,
  foods,
  editing,
  onAdd,
  onRemove,
  onRename,
}: {
  kind: ItemKind;
  group: Item;
  foods: Item[];
  editing: boolean;
  onAdd: (kind: ItemKind, label: string, parentId?: string | null) => void;
  onRemove: (item: Item) => void;
  onRename: (item: Item, label: string) => void;
}) {
  const [confirm, setConfirm] = useState(false);
  const [label, setLabel] = useState(group.label);
  const [showAll, setShowAll] = useState(false);
  const [syncedLabel, setSyncedLabel] = useState(group.label);
  if (syncedLabel !== group.label) {
    setSyncedLabel(group.label);
    setLabel(group.label);
  }

  const have = new Set(foods.map((f) => norm(f.label)));
  const suggestions = suggestionsFor(group.label).filter((s) => !have.has(norm(s)));
  const visible = showAll ? suggestions : suggestions.slice(0, 10);
  const saved = !group.id.startsWith("tmp-");

  return (
    <div className="flex flex-col gap-2">
      <div className="flex items-center gap-2">
        {editing ? (
          <input
            aria-label="Nome dell'elemento"
            value={label}
            onChange={(e) => setLabel(e.target.value)}
            onBlur={() => (label.trim() ? onRename(group, label) : setLabel(group.label))}
            className="min-w-0 flex-1 rounded-field border border-transparent bg-transparent px-1 py-0.5 font-display text-xl hover:border-line focus:border-line"
          />
        ) : (
          <h3 className="font-display text-xl">{group.label}</h3>
        )}
        {editing &&
          (confirm ? (
            <span className="flex items-center gap-1 text-sm">
              <button type="button" onClick={() => onRemove(group)} className="rounded-chip bg-all px-3 py-1.5 font-semibold text-white">
                Elimina
              </button>
              <button type="button" onClick={() => setConfirm(false)} className="rounded-chip px-3 py-1.5 font-semibold text-muted">
                Annulla
              </button>
            </span>
          ) : (
            <button type="button" onClick={() => setConfirm(true)} className="rounded-chip px-3 py-1.5 text-sm font-semibold text-muted">
              Togli
            </button>
          ))}
      </div>

      {foods.length > 0 ? (
        <div className="flex flex-wrap gap-2">
          {foods.map((f) => (
            <Chip key={f.id} kind={kind} onRemove={editing ? () => onRemove(f) : undefined} pending={f.id.startsWith("tmp-")}>
              {f.label}
            </Chip>
          ))}
        </div>
      ) : (
        !editing && <p className="text-sm text-muted">Nessun cibo specifico indicato.</p>
      )}

      {editing && saved && (
        <>
          {visible.length > 0 && (
            <div className="flex flex-col gap-2">
              <p className="text-xs text-muted">Cibi che di solito lo contengono, tocca per aggiungere:</p>
              <div className="flex flex-wrap gap-1.5">
                {visible.map((s) => (
                  <button
                    key={s}
                    type="button"
                    onClick={() => onAdd(kind, s, group.id)}
                    className="min-h-8 rounded-chip border border-dashed border-line px-2.5 py-1 text-sm text-muted hover:border-ink hover:text-ink active:scale-95"
                  >
                    + {s}
                  </button>
                ))}
                {suggestions.length > visible.length && (
                  <button type="button" onClick={() => setShowAll(true)} className="px-2 text-sm font-semibold text-ink underline">
                    altri {suggestions.length - visible.length}
                  </button>
                )}
              </div>
            </div>
          )}
          <AddInput id={`add-food-${group.id}`} placeholder={`Altro cibo con ${group.label.toLowerCase()}`} small onSubmit={(v) => v.split(",").forEach((p) => onAdd(kind, p, group.id))} />
        </>
      )}
    </div>
  );
}

function AddInput({
  id,
  placeholder,
  onSubmit,
  list,
  small,
}: {
  id: string;
  placeholder: string;
  onSubmit: (value: string) => void;
  list?: string;
  small?: boolean;
}) {
  const [value, setValue] = useState("");
  const ref = useRef<HTMLInputElement>(null);
  return (
    <form
      className="flex gap-2"
      onSubmit={(e) => {
        e.preventDefault();
        if (!value.trim()) return;
        onSubmit(value);
        setValue("");
        ref.current?.focus();
      }}
    >
      <input
        ref={ref}
        id={id}
        value={value}
        onChange={(e) => setValue(e.target.value)}
        placeholder={placeholder}
        list={list}
        enterKeyHint="done"
        autoComplete="off"
        maxLength={80}
        className={`min-w-0 flex-1 rounded-field border border-line bg-bg px-3 ${small ? "py-2 text-base" : "py-2.5 text-base"}`}
      />
      <button
        type="submit"
        disabled={!value.trim()}
        className="shrink-0 rounded-field bg-accent px-4 font-semibold text-accent-ink transition-opacity disabled:opacity-30"
      >
        Aggiungi
      </button>
    </form>
  );
}
