/* =========================================================
   ANTARES — Prima nota (db/25)
   Tutti i movimenti di conti e carte: con cosa è stata pagata una spesa,
   dove è entrato un incasso, se c'è la fattura, a quale progetto va
   (o se è una spesa generale). Gli estratti di banca, carte e SumUp si
   caricano da file; i movimenti si abbinano alle fatture.
   Logica senza interfaccia in js/primaNota.js.
   ========================================================= */

const { useState: useS6, useMemo: useM6 } = React;
const { fmt: fmt6, MESI_FULL: MESI6 } = window.AntaresData;
const Icon6 = window.AntaresScreens1.Icon;
const { Modal: Modal6, EmptyState: Empty6, SelectCategoria: SelCat6, progettoOptionsByYear: progOpt6, indoviniCategoria: indCat6 } = window.AntaresUI;
const PN = window.AntaresPrimaNota;

const TIPI_CONTO = [
  { id: "banca",         label: "Conto in banca" },
  { id: "carta_credito", label: "Carta di credito" },
  { id: "carta_debito",  label: "Carta di debito" },
  { id: "pagamenti",     label: "Conto pagamenti (SumUp…)" },
  { id: "cassa",         label: "Cassa contanti" },
];
const TINTE = [
  { h: 205, nome: "Blu" }, { h: 245, nome: "Indaco" }, { h: 230, nome: "Azzurro" }, { h: 165, nome: "Verde acqua" },
  { h: 140, nome: "Verde" }, { h: 95, nome: "Oliva" }, { h: 70, nome: "Ocra" }, { h: 45, nome: "Arancio" },
  { h: 20, nome: "Rosso" }, { h: 355, nome: "Rosa" },
];
// categorie di partenza: si aggiungono quelle già usate e se ne creano di nuove
const CATEGORIE_PN = [
  "Incassi clienti", "Fornitori", "Compensi collaboratori", "Stipendi", "F24 e tasse",
  "Commissioni bancarie", "Trasporti", "Trasferte / hotel", "Catering", "Noleggio attrezzature",
  "Materiali di consumo", "Software e abbonamenti", "Utenze", "Consulenze", "Finanziamenti",
  "Prelievi e versamenti",
];

const oggiIso6 = () => {
  const d = new Date();
  return d.getFullYear() + "-" + String(d.getMonth() + 1).padStart(2, "0") + "-" + String(d.getDate()).padStart(2, "0");
};
const dataIt6 = (iso) => iso ? iso.split("-").reverse().join("/") : "";
const giornoLungo6 = (iso) => new Date(iso + "T12:00:00").toLocaleDateString("it-IT", { weekday: "long", day: "numeric", month: "long" });
const euro6 = (n) => fmt6(n, { decimals: 2 });

function categoriePN(extra) {
  const visti = new Set(), out = [];
  const add = (c) => { const t = String(c || "").trim(); if (t && t !== "Giroconto" && !visti.has(t.toLowerCase())) { visti.add(t.toLowerCase()); out.push(t); } };
  CATEGORIE_PN.forEach(add);
  const altre = [];
  (window.AntaresData.PN_MOVIMENTI || []).forEach(m => altre.push(m.categoria));
  (window.AntaresData.FATTURE || []).forEach(f => altre.push(f.categoria));
  (extra || []).forEach(c => altre.push(c));
  const prima = out.length;
  altre.forEach(add);
  return [...out.slice(0, prima), ...out.slice(prima).sort((a, b) => a.localeCompare(b, "it"))];
}

const contoDi = (id) => (window.AntaresData.PN_CONTI || []).find(c => c.id === id);
// "pagato con": la carta di debito usata, altrimenti il conto
const sceltaDi = (m) => m.strumento_id || m.conto_id;
// una carta di debito collegata spende i soldi del suo conto: il movimento va sul conto
function risolviConto(idScelto) {
  const c = contoDi(idScelto);
  if (c && c.tipo === "carta_debito" && c.conto_addebito_id) return { conto_id: c.conto_addebito_id, strumento_id: c.id };
  return { conto_id: idScelto || null, strumento_id: null };
}
const nomeProgetto6 = (id) => ((window.AntaresData.PROGETTI || []).find(p => p.id === id) || {}).nome || id;

function PallinoConto({ conto }) {
  if (!conto) return <span className="muted">—</span>;
  return (
    <span className="pn-conto">
      <i style={{ background: `oklch(0.62 0.12 ${conto.tinta})` }} />{conto.nome}
    </span>
  );
}

function StatoDoc({ m }) {
  const s = PN.statoMovimento(m);
  if (s === "fattura") return <span className="pn-doc ok" title={"Fattura " + m.fattura_id}><Icon6 name="ok" size={13} />{m.fattura_id}</span>;
  if (s === "giroconto") return <span className="pn-doc"><Icon6 name="swap" size={13} />giroconto</span>;
  if (s === "ricevuta") return <span className="pn-doc ok"><Icon6 name="ok" size={13} />ricevuta</span>;
  if (s === "non_serve") return <span className="pn-doc">senza documento</span>;
  return <span className="pill yellow">{PN.STATI[s].label}</span>;
}

function SelectConto({ value, onChange, conti, vuoto, compatta }) {
  const gruppi = [
    { label: "Conti", lista: conti.filter(c => !String(c.tipo).startsWith("carta")) },
    { label: "Carte", lista: conti.filter(c => String(c.tipo).startsWith("carta")) },
  ].filter(g => g.lista.length);
  return (
    <select className="select" value={value || ""} onChange={e => onChange(e.target.value)} style={compatta ? { minWidth: 0 } : undefined}>
      {vuoto && <option value="">{vuoto}</option>}
      {gruppi.map(g => (
        <optgroup key={g.label} label={g.label}>
          {g.lista.map(c => <option key={c.id} value={c.id}>{c.nome}</option>)}
        </optgroup>
      ))}
    </select>
  );
}

function SelectProgetto6({ value, onChange, compatta }) {
  return (
    <select className="select" value={value || ""} onChange={e => onChange(e.target.value)} style={compatta ? { minWidth: 0 } : undefined}>
      <option value="">Spese generali</option>
      {progOpt6(window.AntaresData.PROGETTI || [])}
    </select>
  );
}

/* ============================================================
   SCHERMATA
   ============================================================ */
