// wf-review.jsx (s143) — Pagina dedicata di APPROVAZIONE WORKFLOW (review per-documento).
// Sostituisce il modale per il flusso di lavoro: timeline workflow + documenti da verificare
// con viewer (PDF/immagini inline; CME → ricostruzione dalla tabella voci), AI a 3 modi per
// documento (verifica/riassunto/Q&A) e decisione per-documento (approva/rifiuta + commento).
// Lo step si approva solo quando tutti i documenti sono approvati; in più "Rimanda indietro".

const WF_EUR = (n) => (Number(n) || 0).toLocaleString('it-IT', { style: 'currency', currency: 'EUR', maximumFractionDigits: 0 });
const WF_NUM = (n) => (Number(n) || 0).toLocaleString('it-IT', { maximumFractionDigits: 2 });

// Card di un singolo documento: viewer (sempre aperto) + AI + nota di stato auto-aggiornante.
// L'approvazione è UNA, a livello step (footer). Qui solo azioni NEGATIVE per-doc (quando
// `actionable`): "Rimanda indietro" (rework all'autore) / "Rifiuta", con commento obbligatorio.
function WfDocCard({ doc, instanceId, stepId, cme, actionable, user, pushToast, onChanged }) {
  const [aiMode, setAiMode] = React.useState(null);   // 'verify'|'summary'|'qa'|null
  const [aiText, setAiText] = React.useState('');
  const [aiLoading, setAiLoading] = React.useState(false);
  const [question, setQuestion] = React.useState('');
  const [comment, setComment] = React.useState('');
  const [saving, setSaving] = React.useState(false);
  const hdr = { 'Content-Type': 'application/json', ...(user?.id ? { 'X-Actor-Persona-Id': user.id } : {}) };

  const isCme = !!cme && (/\.xlsx?$/i.test(doc.title || '') || /spreadsheet|ms-excel/i.test(doc.mimeType || ''));
  const rv = doc.review; // { decision, comment, open, resolvedAt, ... } | null

  // Nota di stato DERIVATA (sempre coerente con lo stato reale).
  const statusNote = rv?.decision === 'returned' && rv.open
    ? { tone: 'warn', text: `Rimandato indietro per modifiche${rv.comment ? ` — “${rv.comment}”` : ''}. In attesa di una nuova versione dall'autore.` }
    : rv?.decision === 'rejected'
      ? { tone: 'err', text: `Rifiutato${rv.comment ? ` — “${rv.comment}”` : ''}.` }
      : { tone: 'info', text: 'In revisione.' };

  const decide = async (decision) => {
    if (saving) return;
    if (!comment.trim()) { pushToast({ title: 'Commento obbligatorio', desc: 'Indica il motivo per rimandare/rifiutare.', tone: 'warn' }); return; }
    setSaving(true);
    try {
      const r = await fetch(`/api/workflow-instances/${encodeURIComponent(instanceId)}/documents/${encodeURIComponent(doc.id)}/review`, {
        method: 'POST', credentials: 'same-origin', headers: hdr,
        body: JSON.stringify({ stepId, decision, comment: comment.trim() }),
      });
      const j = await r.json().catch(() => ({}));
      if (!r.ok) { pushToast({ title: 'Azione non riuscita', desc: j?.detail || j?.error || ('HTTP ' + r.status), tone: 'err' }); return; }
      pushToast({ title: decision === 'returned' ? 'Documento rimandato indietro' : 'Documento rifiutato', desc: doc.title, tone: decision === 'returned' ? 'warn' : 'err' });
      setComment('');
      if (typeof onChanged === 'function') onChanged();
    } catch (e) { pushToast({ title: 'Errore di rete', desc: String(e?.message || e), tone: 'err' }); }
    finally { setSaving(false); }
  };

  const runAi = async (mode) => {
    if (aiLoading) return;
    if (mode === 'qa' && !question.trim()) { pushToast({ title: 'Scrivi una domanda', tone: 'warn' }); return; }
    setAiMode(mode); setAiLoading(true); setAiText('');
    try {
      const r = await fetch(`/api/workflow-instances/${encodeURIComponent(instanceId)}/documents/${encodeURIComponent(doc.id)}/ai`, {
        method: 'POST', credentials: 'same-origin', headers: hdr,
        body: JSON.stringify({ mode, question: mode === 'qa' ? question.trim() : undefined }),
      });
      const j = await r.json().catch(() => ({}));
      if (!r.ok) { setAiText(`⚠ ${j?.detail || j?.error || ('HTTP ' + r.status)}`); return; }
      setAiText(j.data?.text || '(nessuna risposta)');
    } catch (e) { setAiText('⚠ Errore di rete: ' + String(e?.message || e)); }
    finally { setAiLoading(false); }
  };

  return (
    <div className="card" style={{ marginBottom: 12 }}>
      <div className="card-header">
        <div className="title"><Icon name="docs" size={12} /> {doc.title}</div>
        <div className="actions">
          <span style={{ fontSize: 10.5, color: 'var(--text-3)', fontFamily: 'var(--mono)' }}>{doc.type}{isCme ? ' · CME' : ''}{doc.fileSize ? ` · ${Math.round(doc.fileSize / 1024)} KB` : ''}</span>
          <a className="btn ghost sm" href={`/api/documents/${encodeURIComponent(doc.id)}/download`} target="_blank" rel="noreferrer" title="Scarica"><Icon name="download" size={11} /></a>
        </div>
      </div>
      <div className="card-body col" style={{ gap: 10 }}>
        {/* Nota di stato auto-aggiornante (derivata) + azioni negative per-documento */}
        <div className="row" style={{ gap: 8, alignItems: 'center', flexWrap: 'wrap', padding: '6px 10px', borderRadius: 8, border: '1px solid var(--line)', background: 'var(--bg-2)' }}>
          <Chip kind={statusNote.tone} dot>{rv?.decision === 'returned' && rv.open ? 'da rifare' : rv?.decision === 'rejected' ? 'rifiutato' : 'in revisione'}</Chip>
          <span style={{ fontSize: 11.5, color: 'var(--text-2)', flex: 1, minWidth: 160 }}>{statusNote.text}</span>
        </div>
        {actionable && (
          <div className="row" style={{ gap: 8, alignItems: 'center', flexWrap: 'wrap' }}>
            <input value={comment} onChange={(e) => setComment(e.target.value)} placeholder="motivo (obbligatorio per rimandare/rifiutare)" style={{ flex: 1, minWidth: 180, fontSize: 12 }} />
            <Btn variant="ghost" size="sm" disabled={saving || !comment.trim()} title={comment.trim() ? 'Rimanda all\'autore per modifiche' : 'Inserisci il motivo'} onClick={() => decide('returned')}>↩ Rimanda indietro</Btn>
            <Btn variant="ghost" size="sm" disabled={saving || !comment.trim()} title={comment.trim() ? 'Rifiuta il documento' : 'Inserisci il motivo'} onClick={() => decide('rejected')}>Rifiuta</Btn>
          </div>
        )}
        {/* Viewer sempre aperto: CME → ricostruzione tabella; PDF/img → DocViewerPane */}
        <div style={{ border: '1px solid var(--line)', borderRadius: 8, overflow: 'hidden' }}>
          {isCme ? (
            <div style={{ maxHeight: 520, overflow: 'auto' }}>
              <div className="row" style={{ gap: 12, padding: '8px 12px', background: 'var(--bg-2)', borderBottom: '1px solid var(--line)', fontSize: 11.5 }}>
                <span><strong>BAC</strong> {WF_EUR(cme.budget?.bacEur)}</span>
                <span><strong>Impegnato</strong> {WF_EUR(cme.budget?.committedEur)}</span>
                <span><strong>Residuo</strong> {WF_EUR(cme.budget?.residuoEur)}</span>
                <span><strong>Avanz.</strong> {cme.budget?.progressPct}%</span>
                <span className="spacer" /><span style={{ color: 'var(--text-3)' }}>{(cme.voci || []).length} voci</span>
              </div>
              <table className="tbl dense" style={{ fontSize: 10.5, width: '100%' }}>
                <thead><tr><th>WBS</th><th>Codice</th><th>Descrizione</th><th style={{ textAlign: 'right' }}>Qtà</th><th>UM</th><th style={{ textAlign: 'right' }}>P.U.</th><th style={{ textAlign: 'right' }}>Importo</th></tr></thead>
                <tbody>
                  {(cme.voci || []).map((v) => (
                    <tr key={v.id}>
                      <td className="mono">{v.wbsProgetto}</td><td className="mono">{v.codiceVoce}</td>
                      <td>{v.descrizione}</td>
                      <td className="num" style={{ textAlign: 'right' }}>{WF_NUM(v.quantitaTotale)}</td><td>{v.uom}</td>
                      <td className="num" style={{ textAlign: 'right' }}>{WF_NUM(v.prezzoUnitarioEur)}</td>
                      <td className="num mono" style={{ textAlign: 'right' }}>{WF_EUR(v.importoTotaleEur)}</td>
                    </tr>
                  ))}
                </tbody>
              </table>
            </div>
          ) : (
            <div style={{ height: 520, background: 'var(--bg-2)', display: 'flex' }}>
              {window.DocViewerPane
                ? <window.DocViewerPane docId={doc.id} mimeType={doc.mimeType} filename={doc.title} user={user} />
                : <div style={{ margin: 'auto', color: 'var(--text-3)', fontSize: 12 }}>Viewer non disponibile</div>}
            </div>
          )}
        </div>

        {/* AI 3 modi */}
        <div style={{ border: '1px solid var(--line)', borderRadius: 8, padding: '8px 10px' }}>
          <div className="row" style={{ gap: 6, alignItems: 'center', flexWrap: 'wrap' }}>
            <span style={{ fontSize: 10.5, color: 'var(--text-3)', textTransform: 'uppercase', letterSpacing: '.5px' }}><Icon name="sparkle" size={11} /> Assistente AI</span>
            <Btn variant={aiMode === 'verify' ? 'ai' : 'ghost'} size="sm" onClick={() => runAi('verify')} disabled={aiLoading}>Verifica vs budget</Btn>
            <Btn variant={aiMode === 'summary' ? 'ai' : 'ghost'} size="sm" onClick={() => runAi('summary')} disabled={aiLoading}>Riassunto</Btn>
            <span className="row" style={{ gap: 4, alignItems: 'center' }}>
              <input value={question} onChange={(e) => setQuestion(e.target.value)} placeholder="chiedi qualcosa…" style={{ fontSize: 11, width: 180 }} onKeyDown={(e) => { if (e.key === 'Enter') runAi('qa'); }} />
              <Btn variant={aiMode === 'qa' ? 'ai' : 'ghost'} size="sm" onClick={() => runAi('qa')} disabled={aiLoading}>Chiedi</Btn>
            </span>
          </div>
          {(aiLoading || aiText) && (
            <div style={{ marginTop: 8, fontSize: 11.5, whiteSpace: 'pre-wrap', lineHeight: 1.5, color: 'var(--text-2)' }}>
              {aiLoading ? 'L\'AI sta analizzando…' : aiText}
            </div>
          )}
        </div>
      </div>
    </div>
  );
}

