function Item({
  item,
  idx,
  chain,
  mongo_ok,
  brave_ok = false,
  expandedItem,
  setExpandedItem,
  deleteMatch,
  setRefreshKey,
  onSaved,
  onMatchSaved,
  receiptId,
  isItemsPage = false,
  readOnly = false,
  savingItem,
  setSavingItem,
}) {
  const match = item.match;
  const suggested = match?.suggested_from;
  const isMatched = match?.off_found && !suggested;
  const barcodeOnly = match?.barcode && !match?.off_found && !suggested;
  const hasManualInfo = match?.manual_info;
  const isWeighted = match?.is_weighted;
  const isExpanded = expandedItem === item.name;
  const [isSearchOpen, setIsSearchOpen] = React.useState(false);
  const [showReceipts, setShowReceipts] = React.useState(false);
  const [isInfoOpen, setIsInfoOpen] = React.useState(false);
  const [infoFields, setInfoFields] = React.useState({
    product_name: "",
    brands: "",
    category: "",
    quantity: "",
  });
  const [infoSaving, setInfoSaving] = React.useState(false);
  const [autoFilling, setAutoFilling] = React.useState(false);
  const [autoFillError, setAutoFillError] = React.useState(null);

  const hasExpandedTools =
    match?.off_found || hasManualInfo || item.receipt_occurrences?.length > 0;

  async function autoFill() {
    setAutoFilling(true);
    setAutoFillError(null);
    try {
      const r = await fetch("/api/item-info/autofill", {
        method: "POST",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify({
          item_name: item.name,
          barcode: match?.barcode || match?.raw_barcode || undefined,
          chain: isWeighted ? chain || undefined : undefined,
        }),
      });
      if (!r.ok) {
        const d = await r.json().catch(() => ({}));
        setAutoFillError(d.detail || "Auto-compilazione fallita");
        return;
      }
      const d = await r.json();
      setInfoFields((f) => ({
        product_name: d.product_name || f.product_name,
        brands: d.brands || f.brands,
        category: d.category || f.category,
        quantity: d.quantity || f.quantity,
      }));
    } catch (e) {
      setAutoFillError("Errore di rete");
    } finally {
      setAutoFilling(false);
    }
  }

  async function saveItemInfo() {
    setInfoSaving(true);
    setSavingItem?.(item.name);
    try {
      await fetch("/api/item-info", {
        method: "POST",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify({
          item_name: item.name,
          chain,
          receipt_id: receiptId || item.receipt_id,
          ...infoFields,
        }),
      });
      setIsInfoOpen(false);
      onSaved?.(item.name);
      if (onMatchSaved) onMatchSaved(item.name);
      else setRefreshKey?.((k) => k + 1);
    } catch (e) {
      // no-op
    } finally {
      setInfoSaving(false);
      setSavingItem?.(null);
    }
  }

  return (
    <div
      id={`item-${item.name}`}
      className="bg-white rounded-2xl shadow-sm overflow-hidden"
    >
      {/* Main row */}
      <div
        className={`p-4 ${match ? "cursor-pointer active:bg-gray-50" : ""}`}
        onClick={() => match && setExpandedItem(isExpanded ? null : item.name)}
      >
        <div className="flex justify-between items-start gap-2 mb-2">
          {(match?.off_found || item.display_label) && (
            <img
              src={item.display_label ? PLACEHOLDER_IMG : offImg(match.barcode)}
              alt={item.display_label || match.product_name}
              onError={(e) => {
                e.target.onerror = null;
                e.target.src = PLACEHOLDER_IMG;
              }}
              className="w-24 h-24 object-contain rounded-lg border border-gray-100 bg-white flex-shrink-0"
            />
          )}
          <div className="flex-1 min-w-0">
            {savingItem === item.name ? (
              <div className="flex items-center gap-2">
                <div className="w-4 h-4 border-2 border-[#2d7a3a] border-t-transparent rounded-full animate-spin" />
                <span className="text-sm text-gray-500">Salvataggio...</span>
              </div>
            ) : match ? (
              <>
                <div className="font-semibold text-gray-900 text-sm leading-snug">
                  {match.product_name || item.name}
                  {match.brands && (
                    <span className="font-normal text-gray-400 text-xs ml-1.5">
                      {match.brands}
                    </span>
                  )}
                  {match.quantity && (
                    <span className="font-normal text-gray-400 text-xs ml-1.5">
                      {match.quantity}
                    </span>
                  )}
                </div>
                <div className="text-xs text-gray-400 mt-0.5">{item.name}</div>
              </>
            ) : item.display_label ? (
              <>
                <div className="font-semibold text-gray-900 text-sm leading-snug">
                  {item.display_label}
                </div>
                <div className="text-xs text-gray-400 mt-0.5">{item.name}</div>
              </>
            ) : (
              <div className="font-semibold text-gray-900 text-sm leading-snug">
                {item.name}
              </div>
            )}
          </div>
          <div className="text-right flex-shrink-0">
            <div className="font-semibold text-gray-800 text-sm">
              €{fmt(isItemsPage ? item.total_eur : item.amount_eur)}
            </div>
            {isItemsPage ? (
              <div className="text-xs text-gray-400">{item.count}×</div>
            ) : (
              <div className="flex gap-1.5 justify-end">
                {item.quantity > 1 && (
                  <span className="text-xs font-medium text-gray-600">
                    {item.quantity}× €{fmt(item.amount_eur / item.quantity)}
                  </span>
                )}
                <span className="text-xs text-gray-400">
                  {Math.round(item.vat_pct)}% IVA
                </span>
              </div>
            )}
          </div>
        </div>
        <div className="flex items-center justify-between gap-2 mt-2">
          <div className="flex items-center gap-2 min-w-0">
            {item.meta_item && (
              <span className="px-2 py-0.5 rounded text-xs font-medium bg-blue-50 text-blue-600">
                {item.meta_item}
              </span>
            )}
            {barcodeOnly ? (
              <span className="px-2 py-0.5 rounded text-xs font-bold bg-yellow-100 text-yellow-700">
                Solo codice a barre
              </span>
            ) : hasManualInfo ? (
              <span className="px-2 py-0.5 rounded text-xs font-bold bg-blue-100 text-blue-700">
                Manuale
              </span>
            ) : suggested ? (
              <span className="px-2 py-0.5 rounded text-xs font-bold bg-purple-100 text-purple-700">
                from {suggested}
              </span>
            ) : item.display_label ? (
              <span className="px-2 py-0.5 rounded text-xs font-bold bg-green-100 text-green-700">
                Auto
              </span>
            ) : !isMatched ? (
              <span className="px-2 py-0.5 rounded text-xs font-bold bg-gray-100 text-gray-500">
                Non abbinato
              </span>
            ) : null}
            {(item.chains ?? (item.chain ? [item.chain] : [])).map((c) => (
              <span
                key={c}
                className="px-2 py-0.5 rounded text-xs font-bold bg-green-50 text-green-600"
              >
                {c}
              </span>
            ))}
          </div>
          <div className="flex items-center gap-1.5 flex-shrink-0">
            {match?.mediterranean_score != null && (
              <span
                className="inline-flex items-center justify-center w-7 h-7 rounded-full text-xs font-bold text-white flex-shrink-0"
                style={{
                  background: mediterraneanScoreColor(
                    match.mediterranean_score.score,
                  ),
                }}
                title={`Score: ${match.mediterranean_score.score}/100 – ${match.mediterranean_score.label_it}`}
              >
                {match.mediterranean_score.score}
              </span>
            )}
            {isItemsPage && match?.off_found && (
              <a
                href={`#/item/${match.barcode}`}
                onClick={(e) => e.stopPropagation()}
                className="ml-0.5 text-gray-400 hover:text-[#2d7a3a] transition-colors"
                title="Scheda prodotto"
              >
                <svg
                  className="w-4 h-4"
                  fill="none"
                  stroke="currentColor"
                  viewBox="0 0 24 24"
                >
                  <path
                    strokeLinecap="round"
                    strokeLinejoin="round"
                    strokeWidth={2}
                    d="M9 5l7 7-7 7"
                  />
                </svg>
              </a>
            )}
          </div>
        </div>
      </div>

      {/* Expanded section */}
      {isExpanded && (
        <div className="relative border-t border-gray-50">
          {/* Top-right buttons */}
          {hasExpandedTools && (
            <div
              className="absolute top-2 right-2 flex gap-1.5 z-10"
              onClick={(e) => e.stopPropagation()}
            >
              {item.receipt_occurrences?.length > 0 && (
                <button
                  onClick={() => setShowReceipts(true)}
                  className="px-2.5 py-1 border border-gray-200 text-gray-500 text-xs font-semibold rounded-lg active:bg-gray-100"
                >
                  {item.receipt_occurrences.length !== 1
                    ? `${item.receipt_occurrences.length} scontrini`
                    : "1 scontrino"}
                </button>
              )}
              {match?.off_found && (
                <a
                  href={`#/item/${match.barcode}`}
                  className="px-2.5 py-1 border border-gray-200 text-gray-500 text-xs font-semibold rounded-lg active:bg-gray-100"
                >
                  Scheda
                </a>
              )}
            </div>
          )}

          {item.price_stats && (
            <div className="px-4 py-3">
              <div className="text-xs font-semibold text-gray-400 uppercase tracking-wide mb-2">
                Variazione prezzo
              </div>
              <div className="flex gap-6">
                <div>
                  <div className="text-xs text-gray-400">Min</div>
                  <div className="font-semibold text-sm text-gray-800">
                    €{fmt(item.price_stats.min)}
                  </div>
                </div>
                <div>
                  <div className="text-xs text-gray-400">Med</div>
                  <div className="font-semibold text-sm text-gray-800">
                    €{fmt(item.price_stats.avg)}
                  </div>
                </div>
                <div>
                  <div className="text-xs text-gray-400">Max</div>
                  <div className="font-semibold text-sm text-gray-800">
                    €{fmt(item.price_stats.max)}
                  </div>
                </div>
              </div>
            </div>
          )}
          <NutritionPanel match={match} />
        </div>
      )}

      {/* Action buttons */}
      {!readOnly && !item.display_label && !isMatched && (
        <div
          className="px-4 pb-3 flex gap-2"
          onClick={(e) => e.stopPropagation()}
        >
          <button
            onClick={() => setIsSearchOpen(true)}
            className="px-3 py-1.5 border border-gray-200 text-gray-600 text-xs font-semibold rounded-lg active:bg-gray-100"
          >
            {suggested
              ? "Conferma"
              : match?.barcode
                ? "Modifica"
                : "Trova abbinamento"}
          </button>
          {!suggested && (
            <button
              onClick={() => {
                setInfoFields({
                  product_name: match?.manual_product_name || "",
                  brands: match?.manual_brands || "",
                  category: match?.manual_category || "",
                  quantity: match?.manual_quantity || "",
                });
                setAutoFillError(null);
                setIsInfoOpen(true);
              }}
              className="px-3 py-1.5 border border-gray-200 text-gray-600 text-xs font-semibold rounded-lg active:bg-gray-100"
            >
              {hasManualInfo ? "Modifica info" : "Aggiungi info"}
            </button>
          )}
          {match?.barcode && !suggested && (
            <button
              onClick={() => deleteMatch(item.name)}
              className="px-3 py-1.5 bg-red-500 text-white text-xs font-semibold rounded-lg active:bg-red-700"
            >
              Rimuovi
            </button>
          )}
        </div>
      )}

      {/* Receipts modal */}
      {showReceipts &&
        ReactDOM.createPortal(
          <div
            className="fixed inset-0 z-50 flex items-center justify-center bg-black/40"
            onClick={() => setShowReceipts(false)}
          >
            <div
              className="bg-white rounded-2xl shadow-xl w-full max-w-lg mx-4 max-h-[80vh] overflow-hidden flex flex-col"
              onClick={(e) => e.stopPropagation()}
            >
              <div className="flex items-center justify-between px-4 py-3 border-b border-gray-100">
                <span className="font-semibold text-sm text-gray-800 truncate">
                  {item.name}
                </span>
                <button
                  onClick={() => setShowReceipts(false)}
                  className="text-gray-400 text-lg leading-none ml-2"
                >
                  ✕
                </button>
              </div>
              <div className="overflow-y-auto divide-y divide-gray-50 flex-1 min-h-0">
                {item.receipt_occurrences.map((occ, i) => (
                  <a
                    key={i}
                    href={`#/receipts/${occ.id}`}
                    onClick={() => setShowReceipts(false)}
                    className="flex items-center justify-between px-4 py-3 active:bg-gray-50"
                  >
                    <div>
                      <div className="text-sm font-medium text-gray-800 truncate">
                        {occ.store || occ.id}
                      </div>
                      <div className="text-xs text-gray-400">{occ.date}</div>
                    </div>
                    <div className="text-sm font-semibold text-gray-700 ml-4 flex-shrink-0">
                      €{fmt(occ.amount_eur)}
                    </div>
                  </a>
                ))}
              </div>
            </div>
          </div>,
          document.body,
        )}

      {/* Manual info modal */}
      {isInfoOpen &&
        ReactDOM.createPortal(
          <div
            className="fixed inset-0 z-50 flex items-end sm:items-center justify-center bg-black/50"
            onClick={() => setIsInfoOpen(false)}
          >
            <div
              className="bg-white rounded-t-2xl sm:rounded-2xl shadow-xl w-full max-w-lg sm:mx-4"
              onClick={(e) => e.stopPropagation()}
            >
              <div className="flex items-center justify-between px-4 py-3 border-b border-gray-100">
                <span className="font-semibold text-sm text-gray-700 truncate">
                  {item.name}
                </span>
                <div className="flex items-center gap-2 ml-2 flex-shrink-0">
                  {brave_ok && (
                    <button
                      onClick={autoFill}
                      disabled={autoFilling}
                      className="px-2.5 py-1 text-xs font-semibold rounded-lg border border-gray-200 text-gray-600 disabled:opacity-60 active:bg-gray-100"
                    >
                      {autoFilling ? "Ricerca…" : "Auto-compila"}
                    </button>
                  )}
                  <button
                    onClick={() => setIsInfoOpen(false)}
                    className="text-gray-400 text-lg leading-none"
                  >
                    ✕
                  </button>
                </div>
              </div>
              <div className="p-4 space-y-3">
                {autoFillError && (
                  <p className="text-xs text-red-500">{autoFillError}</p>
                )}
                {[
                  {
                    key: "product_name",
                    label: "Nome prodotto",
                    placeholder: "es. Latte Intero Bio",
                  },
                  {
                    key: "brands",
                    label: "Marca",
                    placeholder: "es. Granarolo",
                  },
                  {
                    key: "quantity",
                    label: "Quantità",
                    placeholder: "es. 500g",
                  },
                ].map(({ key, label, placeholder }) => (
                  <div key={key}>
                    <label className="block text-xs font-semibold text-gray-500 mb-1">
                      {label}
                    </label>
                    <input
                      value={infoFields[key]}
                      onChange={(e) =>
                        setInfoFields((f) => ({ ...f, [key]: e.target.value }))
                      }
                      placeholder={placeholder}
                      className="w-full px-3 py-2 text-sm border border-gray-200 rounded-lg focus:outline-none focus:ring-2 focus:ring-[#2d7a3a]/30 focus:border-[#2d7a3a]"
                    />
                  </div>
                ))}
                <div>
                  <label className="block text-xs font-semibold text-gray-500 mb-1">
                    Categoria
                  </label>
                  <AutocompleteInput
                    placeholder="es. Tè"
                    value={infoFields.category}
                    onChange={(v) =>
                      setInfoFields((f) => ({ ...f, category: v }))
                    }
                    field="category"
                  />
                </div>
                <div className="flex justify-end gap-2 pt-1">
                  <button
                    onClick={() => setIsInfoOpen(false)}
                    className="px-4 py-2 rounded-lg text-sm font-medium text-gray-600 hover:bg-gray-100"
                  >
                    Annulla
                  </button>
                  <button
                    onClick={saveItemInfo}
                    disabled={infoSaving}
                    className="px-4 py-2 rounded-lg text-sm font-medium text-white bg-[#2d7a3a] disabled:opacity-60 active:bg-[#236130]"
                  >
                    {infoSaving ? "Salvataggio…" : "Salva"}
                  </button>
                </div>
              </div>
            </div>
          </div>,
          document.body,
        )}

      {/* Search modal */}
      {isSearchOpen &&
        ReactDOM.createPortal(
          <div
            className="fixed inset-0 z-50 flex items-end sm:items-center justify-center bg-black/50"
            onClick={() => setIsSearchOpen(false)}
          >
            <div
              className="bg-white rounded-t-2xl sm:rounded-2xl shadow-xl w-full max-w-lg sm:mx-4 max-h-[90vh] flex flex-col"
              onClick={(e) => e.stopPropagation()}
            >
              <div className="flex items-center justify-between px-4 py-3 border-b border-gray-100 flex-shrink-0">
                <span className="font-semibold text-sm text-gray-700 truncate">
                  {item.name}
                </span>
                <button
                  onClick={() => setIsSearchOpen(false)}
                  className="text-gray-400 text-lg leading-none ml-2"
                >
                  ✕
                </button>
              </div>
              <div className="overflow-y-auto p-4">
                <SearchWidget
                  itemName={item.name}
                  chain={chain}
                  receiptId={
                    receiptId ||
                    item.receipt_id ||
                    item.receipt_occurrences?.[0]?.id
                  }
                  mongoOk={mongo_ok}
                  onSaved={() => {
                    setIsSearchOpen(false);
                    setSavingItem?.(item.name);
                    onSaved?.(item.name);
                    if (onMatchSaved) onMatchSaved(item.name);
                    else setRefreshKey?.((k) => k + 1);
                  }}
                  onCancel={() => setIsSearchOpen(false)}
                />
              </div>
            </div>
          </div>,
          document.body,
        )}
    </div>
  );
}