function PrimaNota() {
  const D = window.AntaresData;
  const conti = (D.PN_CONTI || []);
  const contiAttivi = conti.filter(c => c.attivo !== false);
  const movimenti = (D.PN_MOVIMENTI || []);

  const oggi = new Date();
  const [anno, setAnno] = useS6(String(oggi.getFullYear()));
  const [mese, setMese] = useS6(String(oggi.getMonth() + 1)); // "tutti" o 1..12
  const [filtroConto, setFiltroConto] = useS6("");
  const [filtroStato, setFiltroStato] = useS6("tutti");
  const [filtroProg, setFiltroProg] = useS6("tutti");
  const [cerca, setCerca] = useS6("");
  const [modale, setModale] = useS6(null); // {tipo:"mov", mov} | {tipo:"conti"} | {tipo:"import"}
  const [toast, setToast] = useS6(null);

  // riga di inserimento rapido
  const [qa, setQa] = useS6({ data: oggiIso6(), descrizione: "", scelta: "", categoria: "", progetto: "", importo: "", segno: -1 });
  const [qaBusy, setQaBusy] = useS6(false);

  const avvisa = (kind, msg, ms = 2600) => { setToast({ kind, msg }); setTimeout(() => setToast(null), ms); };

  const anni = useM6(() => {
    const s = new Set([String(oggi.getFullYear())]);
    movimenti.forEach(m => s.add(String(m.data).slice(0, 4)));
    return [...s].sort().reverse();
  }, [movimenti.length]);

  const nelPeriodo = (m) => String(m.data).startsWith(anno + "-" + (mese === "tutti" ? "" : String(mese).padStart(2, "0")));
  const periodo = movimenti.filter(nelPeriodo);
  const filtrati = periodo.filter(m => {
    if (filtroConto && m.conto_id !== filtroConto && m.strumento_id !== filtroConto) return false;
    const s = PN.statoMovimento(m);
    if (filtroStato === "da_sistemare" && PN.STATI[s].ok) return false;
    if (filtroStato === "con_doc" && !(s === "fattura" || s === "ricevuta")) return false;
    if (filtroStato === "giroconti" && s !== "giroconto") return false;
    if (filtroProg === "generali" && m.progetto_id) return false;
    if (filtroProg !== "tutti" && filtroProg !== "generali" && m.progetto_id !== filtroProg) return false;
    if (cerca.trim()) {
      const q = cerca.trim().toLowerCase();
      const testo = [m.descrizione, m.categoria, m.nota, m.fattura_id, nomeProgetto6(m.progetto_id)].join(" ").toLowerCase();
      if (!testo.includes(q)) return false;
    }
    return true;
  });

  // raggruppa per giorno, dal più recente
  const perGiorno = useM6(() => {
    const g = new Map();
    [...filtrati].sort((a, b) => String(b.data).localeCompare(String(a.data)) || String(b.created_at).localeCompare(String(a.created_at)))
      .forEach(m => { if (!g.has(m.data)) g.set(m.data, []); g.get(m.data).push(m); });
    return [...g.entries()];
  }, [filtrati]);

  const nonGiro = periodo.filter(m => !m.giroconto_id);
  const entrate = nonGiro.filter(m => m.importo > 0).reduce((s, m) => s + m.importo, 0);
  const uscite = nonGiro.filter(m => m.importo < 0).reduce((s, m) => s - m.importo, 0);
  const daSistemare = periodo.filter(m => !PN.STATI[PN.statoMovimento(m)].ok).length;
  const etichettaPeriodo = mese === "tutti" ? "anno " + anno : MESI6[Number(mese) - 1].toLowerCase() + " " + anno;

  if (!D.PN_PRONTA) {
    return (
      <div className="stack">
        <TestataPN />
        <div className="card">
          <Empty6 title="La prima nota non è ancora attiva">
            Serve la migrazione del database <code>db/25_prima_nota.sql</code>: crea i conti
            (Conto BCC, Carta di credito Nexi, carte EVO 1 e 2, Conto e carta SumUp) e la tabella dei movimenti.
          </Empty6>
        </div>
      </div>
    );
  }

  const aggiungiRapido = async (e) => {
    e.preventDefault();
    const imp = PN.parseImporto(qa.importo);
    if (!qa.descrizione.trim() || !imp || !qa.scelta) { avvisa("err", "Servono descrizione, conto e importo"); return; }
    setQaBusy(true);
    const { error } = await window.AntaresStore.salvaMovimento({
      data: qa.data, descrizione: qa.descrizione.trim(), ...risolviConto(qa.scelta),
      importo: Math.abs(imp) * qa.segno, categoria: qa.categoria || null, progetto_id: qa.progetto || null,
    });
    setQaBusy(false);
    if (error) { avvisa("err", "Errore: " + error.message, 4000); return; }
    await window.AntaresStore.refresh("prima_nota");
    setQa(q => ({ ...q, descrizione: "", importo: "", categoria: "", progetto: "" }));
    avvisa("ok", "Movimento aggiunto");
  };

  return (
    <div className="stack">
      <TestataPN
        etichetta={etichettaPeriodo}
        onConti={() => setModale({ tipo: "conti" })}
        onImporta={() => setModale({ tipo: "import" })}
        onEsporta={() => esportaCommercialista(periodo, etichettaPeriodo, anno, mese)}
        onNuovo={() => setModale({ tipo: "mov", mov: null })}
      />

      {/* saldi per conto + riepilogo del periodo */}
      <div className="pn-conti">
        {contiAttivi.map(c => <CardConto key={c.id} conto={c} movimenti={movimenti} periodo={periodo}
          attivo={filtroConto === c.id} onClick={() => setFiltroConto(f => f === c.id ? "" : c.id)}
          onImposta={() => setModale({ tipo: "conti" })} />)}
        <div className="kpi">
          <div className="kpi-label">{etichettaPeriodo}</div>
          <div className="kpi-value pn-periodo">
            <span className="pos">+{fmt6(entrate).replace("€ ", "")}</span>
            <span className="muted"> · </span>
            <span>−{fmt6(uscite).replace("€ ", "")}</span>
          </div>
          <div className="kpi-sub">
            {daSistemare > 0
              ? <button type="button" className="link-btn" onClick={() => setFiltroStato("da_sistemare")}>{daSistemare} da sistemare</button>
              : "tutto in ordine"}
          </div>
        </div>
      </div>

      <div className="card">
        {/* filtri */}
        <div className="pn-filtri">
          <div className="segmented compact">
            {[["tutti", "Tutti"], ["da_sistemare", "Da sistemare" + (daSistemare ? " · " + daSistemare : "")], ["con_doc", "Con documento"], ["giroconti", "Giroconti"]].map(([id, l]) => (
              <button key={id} type="button" className={filtroStato === id ? "active" : ""} onClick={() => setFiltroStato(id)}>{l}</button>
            ))}
          </div>
          <div className="pn-filtri-destra">
            <select className="select" value={mese} onChange={e => setMese(e.target.value)} aria-label="Mese">
              <option value="tutti">Tutto l'anno</option>
              {MESI6.map((m, i) => <option key={m} value={String(i + 1)}>{m}</option>)}
            </select>
            <select className="select" value={anno} onChange={e => setAnno(e.target.value)} aria-label="Anno">
              {anni.map(a => <option key={a}>{a}</option>)}
            </select>
            <SelectConto value={filtroConto} onChange={setFiltroConto} conti={conti} vuoto="Tutti i conti" />
            <select className="select" value={filtroProg} onChange={e => setFiltroProg(e.target.value)} aria-label="Progetto">
              <option value="tutti">Tutti i progetti</option>
              <option value="generali">Solo spese generali</option>
              {progOpt6(D.PROGETTI || [])}
            </select>
            <input className="input" placeholder="Cerca…" value={cerca} onChange={e => setCerca(e.target.value)} style={{ width: 160 }} />
          </div>
        </div>

        {/* inserimento rapido */}
        <form className="pn-rapido" onSubmit={aggiungiRapido}>
          <input className="input" type="date" value={qa.data} onChange={e => setQa({ ...qa, data: e.target.value })} aria-label="Data" />
          <input className="input" placeholder="Descrizione movimento…" value={qa.descrizione} onChange={e => setQa({ ...qa, descrizione: e.target.value })} />
          <SelectConto value={qa.scelta} onChange={v => setQa({ ...qa, scelta: v })} conti={contiAttivi} vuoto="Pagato con…" compatta />
          <SelCat6 value={qa.categoria} onChange={v => setQa({ ...qa, categoria: v })} opzioni={categoriePN()} />
          <SelectProgetto6 value={qa.progetto} onChange={v => setQa({ ...qa, progetto: v })} compatta />
          <div className="pn-importo">
            <button type="button" className={"pn-segno " + (qa.segno < 0 ? "out" : "in")} onClick={() => setQa({ ...qa, segno: -qa.segno })}
              title="Cambia tra uscita ed entrata">{qa.segno < 0 ? "−" : "+"}</button>
            <input className="input num" placeholder="0,00" inputMode="decimal" value={qa.importo} onChange={e => setQa({ ...qa, importo: e.target.value })} aria-label="Importo" />
          </div>
          <button type="submit" className="btn" disabled={qaBusy}><Icon6 name="plus" size={14} />Aggiungi</button>
        </form>

        <div className="table-wrap">
          <table className="table pn-tabella">
            <thead>
              <tr>
                <th>Descrizione</th><th>Pagato con</th><th>Categoria</th><th>Progetto</th>
                <th className="num">Entrate</th><th className="num">Uscite</th><th>Documento</th>
              </tr>
            </thead>
            <tbody>
              {perGiorno.length === 0 && (
                <tr><td colSpan={7} className="empty">
                  {movimenti.length === 0
                    ? "Nessun movimento ancora. Aggiungine uno qui sopra o importa un estratto conto."
                    : "Nessun movimento con questi filtri in " + etichettaPeriodo + "."}
                </td></tr>
              )}
              {perGiorno.map(([giorno, righe]) => (
                <React.Fragment key={giorno}>
                  <tr className="pn-giorno"><td colSpan={7}>{giornoLungo6(giorno)}</td></tr>
                  {righe.map(m => (
                    <tr key={m.id} className="pn-riga" onClick={() => setModale({ tipo: "mov", mov: m })}>
                      <td>
                        <div className="pn-desc">{m.descrizione}</div>
                        {m.nota && <div className="pn-nota">{m.nota}</div>}
                      </td>
                      <td><PallinoConto conto={contoDi(sceltaDi(m))} /></td>
                      <td className="muted">{m.categoria || "—"}</td>
                      <td className="muted">{m.giroconto_id ? "—" : (m.progetto_id ? nomeProgetto6(m.progetto_id) : "Spese generali")}</td>
                      <td className="num pos">{m.importo > 0 ? euro6(m.importo) : ""}</td>
                      <td className="num">{m.importo < 0 ? euro6(-m.importo) : ""}</td>
                      <td><StatoDoc m={m} /></td>
                    </tr>
                  ))}
                </React.Fragment>
              ))}
            </tbody>
          </table>
        </div>
      </div>

      {modale && modale.tipo === "mov" && (
        <MovimentoModal mov={modale.mov} onClose={() => setModale(null)} onSaved={(msg) => { setModale(null); avvisa("ok", msg); }} />
      )}
      {modale && modale.tipo === "conti" && <ContiModal onClose={() => setModale(null)} />}
      {modale && modale.tipo === "import" && (
        <ImportaEstrattoModal onClose={() => setModale(null)} onDone={(msg) => { setModale(null); avvisa("ok", msg, 4000); }} />
      )}
      {toast && <div className={"toast " + toast.kind}>{toast.msg}</div>}
    </div>
  );
}