function WfReviewPage() {
  const { user, seed, seedCustom, navigate, routeParam, pushToast } = useStore();
  const instanceId = routeParam ? String(routeParam).split('|')[0] : '';
  const [data, setData] = React.useState(null);
  const [error, setError] = React.useState(null);
  const [acting, setActing] = React.useState(false);
  const [stepComment, setStepComment] = React.useState('');
  const [selectedStepId, setSelectedStepId] = React.useState(null);  // step selezionato → documento mostrato a DX
  const [iterCollapsed, setIterCollapsed] = React.useState(false);   // iter nascosto → documento a tutta pagina
  const [docClosed, setDocClosed] = React.useState(false);           // documento chiuso → iter a tutta pagina
  const [narr, setNarr] = React.useState(null);                      // narrativa di processo (AI/derivata)
  const [narrLoading, setNarrLoading] = React.useState(false);
  const showIter = () => { setIterCollapsed(false); setDocClosed(false); };       // torna affiancati
  const collapseIter = () => { setIterCollapsed(true); setDocClosed(false); };    // documento a tutta pagina
  const closeDoc = () => { setDocClosed(true); setIterCollapsed(false); };        // iter a tutta pagina

  const hdr = user?.id ? { 'X-Actor-Persona-Id': user.id } : {};
  const jsonHdr = { 'Content-Type': 'application/json', ...hdr };

  const load = React.useCallback(() => {
    if (!instanceId) return;
    fetch(`/api/workflow-instances/${encodeURIComponent(instanceId)}/review`, { headers: hdr })
      .then((r) => r.json())
      .then((j) => { if (j.error) setError(j.error); else { setData(j.data); setError(null); } })
      .catch((e) => setError(String(e?.message || e)));
  }, [instanceId, user?.id]);
  React.useEffect(() => { load(); }, [load]);

  // Selezione di default: lo step attivo (così all'apertura il documento da approvare è già a destra).
  React.useEffect(() => {
    if (!data || selectedStepId !== null) return;
    const act = (data.steps || []).find((s) => s.status === 'active');
    setSelectedStepId(act ? act.stepId : (data.steps?.[0]?.stepId ?? null));
  }, [data, selectedStepId]);

  const goBack = () => navigate('alerts', '');

  if (!instanceId) return <div className="page"><div style={{ padding: 20 }}>Istanza non specificata.</div></div>;
  if (error) return <div className="page fade-in"><div style={{ marginBottom: 12 }}><span style={{ cursor: 'pointer', fontSize: 11.5 }} onClick={goBack}>← Le mie attività</span></div><div style={{ padding: 14, color: 'var(--err)' }}>Errore: {error}</div></div>;
  if (!data) return <div className="page"><div style={{ padding: 20, color: 'var(--text-3)' }}>Caricamento…</div></div>;

  const inst = data.instance;
  const steps = data.steps || [];
  const activeStep = steps.find((s) => s.status === 'active') || null;
  const reviewDocs = activeStep?.docs || [];        // documenti dello step ATTIVO (da approvare, DX)
  const selectedStep = steps.find((s) => s.stepId === selectedStepId) || null;  // step consultato (SX)
  const canAct = inst.status === 'in_progress' && !!activeStep;

  // Gate-ruolo lato FE (l'enforcement vero è il 403 backend).
  const approverRoleId = activeStep?.stepSnapshot?.approver?.kind === 'role' ? activeStep.stepSnapshot.approver.roleId : null;
  const myRoleIds = typeof effectiveRoleIdsForUser === 'function'
    ? effectiveRoleIdsForUser(user, seedCustom?.DELEGATIONS, seed?.PERSONAS)
    : (user?.roleIds || []);
  const isMyTurn = !approverRoleId || myRoleIds.includes(approverRoleId);

  // Gate rework: documenti dello step attivo rimandati indietro e ancora da rifare.
  const openReturns = reviewDocs.filter((d) => d.review?.decision === 'returned' && d.review?.open).length;
  const canApproveStep = openReturns === 0;

  const transition = async (decision) => {
    if (acting || !activeStep) return;
    if (decision === 'return' && !stepComment.trim()) { pushToast({ title: 'Commento obbligatorio per rimandare indietro', tone: 'warn' }); return; }
    setActing(true);
    try {
      const r = await fetch(`/api/workflow-instances/${encodeURIComponent(instanceId)}/transition`, {
        method: 'POST', credentials: 'same-origin', headers: jsonHdr,
        body: JSON.stringify({ stepId: activeStep.stepId, decision, comment: stepComment.trim() || undefined }),
      });
      const j = await r.json().catch(() => ({}));
      if (!r.ok) { pushToast({ title: 'Azione bloccata', desc: j?.detail || j?.error || ('HTTP ' + r.status), tone: 'err' }); return; }
      window.dispatchEvent(new Event('my_tasks_changed'));
      pushToast({ title: decision === 'approve' ? 'Step approvato' : decision === 'return' ? 'Rimandato allo step precedente' : 'Step rifiutato', desc: 'Audit registrato', tone: decision === 'reject' ? 'err' : 'ok' });
      setStepComment('');
      if (decision === 'approve' || decision === 'reject') goBack(); else load();
    } catch (e) { pushToast({ title: 'Errore di rete', desc: String(e?.message || e), tone: 'err' }); }
    finally { setActing(false); }
  };

  const stepColor = (st) => st === 'active' ? 'var(--info, #0891b2)' : st === 'completed' ? 'var(--ok, #059669)' : st === 'rejected' ? 'var(--err, #dc2626)' : st === 'skipped' ? 'var(--warn, #d97706)' : 'var(--text-3)';

  const loadNarrative = async () => {
    if (narrLoading) return;
    setNarrLoading(true);
    try {
      const r = await fetch(`/api/workflow-instances/${encodeURIComponent(instanceId)}/narrative`, { method: 'POST', credentials: 'same-origin', headers: jsonHdr });
      const j = await r.json().catch(() => ({}));
      if (!r.ok) { pushToast({ title: 'Narrativa non disponibile', desc: j?.detail || j?.error || ('HTTP ' + r.status), tone: 'err' }); return; }
      setNarr(j.data || null);
    } catch (e) { pushToast({ title: 'Errore di rete', desc: String(e?.message || e), tone: 'err' }); }
    finally { setNarrLoading(false); }
  };

  return (
    <div className="page fade-in" style={{ paddingBottom: 80 }}>
      <div style={{ marginBottom: 10, fontSize: 11.5, color: 'var(--text-2)' }}>
        <span style={{ cursor: 'pointer' }} onClick={goBack}>← Le mie attività</span>
      </div>
      <div className="page-header" style={{ alignItems: 'flex-start', marginBottom: 14 }}>
        <div>
          <div className="eyebrow">Approvazione workflow · {inst.entityType} {inst.entityId}</div>
          <h1 className="page-title" style={{ marginTop: 6 }}>{inst.workflowSnapshot?.name || inst.workflowId}</h1>
          <div className="row" style={{ gap: 8, marginTop: 8, alignItems: 'center' }}>
            <Chip kind={inst.status === 'in_progress' ? 'info' : inst.status === 'completed' ? 'ok' : 'err'} dot>{inst.status}</Chip>
            <span style={{ fontSize: 11, color: 'var(--text-3)' }}>avviato {new Date(inst.startedAt).toLocaleString('it-IT')}</span>
          </div>
        </div>
      </div>

      {/* Narrativa del processo (agente AI, modello piccolo) — on-demand, con fallback derivato */}
      <div style={{ marginBottom: 14, padding: '10px 12px', border: '1px solid var(--line)', borderRadius: 8, background: 'var(--bg-2)' }}>
        <div className="row" style={{ gap: 8, alignItems: 'center', flexWrap: 'wrap' }}>
          <span style={{ fontSize: 10.5, color: 'var(--text-3)', textTransform: 'uppercase', letterSpacing: '.5px' }}><Icon name="sparkle" size={11} /> Narrativa del processo</span>
          <Btn variant="ai" size="sm" onClick={loadNarrative} disabled={narrLoading}>{narrLoading ? 'Genero…' : (narr ? 'Aggiorna' : 'Genera narrativa (AI)')}</Btn>
          {narr && <span style={{ fontSize: 10, color: 'var(--text-3)' }}>{narr.fallback ? 'fonte: stato (derivata)' : `AI · ${narr.model || narr.provider || ''}`}</span>}
        </div>
        {narr && <div style={{ marginTop: 8, fontSize: 12.5, lineHeight: 1.5, color: 'var(--text-1, var(--text-2))' }}>{narr.text}</div>}
      </div>

      {/* Due aree con ruolo fisso che si espandono a vicenda: SX navigazione · DX documento */}
      <div style={{ display: 'flex', gap: 14, alignItems: 'flex-start' }}>
        {/* SX — NAVIGAZIONE iter (a tutta pagina se il documento è chiuso) */}
        {!iterCollapsed && (
          <div style={docClosed ? { flex: 1, minWidth: 0 } : { width: 330, flexShrink: 0, position: 'sticky', top: 12 }}>
            <div className="card">
              <div className="card-header" style={{ justifyContent: 'space-between' }}>
                <div className="title"><Icon name="workflow" size={12} /> Iter di approvazione</div>
                {docClosed
                  ? <Btn variant="ghost" size="sm" onClick={showIter} title="Riapri il documento">▸ Mostra documento</Btn>
                  : <Btn variant="ghost" size="sm" onClick={collapseIter} title="Documento a tutta pagina">« Comprimi</Btn>}
              </div>
              <div className="card-body col" style={{ gap: 0 }}>
                <div style={{ fontSize: 10.5, color: 'var(--text-3)', paddingBottom: 6 }}>Clicca uno step per vederne il documento{docClosed ? '' : ' a destra'}.</div>
                {steps.map((s, i) => {
                  const sel = s.stepId === selectedStepId;
                  const nDocs = (s.docs || []).length;
                  const nRework = (s.docs || []).filter((d) => d.review?.decision === 'returned' && d.review?.open).length;
                  return (
                    <div key={s.stepId} onClick={() => { setSelectedStepId(s.stepId); if (docClosed) showIter(); }}
                      className="row" style={{ gap: 10, alignItems: 'flex-start', padding: '8px 8px', cursor: 'pointer', borderRadius: 6, background: sel ? 'var(--accent-soft, var(--bg-2))' : 'transparent', borderLeft: sel ? '3px solid var(--accent, #c2410c)' : '3px solid transparent', borderBottom: i < steps.length - 1 ? '1px dashed var(--line)' : 'none' }}>
                      <div style={{ width: 10, height: 10, borderRadius: '50%', marginTop: 4, background: stepColor(s.status), flexShrink: 0 }} />
                      <div style={{ minWidth: 0, flex: 1 }}>
                        <div style={{ fontSize: 12, fontWeight: sel ? 700 : (s.status === 'active' ? 600 : 500) }}>{s.stepName}{s.status === 'active' ? ' · in corso' : ''}</div>
                        <div style={{ fontSize: 10, color: 'var(--text-3)' }}>
                          {s.stepSnapshot?.approver?.kind === 'matrix' ? 'matrix' : (s.stepSnapshot?.approver?.roleId || '—')}
                          {s.stepSnapshot?.slaDays ? ` · SLA ${s.stepSnapshot.slaDays}g` : ''}
                          {nDocs ? ` · ${nDocs} doc` : ' · nessun doc'}
                          {nRework > 0 ? <span style={{ color: 'var(--warn, #d97706)' }}> · {nRework} da rifare</span> : ''}
                          {s.decisionBy ? ` · ${s.decisionBy}` : ''}
                        </div>
                      </div>
                      <Chip kind={s.status === 'active' ? 'info' : s.status === 'completed' ? 'ok' : s.status === 'rejected' ? 'err' : s.status === 'skipped' ? 'warn' : ''} dot>{s.status}</Chip>
                    </div>
                  );
                })}
              </div>
            </div>
          </div>
        )}

        {/* DX — DOCUMENTO dello step selezionato (nascosto se docClosed) */}
        {!docClosed && (
          <div style={{ flex: 1, minWidth: 0 }}>
            {iterCollapsed && (
              <Btn variant="ghost" size="sm" onClick={showIter} style={{ marginBottom: 10 }} title="Riapri la navigazione">▸ Mostra iter</Btn>
            )}
            {(() => {
              const viewing = selectedStep || activeStep;
              if (!viewing) {
                return <div style={{ fontSize: 11.5, color: 'var(--text-3)', padding: '8px 10px' }}>
                  {inst.status === 'completed' ? 'Workflow completato.' : inst.status === 'rejected' ? 'Workflow rifiutato.' : 'Nessuno step selezionato.'} Clicca uno step a sinistra.
                </div>;
              }
              const isActiveView = !!activeStep && viewing.stepId === activeStep.stepId;
              const actionable = isActiveView && canAct && isMyTurn;
              const docs = viewing.docs || [];
              const viewReturns = docs.filter((d) => d.review?.decision === 'returned' && d.review?.open).length;
              return (
                <>
                  <div className="row" style={{ alignItems: 'center', marginBottom: 8 }}>
                    <span className="eyebrow">
                      {viewing.stepName} — {isActiveView ? 'documenti da approvare' : 'consultazione'} ({docs.length} doc)
                      {viewReturns > 0 ? ` · ${viewReturns} da rifare` : ''}
                    </span>
                    <span className="spacer" />
                    <Btn variant="ghost" size="sm" onClick={closeDoc} title="Chiudi il documento e allarga l'iter">Chiudi documento »</Btn>
                  </div>
                  {docs.length === 0
                    ? <div style={{ fontSize: 11.5, color: 'var(--text-3)' }}>Nessun documento per questo step. Configura i tipi documento dello step nel Customizing del workflow, o carica i documenti nel progetto.</div>
                    : docs.map((d) => (
                        <WfDocCard key={`${viewing.stepId}:${d.id}`} doc={d} instanceId={instanceId} stepId={viewing.stepId} cme={data.cme} actionable={actionable} user={user} pushToast={pushToast} onChanged={load} />
                      ))}
                </>
              );
            })()}
          </div>
        )}
      </div>

      {/* Footer — positivo a livello step (un'azione); il negativo è per-documento sulle card.
          Per step SENZA documenti, le azioni negative restano qui a livello step. */}
      {canAct && (() => {
        const activeHasDocs = (activeStep.docs || []).length > 0;
        return (
          <div style={{ position: 'fixed', left: 0, right: 0, bottom: 0, background: 'var(--bg-1, #fff)', borderTop: '1px solid var(--line)', padding: '10px 24px', display: 'flex', gap: 10, alignItems: 'center', zIndex: 50 }}>
            <div style={{ fontSize: 11.5, color: 'var(--text-2)', flexShrink: 0 }}>
              {!isMyTurn
                ? <>⚠ Step <strong>{activeStep.stepName}</strong> riservato a <strong>{approverRoleId}</strong></>
                : openReturns > 0
                  ? <>⚠ <strong>{openReturns}</strong> documento/i da rifare prima di approvare</>
                  : <>Step <strong>{activeStep.stepName}</strong>{activeHasDocs ? ' — rivedi i documenti e approva' : ' — decidi'}</>}
            </div>
            {!activeHasDocs && (
              <input value={stepComment} onChange={(e) => setStepComment(e.target.value)} placeholder="commento (obbligatorio per rimandare / rifiutare)" style={{ flex: 1, fontSize: 12 }} />
            )}
            <span className="spacer" />
            <Btn variant="ghost" size="sm" onClick={goBack} disabled={acting}>Chiudi</Btn>
            {!activeHasDocs && <Btn variant="ghost" size="sm" onClick={() => transition('return')} disabled={acting || !isMyTurn}>↩ Rimanda indietro</Btn>}
            {!activeHasDocs && <Btn variant="ghost" size="sm" onClick={() => transition('reject')} disabled={acting || !isMyTurn}>Rifiuta</Btn>}
            <Btn variant="primary" size="sm" onClick={() => transition('approve')} disabled={acting || !isMyTurn || !canApproveStep} title={!isMyTurn ? `Riservato al ruolo ${approverRoleId}` : (canApproveStep ? 'Approva lo step e prosegui' : 'Risolvi prima i documenti rimandati indietro')}>{acting ? '…' : 'Approva e prosegui'}</Btn>
          </div>
        );
      })()}
    </div>
  );
}

Object.assign(window, { WfReviewPage, WfDocCard });