function TestataPN({ etichetta, onConti, onImporta, onEsporta, onNuovo }) {
  return (
    <div className="page-head">
      <div className="page-title-block">
        <span className="eyebrow">Movimenti di conti e carte{etichetta ? " · " + etichetta : ""}</span>
        <h1 className="h1">Prima nota</h1>
      </div>
      {onNuovo && (
        <div className="page-actions">
          <button className="btn ghost" onClick={onConti}><Icon6 name="wallet" size={14} />Conti e carte</button>
          <button className="btn ghost" onClick={onImporta}><Icon6 name="upload" size={14} />Importa estratto conto</button>
          <button className="btn ghost" onClick={onEsporta}><Icon6 name="download" size={14} />Esporta per commercialista</button>
          <button className="btn primary" onClick={onNuovo}><Icon6 name="plus" size={14} />Nuovo movimento</button>
        </div>
      )}
    </div>
  );
}

function CardConto({ conto, movimenti, periodo, attivo, onClick, onImposta }) {
  const collegato = conto.tipo === "carta_debito" && conto.conto_addebito_id ? contoDi(conto.conto_addebito_id) : null;
  const tipo = (TIPI_CONTO.find(t => t.id === conto.tipo) || {}).label || conto.tipo;
  let valore, sotto;
  if (collegato) {
    // una carta di debito collegata non ha un saldo suo: mostra quanto ha speso nel periodo
    const speso = periodo.filter(m => m.strumento_id === conto.id && m.importo < 0).reduce((s, m) => s - m.importo, 0);
    valore = fmt6(-speso, { decimals: 2 });
    sotto = "spesi nel periodo · su " + collegato.nome;
  } else {
    valore = fmt6(PN.saldoConto(conto, movimenti), { decimals: 2 });
    sotto = conto.data_saldo ? tipo + " · saldo dal " + dataIt6(conto.data_saldo) : null;
  }
  return (
    <button type="button" className={"kpi pn-conto-card" + (attivo ? " attivo" : "")} onClick={onClick}
      title={attivo ? "Mostra tutti i conti" : "Mostra solo " + conto.nome}>
      <div className="kpi-label"><PallinoConto conto={conto} /></div>
      <div className="kpi-value">{valore}</div>
      <div className="kpi-sub">
        {sotto || <span>{tipo} · <span className="link-btn" role="link" onClick={e => { e.stopPropagation(); onImposta(); }}>imposta il saldo iniziale</span></span>}
      </div>
    </button>
  );
}

/* ============================================================
   NUOVO / MODIFICA MOVIMENTO
   ============================================================ */
function MovimentoModal({ mov, onClose, onSaved }) {
  const D = window.AntaresData;
  const conti = (D.PN_CONTI || []).filter(c => c.attivo !== false || (mov && (c.id === mov.conto_id || c.id === mov.strumento_id)));
  const gambe = mov && mov.giroconto_id ? (D.PN_MOVIMENTI || []).filter(m => m.giroconto_id === mov.giroconto_id) : [];
  const gambaDa = gambe.find(m => m.importo < 0), gambaA = gambe.find(m => m.importo > 0);

  const [tipo, setTipo] = useS6(mov ? (mov.giroconto_id ? "giroconto" : mov.importo > 0 ? "entrata" : "uscita") : "uscita");
  const [data, setData] = useS6(mov ? mov.data : oggiIso6());
  const [descrizione, setDescrizione] = useS6(mov ? mov.descrizione : "");
  const [importo, setImporto] = useS6(mov ? String(Math.abs(mov.importo)).replace(".", ",") : "");
  const [scelta, setScelta] = useS6(mov ? sceltaDi(mov) : "");
  const [categoria, setCategoria] = useS6(mov ? mov.categoria || "" : "");
  const [progetto, setProgetto] = useS6(mov ? mov.progetto_id || "" : "");
  const [fatturaId, setFatturaId] = useS6(mov ? mov.fattura_id || "" : "");
  const [giust, setGiust] = useS6(mov ? mov.giustificativo || "" : "");
  const [nota, setNota] = useS6(mov ? mov.nota || "" : "");
  const [riconciliato, setRiconciliato] = useS6(mov ? !!mov.riconciliato : false);
  const [chiudi, setChiudi] = useS6(true);
  const [giroDa, setGiroDa] = useS6(gambaDa ? gambaDa.conto_id : "");
  const [giroA, setGiroA] = useS6(gambaA ? gambaA.conto_id : "");
  const [busy, setBusy] = useS6(false);
  const [err, setErr] = useS6("");

  const imp = Math.abs(PN.parseImporto(importo) || 0);
  const segno = tipo === "entrata" ? 1 : -1;

  // fatture: prima le suggerite (importo e nome), poi tutte quelle del tipo giusto
  const tipoFatt = tipo === "entrata" ? "attiva" : "passiva";
  const giaCollegate = new Set((D.PN_MOVIMENTI || []).filter(m => m.fattura_id && (!mov || m.id !== mov.id)).map(m => m.fattura_id));
  const suggerite = imp && descrizione ? PN.candidatiFattura({ data, importo: imp * segno, descrizione }, D.FATTURE || [], giaCollegate).slice(0, 5) : [];
  const altreFatt = (D.FATTURE || []).filter(f => f.tipo === tipoFatt && !suggerite.some(s => s.fattura.id === f.id)).slice(0, 400);
  const fattura = (D.FATTURE || []).find(f => f.id === fatturaId);
  const fatturaAperta = fattura && !["pagata", "incassata"].includes(String(fattura.stato).toLowerCase());

  const scegliFattura = (id) => {
    setFatturaId(id);
    const f = (D.FATTURE || []).find(x => x.id === id);
    if (!f) return;
    if (!progetto && f.progetto_id) setProgetto(f.progetto_id);
    if (!categoria) setCategoria(f.tipo === "attiva" ? "Incassi clienti" : (f.categoria || "Fornitori"));
    if (!descrizione) setDescrizione((f.tipo === "attiva" ? "Incasso " : "Pagamento ") + f.controparte);
    if (!importo) setImporto(String(PN.totaleFattura(f)).replace(".", ","));
  };

  const salva = async () => {
    setErr("");
    if (!descrizione.trim()) { setErr("Scrivi una descrizione"); return; }
    if (!imp) { setErr("Inserisci un importo"); return; }
    setBusy(true);
    let res;
    if (tipo === "giroconto") {
      res = await window.AntaresStore.salvaGiroconto({
        sostituisci: mov ? (mov.giroconto_id ? { giroconto_id: mov.giroconto_id } : { id: mov.id }) : null,
        data, descrizione: descrizione.trim(), da: giroDa, a: giroA, importo: imp, nota,
      });
    } else {
      if (!scelta) { setBusy(false); setErr("Scegli con cosa è stato pagato o dove è entrato"); return; }
      res = await window.AntaresStore.salvaMovimento({
        id: mov && !mov.giroconto_id ? mov.id : undefined,
        data, descrizione: descrizione.trim(), ...risolviConto(scelta), importo: imp * segno,
        categoria: categoria || null, progetto_id: progetto || null, fattura_id: fatturaId || null,
        giustificativo: fatturaId ? null : (giust || null), giroconto_id: null,
        nota: nota.trim() || null, riconciliato,
      });
      // un giroconto diventato spesa/entrata: scritto il movimento nuovo, si tolgono le due gambe
      if (!res.error && mov && mov.giroconto_id) {
        const del = await window.AntaresStore.eliminaMovimento(mov);
        if (del.error) res = del;
      }
      if (!res.error && mov && mov.fattura_id && mov.fattura_id !== fatturaId) await forseRiapri(mov.fattura_id);
      if (!res.error && fatturaId && chiudi && fatturaAperta) {
        const c = await window.AntaresStore.chiudiFattura(fatturaId, data);
        if (c.error) res = c;
        else await window.AntaresStore.refresh("fatture");
      }
    }
    setBusy(false);
    if (res.error) { setErr(res.error.message); return; }
    await window.AntaresStore.refresh("prima_nota");
    onSaved(mov ? "Movimento aggiornato" : "Movimento registrato");
  };

  // la fattura che questo movimento aveva chiuso: se si scollega o si elimina il
  // movimento (e nessun altro movimento la paga), si propone di riaprirla
  const forseRiapri = async (idFattura) => {
    const f = (D.FATTURE || []).find(x => x.id === idFattura);
    if (!f || !["pagata", "incassata"].includes(String(f.stato).toLowerCase())) return;
    const altri = (D.PN_MOVIMENTI || []).some(m => m.fattura_id === idFattura && m.id !== mov.id);
    if (altri) return;
    if (!confirm(`La fattura ${f.id} (${f.controparte}) risulta ${f.stato}. Riportarla a ${f.tipo === "attiva" ? "emessa" : "da pagare"}?`)) return;
    const r = await window.AntaresStore.riapriFattura(f.id);
    if (!r.error) await window.AntaresStore.refresh("fatture");
  };

  const elimina = async () => {
    if (!confirm(mov.giroconto_id ? "Eliminare il giroconto (entrambi i movimenti)?" : "Eliminare questo movimento?")) return;
    setBusy(true);
    const { error } = await window.AntaresStore.eliminaMovimento(mov);
    setBusy(false);
    if (error) { setErr(error.message); return; }
    if (mov.fattura_id) await forseRiapri(mov.fattura_id);
    await window.AntaresStore.refresh("prima_nota");
    onSaved("Movimento eliminato");
  };

  return (
    <Modal6
      title={mov ? "Modifica movimento" : "Nuovo movimento"}
      subtitle={mov && mov.origine === "import" ? "Importato da un estratto conto" : null}
      onClose={onClose} width={640}
      footer={<>
        {mov && <button type="button" className="btn danger modal-foot-left" onClick={elimina} disabled={busy}>Elimina</button>}
        <button type="button" className="btn" onClick={onClose}>Annulla</button>
        <button type="button" className="btn primary" onClick={salva} disabled={busy}>{busy ? "Salvo…" : "Salva"}</button>
      </>}
    >
      {err && <div className="login-error" style={{ marginBottom: 10 }}>{err}</div>}
      <div className="segmented" style={{ display: "flex", marginBottom: 14 }}>
        {[["uscita", "Uscita"], ["entrata", "Entrata"], ["giroconto", "Giroconto"]].map(([id, l]) => (
          <button key={id} type="button" className={tipo === id ? "active" : ""} style={{ flex: 1 }} onClick={() => setTipo(id)}>{l}</button>
        ))}
      </div>
      <div className="form-grid">
        <div className="field">
          <label className="label">Data</label>
          <input className="input" type="date" value={data} onChange={e => setData(e.target.value)} />
        </div>
        <div className="field">
          <label className="label">Importo</label>
          <input className="input num" inputMode="decimal" placeholder="0,00" value={importo} onChange={e => setImporto(e.target.value)} />
        </div>
        <div className="field span-2">
          <label className="label">Descrizione</label>
          <input className="input" value={descrizione} onChange={e => setDescrizione(e.target.value)}
            placeholder={tipo === "giroconto" ? "es. Addebito estratto Nexi di settembre" : "es. Cartucce stampante"} />
        </div>

        {tipo === "giroconto" ? (
          <>
            <div className="field">
              <label className="label">Esce da</label>
              <SelectConto value={giroDa} onChange={setGiroDa} conti={conti.filter(c => !(c.tipo === "carta_debito" && c.conto_addebito_id))} vuoto="Scegli…" />
            </div>
            <div className="field">
              <label className="label">Entra in</label>
              <SelectConto value={giroA} onChange={setGiroA} conti={conti.filter(c => !(c.tipo === "carta_debito" && c.conto_addebito_id))} vuoto="Scegli…" />
            </div>
            <p className="help span-2" style={{ gridColumn: "1 / -1", margin: 0 }}>
              Il giroconto sposta soldi tra due conti (anche l'addebito mensile della carta di credito): non è né una spesa né un ricavo.
            </p>
          </>
        ) : (
          <>
            <div className="field">
              <label className="label">{tipo === "entrata" ? "Entrato su" : "Pagato con"}</label>
              <SelectConto value={scelta} onChange={setScelta} conti={conti} vuoto="Scegli…" />
            </div>
            <div className="field">
              <label className="label">Categoria</label>
              <SelCat6 value={categoria} onChange={setCategoria} opzioni={categoriePN()} />
            </div>
            <div className="field span-2">
              <label className="label">Progetto</label>
              <SelectProgetto6 value={progetto} onChange={setProgetto} />
            </div>
            <div className="field span-2">
              <label className="label">{tipo === "entrata" ? "Fattura incassata" : "Fattura pagata"}</label>
              <select className="select" value={fatturaId} onChange={e => scegliFattura(e.target.value)}>
                <option value="">— nessuna fattura —</option>
                {suggerite.length > 0 && (
                  <optgroup label="Suggerite (importo e nome)">
                    {suggerite.map(s => (
                      <option key={s.fattura.id} value={s.fattura.id}>
                        {s.fattura.id} · {s.fattura.controparte} · {s.fattura.data} · {euro6(s.totale)}
                      </option>
                    ))}
                  </optgroup>
                )}
                <optgroup label={tipo === "entrata" ? "Fatture emesse" : "Fatture ricevute"}>
                  {altreFatt.map(f => (
                    <option key={f.id} value={f.id}>{f.id} · {f.controparte} · {f.data} · {euro6(PN.totaleFattura(f))}</option>
                  ))}
                  {fattura && fattura.tipo !== tipoFatt && <option value={fattura.id}>{fattura.id} · {fattura.controparte}</option>}
                </optgroup>
              </select>
              {fattura && fatturaAperta && (
                <label className="pn-check">
                  <input type="checkbox" checked={chiudi} onChange={e => setChiudi(e.target.checked)} />
                  Segna la fattura come {fattura.tipo === "attiva" ? "incassata" : "pagata"} il {dataIt6(data)}
                </label>
              )}
              {fattura && Math.abs(PN.totaleFattura(fattura) - imp) > 1 && (
                <div className="help" style={{ color: "var(--yellow)" }}>
                  Il totale della fattura è {euro6(PN.totaleFattura(fattura))}: questo movimento ne copre solo una parte, o c'è una differenza.
                </div>
              )}
            </div>
            {!fatturaId && (
              <div className="field span-2">
                <label className="label">Senza fattura</label>
                <div className="segmented compact" style={{ display: "flex" }}>
                  {[["", tipo === "entrata" ? "Da abbinare" : "Manca la fattura"], ["ricevuta", "C'è la ricevuta"], ["non_serve", "Non serve documento"]].map(([id, l]) => (
                    <button key={id || "manca"} type="button" style={{ flex: 1 }} className={giust === id ? "active" : ""} onClick={() => setGiust(id)}>{l}</button>
                  ))}
                </div>
                <div className="help">"Non serve documento": commissioni bancarie, F24, stipendi, prelievi…</div>
              </div>
            )}
          </>
        )}
        <div className="field span-2">
          <label className="label">Nota</label>
          <input className="input" value={nota} onChange={e => setNota(e.target.value)} placeholder="facoltativa" />
        </div>
        {tipo !== "giroconto" && (
          <label className="pn-check span-2" style={{ gridColumn: "1 / -1" }}>
            <input type="checkbox" checked={riconciliato} onChange={e => setRiconciliato(e.target.checked)} />
            Riconciliato: compare anche nell'estratto conto
          </label>
        )}
      </div>
    </Modal6>
  );
}

/* ============================================================
   CONTI E CARTE
   ============================================================ */
function ContiModal({ onClose }) {
  const D = window.AntaresData;
  const [righe, setRighe] = useS6(() => (D.PN_CONTI || []).map(c => ({ ...c, saldo_iniziale: String(c.saldo_iniziale ?? 0).replace(".", ","), _mod: false })));
  const [busy, setBusy] = useS6(false);
  const [err, setErr] = useS6("");

  const cambia = (i, k, v) => setRighe(rs => rs.map((r, j) => j === i ? { ...r, [k]: v, _mod: true } : r));
  const aggiungi = () => setRighe(rs => [...rs, { nome: "", tipo: "banca", tinta: 205, saldo_iniziale: "0", data_saldo: null, attivo: true, ordine: rs.length + 1, conto_addebito_id: null, _mod: true, _nuovo: true }]);

  const salva = async () => {
    setErr("");
    const nomi = righe.map(r => r.nome.trim().toLowerCase());
    if (nomi.some(n => !n)) { setErr("Ogni conto deve avere un nome"); return; }
    if (new Set(nomi).size !== nomi.length) { setErr("Due conti hanno lo stesso nome"); return; }
    setBusy(true);
    for (const r of righe.filter(x => x._mod)) {
      const { error } = await window.AntaresStore.salvaConto({
        ...r, nome: r.nome.trim(), saldo_iniziale: PN.parseImporto(r.saldo_iniziale) || 0,
        tinta: Number(r.tinta), conto_addebito_id: r.conto_addebito_id || null, data_saldo: r.data_saldo || null,
      });
      if (error) { setBusy(false); setErr(r.nome + ": " + error.message); return; }
      // carta di debito collegata/scollegata: i movimenti già registrati la seguono
      const prima = (D.PN_CONTI || []).find(c => c.id === r.id);
      const eraCollegata = prima && prima.tipo === "carta_debito" ? prima.conto_addebito_id || null : null;
      const ora = r.tipo === "carta_debito" ? r.conto_addebito_id || null : null;
      if (prima && eraCollegata !== ora) {
        if (eraCollegata) { const u = await window.AntaresStore.spostaMovimentiCarta(r.id, null); if (u.error) { setBusy(false); setErr(u.error.message); return; } }
        if (ora) { const u = await window.AntaresStore.spostaMovimentiCarta(r.id, ora); if (u.error) { setBusy(false); setErr(u.error.message); return; } }
      }
    }
    await window.AntaresStore.refresh("prima_nota");
    setBusy(false);
    onClose();
  };

  const elimina = async (r, i) => {
    if (r._nuovo) { setRighe(rs => rs.filter((_, j) => j !== i)); return; }
    const usati = (D.PN_MOVIMENTI || []).filter(m => m.conto_id === r.id || m.strumento_id === r.id).length;
    if (usati) { setErr(`"${r.nome}" ha ${usati} movimenti: invece di eliminarlo togli la spunta "attivo".`); return; }
    if (!confirm(`Eliminare "${r.nome}"?`)) return;
    const { error } = await window.AntaresStore.eliminaConto(r.id);
    if (error) { setErr(error.message); return; }
    setRighe(rs => rs.filter((_, j) => j !== i));
    await window.AntaresStore.refresh("prima_nota");
  };

  return (
    <Modal6 title="Conti e carte" subtitle="Dove stanno i soldi e con cosa si paga. Il saldo iniziale serve a far tornare i saldi con la banca."
      onClose={onClose} width={1000}
      footer={<>
        <button type="button" className="btn modal-foot-left" onClick={aggiungi}><Icon6 name="plus" size={14} />Aggiungi conto o carta</button>
        <button type="button" className="btn" onClick={onClose}>Annulla</button>
        <button type="button" className="btn primary" onClick={salva} disabled={busy}>{busy ? "Salvo…" : "Salva"}</button>
      </>}
    >
      {err && <div className="login-error" style={{ marginBottom: 10 }}>{err}</div>}
      <div className="table-wrap">
        <table className="table pn-conti-tab">
          <thead>
            <tr><th>Nome</th><th>Tipo</th><th>Collegato a</th><th className="num">Saldo iniziale</th><th>Al giorno</th><th>Colore</th><th>Attivo</th><th></th></tr>
          </thead>
          <tbody>
            {righe.map((r, i) => (
              <tr key={r.id || "n" + i}>
                <td><input className="input" value={r.nome} onChange={e => cambia(i, "nome", e.target.value)} /></td>
                <td>
                  <select className="select" value={r.tipo} onChange={e => cambia(i, "tipo", e.target.value)}>
                    {TIPI_CONTO.map(t => <option key={t.id} value={t.id}>{t.label}</option>)}
                  </select>
                </td>
                <td>
                  {String(r.tipo).startsWith("carta") ? (
                    <select className="select" value={r.conto_addebito_id || ""} onChange={e => cambia(i, "conto_addebito_id", e.target.value || null)}
                      title={r.tipo === "carta_debito" ? "Il conto da cui escono i soldi quando paghi con questa carta" : "Il conto su cui arriva l'addebito mensile"}>
                      <option value="">— nessuno —</option>
                      {righe.filter(x => x.id && x.id !== r.id && !String(x.tipo).startsWith("carta")).map(x => <option key={x.id} value={x.id}>{x.nome}</option>)}
                    </select>
                  ) : <span className="muted">—</span>}
                </td>
                <td>
                  {r.tipo === "carta_debito" && r.conto_addebito_id
                    ? <span className="muted" title="Spende i soldi del conto collegato: non ha un saldo suo">—</span>
                    : <input className="input num" style={{ width: 120 }} value={r.saldo_iniziale} onChange={e => cambia(i, "saldo_iniziale", e.target.value)} />}
                </td>
                <td><input className="input" type="date" value={r.data_saldo || ""} onChange={e => cambia(i, "data_saldo", e.target.value)} /></td>
                <td>
                  <div className="row" style={{ gap: 6, flexWrap: "nowrap" }}>
                    <span className="pn-conto"><i style={{ background: `oklch(0.62 0.12 ${r.tinta})` }} /></span>
                    <select className="select" value={r.tinta} onChange={e => cambia(i, "tinta", Number(e.target.value))}>
                      {TINTE.map(t => <option key={t.h} value={t.h}>{t.nome}</option>)}
                      {!TINTE.some(t => t.h === Number(r.tinta)) && <option value={r.tinta}>Personale</option>}
                    </select>
                  </div>
                </td>
                <td style={{ textAlign: "center" }}><input type="checkbox" checked={r.attivo !== false} onChange={e => cambia(i, "attivo", e.target.checked)} /></td>
                <td><button type="button" className="btn-row danger" onClick={() => elimina(r, i)} title="Elimina">×</button></td>
              </tr>
            ))}
          </tbody>
        </table>
      </div>
      <p className="help" style={{ marginTop: 10 }}>
        Una <strong>carta di debito</strong> collegata a un conto spende i soldi di quel conto: le sue spese finiscono sul conto e la carta resta come "pagato con".
        Così, caricando sia l'estratto della carta sia quello del conto, la stessa spesa non conta due volte (l'import segnala i probabili doppioni).
        Una <strong>carta di credito</strong> ha un suo saldo, che si azzera con il giroconto dell'addebito mensile.
      </p>
    </Modal6>
  );
}

/* ============================================================
   IMPORTA ESTRATTO CONTO (CSV / Excel di banca, carte, SumUp)
   ============================================================ */
function leggiFileEstratto(file) {
  return new Promise((resolve, reject) => {
    const csv = /\.(csv|txt)$/i.test(file.name);
    const reader = new FileReader();
    reader.onerror = () => reject(new Error("File non leggibile"));
    reader.onload = (e) => {
      try {
        let wb;
        if (csv) {
          const testo = String(e.target.result).replace(/^﻿/, "");
          const prime = testo.split(/\r?\n/).slice(0, 15).join("\n");
          const conta = (c) => (prime.match(new RegExp("\\" + c, "g")) || []).length;
          const FS = conta(";") > conta(",") ? ";" : conta("\t") > conta(",") ? "\t" : ",";
          wb = window.XLSX.read(testo, { type: "string", FS, raw: true });
        } else {
          wb = window.XLSX.read(new Uint8Array(e.target.result), { type: "array", cellDates: true });
        }
        const ws = wb.Sheets[wb.SheetNames[0]];
        resolve(window.XLSX.utils.sheet_to_json(ws, { header: 1, defval: "", raw: true }));
      } catch (ex) { reject(ex); }
    };
    if (csv) reader.readAsText(file, "utf-8"); else reader.readAsArrayBuffer(file);
  });
}

const leggiLS6 = (k) => { try { return JSON.parse(localStorage.getItem(k) || "null"); } catch (e) { return null; } };
const scriviLS6 = (k, v) => { try { localStorage.setItem(k, JSON.stringify(v)); } catch (e) {} };

function ImportaEstrattoModal({ onClose, onDone }) {
  const D = window.AntaresData;
  const conti = (D.PN_CONTI || []).filter(c => c.attivo !== false);
  const [contoId, setContoId] = useS6("");
  const [grid, setGrid] = useS6(null);
  const [nomeFile, setNomeFile] = useS6("");
  const [inizio, setInizio] = useS6(0);
  const [col, setCol] = useS6(null);
  const [inverti, setInverti] = useS6(false);
  const [righe, setRighe] = useS6([]);
  const [chiudi, setChiudi] = useS6(true);
  const [err, setErr] = useS6("");
  const [busy, setBusy] = useS6(false);
  const [scartate, setScartate] = useS6(0);

  const conto = contoDi(contoId);
  const intestazione = grid && inizio > 0 ? grid[inizio - 1] : null;
  const firma = intestazione ? intestazione.map(h => String(h).trim().toLowerCase()).join("|") : "";

  // prepara le righe da mostrare: doppioni, fattura e categoria suggerite
  const prepara = (g, ini, c, inv, idConto) => {
    const lette = PN.righeDaGriglia(g, ini, c, { inverti: inv });
    setScartate(lette.scartate || 0);
    const chiavi = PN.chiaviImport(idConto, lette);
    const { conto_id, strumento_id } = risolviConto(idConto);
    const esistenti = D.PN_MOVIMENTI || [];
    const chiaviDB = new Set(esistenti.map(m => m.import_chiave).filter(Boolean));
    const mem = PN.memoriaCategorie(esistenti);
    const usate = new Set(esistenti.map(m => m.fattura_id).filter(Boolean));
    const conti = D.PN_CONTI || [];
    // carte di debito non collegate che però spendono da questo conto: i loro movimenti
    // contano come già registrati sul conto
    const contiCollegati = conti.filter(x => x.tipo === "carta_debito" && x.conto_addebito_id === conto_id).map(x => x.id);
    const usati = new Set();
    // Soldi che si spostano tra i vostri conti sono giroconti, non spese o entrate:
    // l'addebito mensile della Nexi sul conto, un bonifico dalla BCC a una carta EVO
    // (che ha un IBAN suo), il versamento di SumUp sulla banca… Si riconoscono dal
    // nome dell'altro conto nella descrizione ("EVO 1", "Nexi", "SumUp"), solo se
    // ne torna esattamente uno.
    const altriConti = conti.filter(x => x.attivo !== false && x.id !== conto_id && x.id !== idConto
      && !(x.tipo === "carta_debito" && x.conto_addebito_id));
    const GENERICHE = ["carta", "credito", "debito", "conto", "banca", "cassa", "pagamenti"];
    const modelli = altriConti.map(x => {
      const parti = String(x.nome).toLowerCase().split(/[^a-z0-9]+/).filter(w => (w.length >= 3 || /^\d+$/.test(w)) && !GENERICHE.includes(w));
      return parti.length && parti.some(w => w.length >= 3)
        ? { conto: x, re: new RegExp("\\b" + parti.join("\\W*") + "\\b", "i") } : null;
    }).filter(Boolean);
    const altroConto = (desc) => {
      const trovati = modelli.filter(m => m.re.test(desc));
      return trovati.length === 1 ? trovati[0].conto : null;
    };
    return lette.map((r, i) => {
      const gia = chiaviDB.has(chiavi[i]);
      const doppio = !gia ? PN.probabileDoppione(r, conto_id, esistenti, { fonte: idConto, contiCollegati, usati }) : null;
      if (doppio) usati.add(doppio.id);
      const giro = !gia && !doppio ? altroConto(r.descrizione) : null;
      const cand = PN.candidatiFattura(r, D.FATTURE || [], usate);
      const sicura = cand[0] && cand[0].sicura ? cand[0].fattura : null;
      if (sicura) usate.add(sicura.id);
      const m = mem[PN.chiaveEsercente(r.descrizione)] || {};
      let categoria = m.categoria || null;
      if (!categoria && sicura) categoria = sicura.tipo === "attiva" ? "Incassi clienti" : (sicura.categoria || "Fornitori");
      if (!categoria) categoria = indCat6("", r.descrizione);
      if (!categoria && /commission|canone|spese tenuta|imposta di bollo|bollo/i.test(r.descrizione)) categoria = "Commissioni bancarie";
      return {
        ...r, import_chiave: chiavi[i], conto_id, strumento_id,
        stato: gia ? "gia" : doppio ? "doppio" : "nuovo",
        doppioDi: doppio, scelta: !gia && !doppio,
        giroconto_verso: giro ? giro.id : null,
        candidati: giro ? [] : cand.slice(0, 5), fattura_id: sicura && !giro ? sicura.id : "",
        categoria, progetto_id: (sicura && sicura.progetto_id) || m.progetto_id || "",
        giustificativo: /commission|canone|spese tenuta|imposta di bollo|f24/i.test(r.descrizione) ? "non_serve" : null,
      };
    });
  };

  const applica = (g, ini, c, inv, idConto) => {
    if (!g || !c || !idConto || c.data < 0 || (c.importo < 0 && c.entrate < 0 && c.uscite < 0)) { setRighe([]); return; }
    setRighe(prepara(g, ini, c, inv, idConto));
  };

  const caricaFile = async (file) => {
    setErr(""); setRighe([]); setGrid(null);
    if (!file) return;
    if (!window.XLSX) { setErr("Libreria Excel non caricata. Ricarica la pagina."); return; }
    try {
      const g = await leggiFileEstratto(file);
      setNomeFile(file.name);
      let h = PN.trovaIntestazione(g);
      if (h < 0) h = 0;
      const H = g[h] || [];
      const firmaF = H.map(x => String(x).trim().toLowerCase()).join("|");
      // le colonne scelte l'ultima volta per lo stesso tipo di file e conto
      const mem = leggiLS6("antares.pn.colonne." + contoId);
      const c = mem && mem.firma === firmaF ? mem.col : PN.indovinaColonne(H);
      const lette = PN.righeDaGriglia(g, h + 1, c, {});
      const positivi = lette.filter(r => r.importo > 0).length;
      const inv = mem && mem.firma === firmaF ? !!mem.inverti
        : (conto && conto.tipo === "carta_credito" && lette.length > 0 && positivi / lette.length > 0.6);
      setGrid(g); setInizio(h + 1); setCol(c); setInverti(inv);
      applica(g, h + 1, c, inv, contoId);
      if (!lette.length) setErr("Non trovo movimenti: controlla qui sotto quali colonne contengono data e importo.");
    } catch (ex) {
      console.error("[estratto]", ex);
      setErr("Non riesco a leggere il file: " + (ex.message || ex));
    }
  };

  const cambiaCol = (k, v) => { const c = { ...col, [k]: Number(v) }; setCol(c); applica(grid, inizio, c, inverti, contoId); };
  const cambiaInverti = (v) => { setInverti(v); applica(grid, inizio, col, v, contoId); };
  const cambiaRiga = (i, patch) => setRighe(rs => rs.map((r, j) => j === i ? { ...r, ...patch } : r));
  const scelte = righe.filter(r => r.scelta);
  // "seleziona tutte" vale per le righe nuove: le già importate non si possono
  // scegliere e i probabili doppioni restano a scelta una per una
  const selezionabili = righe.filter(r => r.stato === "nuovo");
  const tutteScelte = selezionabili.length > 0 && selezionabili.every(r => r.scelta);
  const qualcunaScelta = selezionabili.some(r => r.scelta);
  const sceglieTutte = (si) => setRighe(rs => rs.map(r => r.stato === "nuovo" ? { ...r, scelta: si } : r));

  const importa = async () => {
    setErr(""); setBusy(true);
    scriviLS6("antares.pn.colonne." + contoId, { firma, col, inverti });
    const res = await window.AntaresStore.importaMovimenti(scelte.map(r => ({
      data: r.data, descrizione: r.descrizione, conto_id: r.conto_id, strumento_id: r.strumento_id,
      importo: r.importo, categoria: r.categoria || null, progetto_id: r.progetto_id || null,
      fattura_id: r.fattura_id || null, giustificativo: r.fattura_id ? null : r.giustificativo,
      import_chiave: r.import_chiave, riconciliato: true, giroconto_verso: r.giroconto_verso || null,
      nota: String(r.nota || "").trim() || null,
    })));
    if (res.error) { setBusy(false); setErr(res.error.message); return; }
    // i probabili doppioni lasciati fuori sono movimenti già registrati a mano:
    // l'estratto li conferma, quindi diventano riconciliati (e il prossimo import
    // dello stesso file li riconosce come già presenti)
    let riconciliati = 0;
    for (const r of righe.filter(x => x.stato === "doppio" && !x.scelta && x.doppioDi)) {
      const patch = { id: r.doppioDi.id, riconciliato: true };
      if (!r.doppioDi.import_chiave) patch.import_chiave = r.import_chiave;
      const u = await window.AntaresStore.salvaMovimento(patch);
      if (!u.error) riconciliati++;
    }
    let chiuse = 0;
    if (chiudi) {
      for (const r of scelte.filter(x => x.fattura_id)) {
        const f = (D.FATTURE || []).find(x => x.id === r.fattura_id);
        if (f && !["pagata", "incassata"].includes(String(f.stato).toLowerCase())) {
          const c = await window.AntaresStore.chiudiFattura(f.id, r.data);
          if (!c.error) chiuse++;
        }
      }
      if (chiuse) await window.AntaresStore.refresh("fatture");
    }
    await window.AntaresStore.refresh("prima_nota");
    setBusy(false);
    const n = (res.data || []).length;
    onDone(`Importati ${n} movimenti` + (res.saltate ? ` · ${res.saltate} già presenti` : "")
      + (riconciliati ? ` · ${riconciliati} già registrati segnati come riconciliati` : "") + (chiuse ? ` · ${chiuse} fatture chiuse` : ""));
  };

  const opzCol = (intestazione || []).map((h, i) => <option key={i} value={i}>{String(h || "colonna " + (i + 1))}</option>);
  const nAbbinate = scelte.filter(r => r.fattura_id).length;

  return (
    <Modal6 title="Importa estratto conto"
      subtitle="CSV o Excel scaricato dalla banca, dalla carta o da SumUp. Il file si legge solo qui: rileggerlo non crea doppioni."
      onClose={onClose} width={1180}
      footer={<>
        {righe.length > 0 && nAbbinate > 0 && (
          <label className="pn-check modal-foot-left">
            <input type="checkbox" checked={chiudi} onChange={e => setChiudi(e.target.checked)} />
            Segna come pagate/incassate le {nAbbinate} fatture abbinate
          </label>
        )}
        <button type="button" className="btn" onClick={onClose}>Annulla</button>
        <button type="button" className="btn primary" onClick={importa} disabled={busy || !righe.some(r => r.scelta || r.stato === "doppio")}>
          {busy ? "Importo…" : `Importa ${scelte.length || ""} movimenti`}
        </button>
      </>}
    >
      {err && <div className="login-error" style={{ marginBottom: 10 }}>{err}</div>}
      <div className="form-grid" style={{ marginBottom: 12 }}>
        <div className="field">
          <label className="label">Di quale conto o carta è l'estratto?</label>
          <SelectConto value={contoId} onChange={v => { setContoId(v); setGrid(null); setRighe([]); setNomeFile(""); }} conti={conti} vuoto="Scegli…" />
        </div>
        <div className="field">
          <label className="label">File</label>
          <input key={contoId} type="file" accept=".csv,.txt,.xls,.xlsx" disabled={!contoId} onChange={e => caricaFile(e.target.files[0])} />
          {conto && conto.tipo === "carta_debito" && conto.conto_addebito_id && (
            <div className="help">Le spese andranno sul {contoDi(conto.conto_addebito_id)?.nome}, con "{conto.nome}" come pagato con.</div>
          )}
        </div>
      </div>

      {grid && col && (
        <div className="pn-colonne">
          <span className="label">Colonne di {nomeFile}</span>
          <label>Data <select className="select" value={col.data} onChange={e => cambiaCol("data", e.target.value)}><option value={-1}>—</option>{opzCol}</select></label>
          <label>Descrizione <select className="select" value={col.descrizione} onChange={e => cambiaCol("descrizione", e.target.value)}><option value={-1}>—</option>{opzCol}</select></label>
          <label>Importo <select className="select" value={col.importo} onChange={e => cambiaCol("importo", e.target.value)}><option value={-1}>— (entrate e uscite separate)</option>{opzCol}</select></label>
          {col.importo < 0 && <>
            <label>Entrate <select className="select" value={col.entrate} onChange={e => cambiaCol("entrate", e.target.value)}><option value={-1}>—</option>{opzCol}</select></label>
            <label>Uscite <select className="select" value={col.uscite} onChange={e => cambiaCol("uscite", e.target.value)}><option value={-1}>—</option>{opzCol}</select></label>
          </>}
          <label className="pn-check"><input type="checkbox" checked={inverti} onChange={e => cambiaInverti(e.target.checked)} />
            Inverti i segni <span className="muted">(estratti delle carte che scrivono le spese in positivo)</span></label>
        </div>
      )}

      {righe.length > 0 && (
        <>
          <div className="row" style={{ gap: 8, margin: "10px 0" }}>
            <span className="pill teal">{righe.length} righe lette</span>
            <span className="pill">{scelte.length} da importare{selezionabili.length ? ` (${selezionabili.filter(r => r.scelta).length} di ${selezionabili.length} nuove)` : ""}</span>
            {righe.some(r => r.stato === "gia") && <span className="pill">{righe.filter(r => r.stato === "gia").length} già importate</span>}
            {righe.some(r => r.stato === "doppio") && <span className="pill yellow">{righe.filter(r => r.stato === "doppio").length} già registrati a mano: non si importano, diventano riconciliati</span>}
            <span className="pill">{nAbbinate} abbinate a una fattura</span>
            {righe.some(r => r.giroconto_verso && r.scelta) && <span className="pill">{righe.filter(r => r.giroconto_verso && r.scelta).length} spostamenti tra vostri conti, come giroconto</span>}
            {scartate > 0 && <span className="pill coral">{scartate} righe non lette: controlla le colonne o il file</span>}
          </div>
          <div className="table-wrap" style={{ maxHeight: "48vh", overflow: "auto" }}>
            <table className="table pn-import">
              <thead>
                <tr>
                  <th>
                    <input type="checkbox" aria-label="Seleziona tutte le spese nuove" title="Seleziona o togli tutte le spese nuove"
                      checked={tutteScelte} disabled={selezionabili.length === 0}
                      ref={el => { if (el) el.indeterminate = qualcunaScelta && !tutteScelte; }}
                      onChange={e => sceglieTutte(e.target.checked)} />
                  </th>
                  <th>Data</th><th>Descrizione</th><th className="num">Importo</th><th>Categoria</th><th>Progetto</th><th>Fattura</th><th>Nota</th>
                </tr>
              </thead>
              <tbody>
                {righe.map((r, i) => (
                  <tr key={r.import_chiave} className={r.stato === "gia" ? "pn-import-gia" : ""}>
                    <td><input type="checkbox" checked={r.scelta} disabled={r.stato === "gia"} onChange={e => cambiaRiga(i, { scelta: e.target.checked })} /></td>
                    <td className="mono" style={{ fontSize: 12, whiteSpace: "nowrap" }}>{dataIt6(r.data)}</td>
                    <td style={{ fontSize: 12.5 }}>
                      {r.descrizione}
                      {r.stato === "gia" && <div className="pn-nota">già importata</div>}
                      {r.giroconto_verso && <div className="pn-nota">
                        giroconto {r.importo < 0 ? "verso" : "da"} {contoDi(r.giroconto_verso)?.nome} ·{" "}
                        <button type="button" className="link-btn" onClick={() => cambiaRiga(i, { giroconto_verso: null })}>non è un giroconto</button>
                      </div>}
                      {r.stato === "doppio" && <div className="pn-nota" style={{ color: "var(--yellow)" }}>
                        forse è già registrata: "{r.doppioDi.descrizione}" del {dataIt6(r.doppioDi.data)}</div>}
                    </td>
                    <td className={"num " + (r.importo > 0 ? "pos" : "")} style={{ whiteSpace: "nowrap" }}>{euro6(r.importo)}</td>
                    <td><SelCat6 compatta value={r.categoria} opzioni={categoriePN(righe.map(x => x.categoria))} onChange={v => cambiaRiga(i, { categoria: v })} /></td>
                    <td><SelectProgetto6 compatta value={r.progetto_id} onChange={v => cambiaRiga(i, { progetto_id: v })} /></td>
                    <td>
                      {r.candidati.length ? (
                        <select className="select" style={{ fontSize: 12, padding: "3px 6px", maxWidth: 220 }} value={r.fattura_id}
                          onChange={e => {
                            const f = r.candidati.find(c => c.fattura.id === e.target.value);
                            cambiaRiga(i, { fattura_id: e.target.value, progetto_id: r.progetto_id || (f && f.fattura.progetto_id) || "" });
                          }}>
                          <option value="">— nessuna —</option>
                          {r.candidati.map(c => <option key={c.fattura.id} value={c.fattura.id}>{c.fattura.controparte} · {c.fattura.data} · {euro6(c.totale)}</option>)}
                        </select>
                      ) : <span className="muted" style={{ fontSize: 12 }}>nessuna simile</span>}
                    </td>
                    <td>
                      <input className="input pn-import-nota" value={r.nota || ""} placeholder="es. fatturata dopo"
                        aria-label={"Nota per " + r.descrizione} disabled={r.stato === "gia"}
                        onChange={e => cambiaRiga(i, { nota: e.target.value })} />
                    </td>
                  </tr>
                ))}
              </tbody>
            </table>
          </div>
        </>
      )}
    </Modal6>
  );
}

/* ============================================================
   EXPORT PER IL COMMERCIALISTA (Excel)
   ============================================================ */
function esportaCommercialista(movimenti, etichetta, anno, mese) {
  if (!window.XLSX) { alert("Libreria Excel non caricata. Ricarica la pagina."); return; }
  const ordinati = [...movimenti].sort((a, b) => String(a.data).localeCompare(String(b.data)));
  const riga = (m) => {
    const c = contoDi(m.conto_id), s = m.strumento_id ? contoDi(m.strumento_id) : null;
    const st = PN.statoMovimento(m);
    return {
      "Data": dataIt6(m.data),
      "Conto": c ? c.nome : "",
      "Pagato con": s ? s.nome : (c ? c.nome : ""),
      "Descrizione": m.descrizione,
      "Categoria": m.categoria || "",
      "Progetto": m.giroconto_id ? "" : (m.progetto_id ? nomeProgetto6(m.progetto_id) : "Spese generali"),
      "Entrate": m.importo > 0 ? m.importo : null,
      "Uscite": m.importo < 0 ? -m.importo : null,
      "Fattura": m.fattura_id || "",
      "Documento": PN.STATI[st].label,
      "Nota": m.nota || "",
    };
  };
  const wb = window.XLSX.utils.book_new();
  const tutti = window.XLSX.utils.json_to_sheet(ordinati.map(riga));
  tutti["!cols"] = [10, 22, 22, 46, 22, 28, 12, 12, 16, 16, 30].map(w => ({ wch: w }));
  window.XLSX.utils.book_append_sheet(wb, tutti, "Prima nota");
  const mancanti = ordinati.filter(m => !PN.STATI[PN.statoMovimento(m)].ok);
  const ws2 = window.XLSX.utils.json_to_sheet(mancanti.length ? mancanti.map(riga) : [{ "Data": "Nessun movimento senza documento" }]);
  ws2["!cols"] = tutti["!cols"];
  window.XLSX.utils.book_append_sheet(wb, ws2, "Senza documento");
  const nome = "prima-nota-" + anno + (mese === "tutti" ? "" : "-" + String(mese).padStart(2, "0")) + ".xlsx";
  window.XLSX.writeFile(wb, nome);
}

window.AntaresScreens6 = { PrimaNota };
