// ============================================================
// customizing-sub-3.jsx — FIX P1 (sessione 34)
// Sostituisce le 3 sezioni Customizing che erano read-only-from-seed:
//   - CustChecklists  → /api/config/checklist-rules
//   - CustAI          → /api/config/ai-agents  +  /api/config/ai-policy
//   - CustUsers       → /api/config/users      +  reset-password
//
// Caricato in `apps/web/public/app/index.html` DOPO customizing-sub-1.jsx
// e customizing-sub-2.jsx, quindi i `Object.assign(window, …)` qui
// sovrascrivono le versioni stub precedenti.
// ============================================================

// ----- Helper comune -----
function useApiList(url) {
  const [items, setItems] = React.useState(null);
  const [loading, setLoading] = React.useState(true);
  const [error, setError] = React.useState(null);
  const reload = React.useCallback(async () => {
    try {
      setLoading(true);
      setError(null);
      const r = await fetch(url, { credentials: 'same-origin', cache: 'no-store' });
      if (!r.ok) throw new Error('HTTP ' + r.status);
      const j = await r.json();
      setItems(Array.isArray(j.data) ? j.data : []);
    } catch (e) {
      setError(String(e?.message || e));
    } finally {
      setLoading(false);
    }
  }, [url]);
  React.useEffect(() => { reload(); }, [reload]);
  return { items, setItems, loading, error, reload };
}

function actorHeaders(userId) {
  return {
    'Content-Type': 'application/json',
    'X-Actor-Persona-Id': userId || '',
  };
}

// =============================================================
// CustChecklists — list + new + edit + delete su checklist_rule
// =============================================================
function CustChecklists() {
  const { user, pushToast, seedCustom } = useStore();
  const docs = seedCustom.DOC_TYPES || [];
  const { items: rules, setItems: setRules, loading, error, reload } =
    useApiList('/api/config/checklist-rules');

  const [sel, setSel] = React.useState(null);
  const [filter, setFilter] = React.useState('*');
  const [showNew, setShowNew] = React.useState(false);

  const visibleRules = rules || [];
  const entityTypes = Array.from(new Set(visibleRules.map((r) => r.entityType)));
  const rows = filter === '*' ? visibleRules : visibleRules.filter((r) => r.entityType === filter);

  async function deleteRule(id) {
    if (!confirm('Disattivare la regola? È un soft-delete: resterà tracciata ma non sarà più applicata.')) return;
    try {
      const r = await fetch(`/api/config/checklist-rules/${encodeURIComponent(id)}`, {
        method: 'DELETE',
        headers: actorHeaders(user?.id),
        credentials: 'same-origin',
      });
      if (!r.ok) throw new Error((await r.json().catch(() => ({}))).error || ('HTTP ' + r.status));
      setRules((prev) => (prev || []).map((x) => (x.id === id ? { ...x, active: false } : x)));
      pushToast({ title: 'Regola disattivata', tone: 'ok' });
    } catch (e) {
      pushToast({ title: 'Errore disattivazione', desc: String(e), tone: 'err' });
    }
  }

  return (
    <>
      <div className="row" style={{ gap: 8, marginBottom: 12, alignItems: 'center' }}>
        <div className="row" style={{ gap: 2, background: 'var(--bg-2)', padding: 2, borderRadius: 6 }}>
          <button className={`btn sm ${filter === '*' ? 'primary' : 'ghost'}`} onClick={() => setFilter('*')}>Tutte</button>
          {entityTypes.map((t) => (
            <button key={t} className={`btn sm ${filter === t ? 'primary' : 'ghost'}`} onClick={() => setFilter(t)}>{t}</button>
          ))}
        </div>
        <span className="spacer" />
        <ConfigWriteBtn onClick={() => setShowNew(true)}><Icon name="plus" size={11}/> Nuova regola</ConfigWriteBtn>
      </div>

      {loading && <div style={{ fontSize: 12, color: 'var(--text-3)', padding: 14 }}>Caricamento…</div>}
      {error && <div style={{ fontSize: 12, color: 'var(--err)', padding: 14 }}>Errore: {error}</div>}

      {!loading && !error && (
        <table className="tbl dense">
          <thead><tr>
            <th style={{ width: 100 }}>ID</th>
            <th>Nome</th>
            <th style={{ width: 90 }}>Entità</th>
            <th style={{ width: 70, textAlign: 'center' }}>Prio</th>
            <th>Condizione</th>
            <th style={{ width: 60, textAlign: 'center' }}>Req</th>
            <th style={{ width: 60, textAlign: 'center' }}>Opt</th>
            <th style={{ width: 90, textAlign: 'center' }}>Override</th>
            <th style={{ width: 70, textAlign: 'center' }}>Stato</th>
          </tr></thead>
          <tbody>
            {rows.map((r) => (
              <tr key={r.id} className="clickable" onClick={() => setSel(r)}>
                <td className="mono" style={{ fontSize: 10.5 }}>{r.id}</td>
                <td style={{ fontWeight: 500 }}>{r.name}</td>
                <td><Chip>{r.entityType}</Chip></td>
                <td className="mono num">{r.priority}</td>
                <td style={{ fontSize: 11.5, color: 'var(--text-2)' }}>
                  {typeof window.formatCondition === 'function' ? window.formatCondition(r.conditions) : JSON.stringify(r.conditions)}
                </td>
                <td className="mono num">{(r.required || []).length}</td>
                <td className="mono num" style={{ color: 'var(--text-3)' }}>{(r.optional || []).length}</td>
                <td style={{ textAlign: 'center' }}>{r.allowOverride ? <Chip>sì</Chip> : <Chip kind="err">no</Chip>}</td>
                <td style={{ textAlign: 'center' }}><Chip kind={r.active ? 'ok' : ''} dot>{r.active ? 'attiva' : 'off'}</Chip></td>
              </tr>
            ))}
          </tbody>
        </table>
      )}

      <ChecklistRuleEditModal
        sel={sel}
        onClose={() => setSel(null)}
        onSaved={(updated) => {
          setRules((prev) => (prev || []).map((x) => (x.id === updated.id ? updated : x)));
        }}
        onDelete={deleteRule}
        docs={docs}
      />
      <ChecklistRuleNewModal
        open={showNew}
        onClose={() => setShowNew(false)}
        docs={docs}
        existingIds={visibleRules.map((r) => r.id)}
        onCreated={(row) => {
          setRules((prev) => [row, ...(prev || [])]);
          reload();
        }}
      />
    </>
  );
}

function ChecklistRuleEditModal({ sel, onClose, onSaved, onDelete, docs }) {
  const { user, pushToast } = useStore();
  const [form, setForm] = React.useState(null);
  const [saving, setSaving] = React.useState(false);

  React.useEffect(() => {
    setForm(sel ? {
      name: sel.name,
      priority: sel.priority,
      required: sel.required || [],
      optional: sel.optional || [],
      allowOverride: !!sel.allowOverride,
      active: !!sel.active,
    } : null);
  }, [sel]);

  if (!sel || !form) return null;

  const set = (k, v) => setForm((f) => ({ ...f, [k]: v }));
  const isDirty =
    form.name !== sel.name ||
    form.priority !== sel.priority ||
    JSON.stringify(form.required) !== JSON.stringify(sel.required || []) ||
    JSON.stringify(form.optional) !== JSON.stringify(sel.optional || []) ||
    form.allowOverride !== sel.allowOverride ||
    form.active !== sel.active;

  async function handleSave() {
    if (!isDirty || saving) return;
    setSaving(true);
    try {
      const body = {
        name: form.name,
        priority: form.priority,
        required: form.required,
        optional: form.optional,
        allowOverride: form.allowOverride,
        active: form.active,
      };
      const r = await fetch(`/api/config/checklist-rules/${encodeURIComponent(sel.id)}`, {
        method: 'PATCH',
        headers: actorHeaders(user?.id),
        credentials: 'same-origin',
        body: JSON.stringify(body),
      });
      const j = await r.json().catch(() => ({}));
      if (!r.ok) throw new Error(j.error || 'HTTP ' + r.status);
      pushToast({ title: 'Regola aggiornata', tone: 'ok' });
      onSaved(j.data);
      onClose();
    } catch (e) {
      pushToast({ title: 'Errore aggiornamento', desc: String(e), tone: 'err' });
    } finally {
      setSaving(false);
    }
  }

  function toggleDoc(code, list) {
    const arr = form[list];
    const other = list === 'required' ? 'optional' : 'required';
    if (arr.includes(code)) set(list, arr.filter((x) => x !== code));
    else {
      set(list, [...arr, code]);
      if (form[other].includes(code)) set(other, form[other].filter((x) => x !== code));
    }
  }

  return (
    <Modal
      open={!!sel}
      onClose={onClose}
      title={`Regola · ${sel.name}`}
      size="lg"
      footer={
        <>
          <Btn variant="ghost" size="sm" onClick={onClose}>Chiudi</Btn>
          {sel.active && <Btn variant="ghost" size="sm" onClick={() => onDelete(sel.id)}><Icon name="trash" size={11}/> Disattiva</Btn>}
          <Btn variant="primary" size="sm" disabled={!isDirty || saving} onClick={handleSave}>
            {saving ? 'Salvataggio…' : (isDirty ? 'Salva' : 'Salvato')}
          </Btn>
        </>
      }
    >
      <div className="col" style={{ gap: 14 }}>
        <div className="grid grid-3">
          <div className="field"><label>ID</label><input value={sel.id} disabled style={{ fontFamily: 'var(--font-mono)', fontSize: 11 }}/></div>
          <div className="field"><label>Nome</label><input value={form.name} onChange={(e) => set('name', e.target.value)}/></div>
          <div className="field"><label>Priorità</label><input type="number" value={form.priority} onChange={(e) => set('priority', Number(e.target.value))}/></div>
        </div>
        <div className="grid grid-2">
          <div className="field"><label>Override consentito</label>
            <select value={form.allowOverride ? 'yes' : 'no'} onChange={(e) => set('allowOverride', e.target.value === 'yes')}>
              <option value="yes">Sì — utenti privilegiati possono saltare doc</option>
              <option value="no">No — blocca sempre</option>
            </select>
          </div>
          <div className="field"><label>Stato</label>
            <select value={form.active ? 'on' : 'off'} onChange={(e) => set('active', e.target.value === 'on')}>
              <option value="on">Attiva</option>
              <option value="off">Disattivata</option>
            </select>
          </div>
        </div>

        <div>
          <div className="eyebrow" style={{ marginBottom: 6 }}>Documenti</div>
          <div style={{ padding: 8, border: '1px solid var(--line)', borderRadius: 4, maxHeight: 220, overflowY: 'auto' }}>
            <div style={{ display: 'flex', flexWrap: 'wrap', gap: 4 }}>
              {docs.map((d) => {
                const inReq = form.required.includes(d.code);
                const inOpt = form.optional.includes(d.code);
                return (
                  <div key={d.code} className="row" style={{ gap: 2, padding: '2px 6px', border: '1px solid var(--line)', borderRadius: 4, fontSize: 10.5 }}>
                    <span style={{ fontFamily: 'var(--font-mono)' }}>{d.code}</span>
                    <span style={{ color: 'var(--text-3)', marginLeft: 4, marginRight: 4 }}>·</span>
                    <button className={`btn sm ${inReq ? 'primary' : 'ghost'}`} style={{ fontSize: 9, padding: '1px 4px' }} onClick={() => toggleDoc(d.code, 'required')}>req</button>
                    <button className={`btn sm ${inOpt ? 'primary' : 'ghost'}`} style={{ fontSize: 9, padding: '1px 4px' }} onClick={() => toggleDoc(d.code, 'optional')}>opt</button>
                  </div>
                );
              })}
            </div>
          </div>
          <div style={{ marginTop: 6, fontSize: 10.5, color: 'var(--text-3)' }}>
            <strong style={{ color: 'var(--text-2)' }}>Richiesti ({form.required.length}):</strong> {form.required.join(', ') || '—'}
            {' · '}
            <strong style={{ color: 'var(--text-2)' }}>Opzionali ({form.optional.length}):</strong> {form.optional.join(', ') || '—'}
          </div>
        </div>

        <div style={{ fontSize: 10.5, color: 'var(--text-3)' }}>
          <Icon name="info" size={10}/> Le modifiche al campo <code>conditions</code> si fanno via API (non da questo modal MVP). Vedi <code>docs/checklist-engine.md</code>.
        </div>
      </div>
    </Modal>
  );
}

function ChecklistRuleNewModal({ open, onClose, docs, existingIds, onCreated }) {
  const { user, pushToast } = useStore();
  const [form, setForm] = React.useState({
    name: '',
    entityType: 'rda',
    priority: 100,
    required: [],
    optional: [],
    allowOverride: true,
    active: true,
  });
  const [saving, setSaving] = React.useState(false);

  React.useEffect(() => {
    if (open) {
      setForm({ name: '', entityType: 'rda', priority: 100, required: [], optional: [], allowOverride: true, active: true });
    }
  }, [open]);

  if (!open) return null;
  const set = (k, v) => setForm((f) => ({ ...f, [k]: v }));
  const valid = form.name.trim().length > 0;

  function toggleDoc(code, list) {
    const arr = form[list];
    const other = list === 'required' ? 'optional' : 'required';
    if (arr.includes(code)) set(list, arr.filter((x) => x !== code));
    else {
      set(list, [...arr, code]);
      if (form[other].includes(code)) set(other, form[other].filter((x) => x !== code));
    }
  }

  async function handleCreate() {
    if (!valid || saving) return;
    setSaving(true);
    try {
      const body = {
        name: form.name.trim(),
        entityType: form.entityType,
        priority: form.priority,
        required: form.required,
        optional: form.optional,
        conditions: {},
        allowOverride: form.allowOverride,
        active: form.active,
      };
      const r = await fetch('/api/config/checklist-rules', {
        method: 'POST',
        headers: actorHeaders(user?.id),
        credentials: 'same-origin',
        body: JSON.stringify(body),
      });
      const j = await r.json().catch(() => ({}));
      if (!r.ok) throw new Error(j.error || 'HTTP ' + r.status);
      pushToast({ title: 'Regola creata', tone: 'ok' });
      onCreated(j.data);
      onClose();
    } catch (e) {
      pushToast({ title: 'Errore creazione', desc: String(e), tone: 'err' });
    } finally {
      setSaving(false);
    }
  }

  return (
    <Modal
      open={open}
      onClose={onClose}
      title="Nuova regola checklist"
      size="lg"
      footer={
        <>
          <Btn variant="ghost" size="sm" onClick={onClose}>Annulla</Btn>
          <Btn variant="primary" size="sm" disabled={!valid || saving} onClick={handleCreate}>
            {saving ? 'Creazione…' : 'Crea regola'}
          </Btn>
        </>
      }
    >
      <div className="col" style={{ gap: 14 }}>
        <div style={{ fontSize: 11.5, color: 'var(--text-2)', lineHeight: 1.5 }}>
          Le regole determinano quali documenti sono richiesti su un'entità (RdA, vendor, progetto, workflow_step). Le condizioni complesse (espressioni su campi entità) si modificano via API.
        </div>

        <div className="grid grid-3">
          <div className="field"><label>Nome <span style={{ color: 'var(--err)' }}>*</span></label>
            <input value={form.name} onChange={(e) => set('name', e.target.value)} placeholder="es. RdA con vendor extra-UE"/>
          </div>
          <div className="field"><label>Entità target</label>
            <select value={form.entityType} onChange={(e) => set('entityType', e.target.value)}>
              <option value="rda">rda</option>
              <option value="project">project</option>
              <option value="vendor">vendor</option>
              <option value="workflow_step">workflow_step</option>
            </select>
          </div>
          <div className="field"><label>Priorità</label>
            <input type="number" min={0} max={1000} value={form.priority} onChange={(e) => set('priority', Number(e.target.value))}/>
          </div>
        </div>

        <div>
          <div className="eyebrow" style={{ marginBottom: 6 }}>Documenti</div>
          <div style={{ padding: 8, border: '1px solid var(--line)', borderRadius: 4, maxHeight: 200, overflowY: 'auto' }}>
            <div style={{ display: 'flex', flexWrap: 'wrap', gap: 4 }}>
              {docs.map((d) => {
                const inReq = form.required.includes(d.code);
                const inOpt = form.optional.includes(d.code);
                return (
                  <div key={d.code} className="row" style={{ gap: 2, padding: '2px 6px', border: '1px solid var(--line)', borderRadius: 4, fontSize: 10.5 }}>
                    <span style={{ fontFamily: 'var(--font-mono)' }}>{d.code}</span>
                    <span style={{ color: 'var(--text-3)', marginLeft: 4, marginRight: 4 }}>·</span>
                    <button className={`btn sm ${inReq ? 'primary' : 'ghost'}`} style={{ fontSize: 9, padding: '1px 4px' }} onClick={() => toggleDoc(d.code, 'required')}>req</button>
                    <button className={`btn sm ${inOpt ? 'primary' : 'ghost'}`} style={{ fontSize: 9, padding: '1px 4px' }} onClick={() => toggleDoc(d.code, 'optional')}>opt</button>
                  </div>
                );
              })}
            </div>
          </div>
          <div style={{ marginTop: 6, fontSize: 10.5, color: 'var(--text-3)' }}>
            <strong style={{ color: 'var(--text-2)' }}>Richiesti ({form.required.length}):</strong> {form.required.join(', ') || '—'}
            {' · '}
            <strong style={{ color: 'var(--text-2)' }}>Opzionali ({form.optional.length}):</strong> {form.optional.join(', ') || '—'}
          </div>
        </div>

        <div className="row" style={{ gap: 14 }}>
          <label className="row" style={{ gap: 6, fontSize: 11.5, cursor: 'pointer' }}>
            <input type="checkbox" checked={form.allowOverride} onChange={(e) => set('allowOverride', e.target.checked)}/>
            Override manuale consentito
          </label>
          <label className="row" style={{ gap: 6, fontSize: 11.5, cursor: 'pointer' }}>
            <input type="checkbox" checked={form.active} onChange={(e) => set('active', e.target.checked)}/>
            Attiva subito
          </label>
        </div>

        {existingIds.length > 0 && (
          <div style={{ fontSize: 10.5, color: 'var(--text-3)' }}>
            <Icon name="info" size={10}/> {existingIds.length} regole già esistenti. Le condizioni avanzate si modificano dopo via PATCH.
          </div>
        )}
      </div>
    </Modal>
  );
}

// =============================================================
// CustAI — agents + policy live
// =============================================================
function CustAI() {
  const { user, pushToast } = useStore();
  const { items: agents, setItems: setAgents, loading: la, error: ea, reload: reloadAgents } =
    useApiList('/api/config/ai-agents');
  const [policy, setPolicy] = React.useState(null);
  const [loadingPolicy, setLoadingPolicy] = React.useState(true);
  const [policyError, setPolicyError] = React.useState(null);

  const loadPolicy = React.useCallback(async () => {
    try {
      setLoadingPolicy(true);
      setPolicyError(null);
      const r = await fetch('/api/config/ai-policy', { credentials: 'same-origin', cache: 'no-store' });
      if (!r.ok) throw new Error('HTTP ' + r.status);
      const j = await r.json();
      setPolicy(j.data);
    } catch (e) {
      setPolicyError(String(e?.message || e));
    } finally {
      setLoadingPolicy(false);
    }
  }, []);
  React.useEffect(() => { loadPolicy(); }, [loadPolicy]);

  const [sel, setSel] = React.useState(null);
  const [showNew, setShowNew] = React.useState(false);
  const [showPolicyEdit, setShowPolicyEdit] = React.useState(false);
  const [showAutonomyEdit, setShowAutonomyEdit] = React.useState(false);

  async function deleteAgent(id) {
    if (!confirm('Disattivare l\'agent? Soft-delete: resterà tracciato ma non chiamabile.')) return;
    try {
      const r = await fetch(`/api/config/ai-agents/${encodeURIComponent(id)}`, {
        method: 'DELETE',
        headers: actorHeaders(user?.id),
        credentials: 'same-origin',
      });
      if (!r.ok) throw new Error((await r.json().catch(() => ({}))).error || 'HTTP ' + r.status);
      setAgents((prev) => (prev || []).map((x) => (x.id === id ? { ...x, active: false } : x)));
      pushToast({ title: 'Agent disattivato', tone: 'ok' });
    } catch (e) {
      pushToast({ title: 'Errore', desc: String(e), tone: 'err' });
    }
  }

  return (
    <>
      <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr 1fr 1fr', gap: 10, marginBottom: 16 }}>
        <div className="card" style={{ padding: 12 }}>
          <div className="eyebrow">Modello default</div>
          <div style={{ fontSize: 14, fontWeight: 500, marginTop: 4 }}>{policy?.defaultModel || '—'}</div>
        </div>
        <div className="card" style={{ padding: 12 }}>
          <div className="eyebrow">Fallback</div>
          <div style={{ fontSize: 14, marginTop: 4 }}>{policy?.fallbackModel || '—'}</div>
        </div>
        <div className="card" style={{ padding: 12 }}>
          <div className="eyebrow">Data residency</div>
          <div style={{ fontSize: 14, marginTop: 4 }}>{policy ? <Chip kind="ok">{policy.dataResidency}</Chip> : '—'}</div>
        </div>
        <div className="card" style={{ padding: 12 }}>
          <div className="eyebrow">PII redaction</div>
          <div style={{ fontSize: 14, marginTop: 4 }}>{policy ? (policy.piiRedaction ? <Chip kind="ok">on</Chip> : <Chip kind="err">off</Chip>) : '—'}</div>
        </div>
      </div>

      <div className="row" style={{ marginBottom: 12, alignItems: 'center' }}>
        <div className="eyebrow">Agenti AI ({(agents || []).length})</div>
        <span className="spacer" />
        <Btn variant="ghost" size="sm" onClick={() => setShowPolicyEdit(true)} disabled={!policy}><Icon name="settings" size={11}/> Modifica policy globale</Btn>
        <Btn variant="ghost" size="sm" onClick={() => setShowAutonomyEdit(true)}><Icon name="shield" size={11}/> Autonomia tool</Btn>
        <ConfigWriteBtn onClick={() => setShowNew(true)}><Icon name="plus" size={11}/> Nuovo agent</ConfigWriteBtn>
      </div>

      {la && <div style={{ fontSize: 12, color: 'var(--text-3)' }}>Caricamento agents…</div>}
      {ea && <div style={{ fontSize: 12, color: 'var(--err)' }}>Errore: {ea}</div>}

      {!la && !ea && (
        <table className="tbl dense">
          <thead><tr>
            <th style={{ width: 140 }}>Code</th>
            <th>Nome</th>
            <th style={{ width: 160 }}>Modello</th>
            <th style={{ width: 90, textAlign: 'right' }}>Temp</th>
            <th style={{ width: 110, textAlign: 'center' }}>Provider</th>
            <th style={{ width: 100, textAlign: 'center' }}>Autonomia</th>
            <th style={{ width: 70, textAlign: 'center' }}>Stato</th>
          </tr></thead>
          <tbody>
            {(agents || []).map((a) => (
              <tr key={a.id} className="clickable" onClick={() => setSel(a)}>
                <td><Chip kind="ai">{a.code}</Chip></td>
                <td style={{ fontWeight: 500 }}>{a.name}</td>
                <td style={{ fontSize: 11.5 }}>{a.model}</td>
                <td className="num mono">{Number(a.temperature).toFixed(2)}</td>
                <td style={{ textAlign: 'center', fontSize: 11 }}>{a.provider}</td>
                <td style={{ textAlign: 'center' }}><Chip kind={a.autonomy === 'auto' ? 'warn' : 'info'}>{a.autonomy}</Chip></td>
                <td style={{ textAlign: 'center' }}><Chip kind={a.active ? 'ok' : ''} dot>{a.active ? 'on' : 'off'}</Chip></td>
              </tr>
            ))}
          </tbody>
        </table>
      )}

      <AiAgentEditModal
        sel={sel}
        onClose={() => setSel(null)}
        onSaved={(u) => setAgents((prev) => (prev || []).map((x) => x.id === u.id ? u : x))}
        onDelete={deleteAgent}
      />
      <AiAgentNewModal
        open={showNew}
        onClose={() => setShowNew(false)}
        onCreated={(row) => { setAgents((prev) => [row, ...(prev || [])]); reloadAgents(); }}
      />
      <AiPolicyEditModal
        open={showPolicyEdit}
        onClose={() => setShowPolicyEdit(false)}
        policy={policy}
        onSaved={(p) => setPolicy(p)}
      />
      <AiAutonomyPolicyEditModal
        open={showAutonomyEdit}
        onClose={() => setShowAutonomyEdit(false)}
      />
    </>
  );
}

function AiAgentEditModal({ sel, onClose, onSaved, onDelete }) {
  const { user, pushToast } = useStore();
  const [form, setForm] = React.useState(null);
  const [saving, setSaving] = React.useState(false);

  React.useEffect(() => {
    if (sel) setForm({
      code: sel.code,
      name: sel.name,
      description: sel.description || '',
      provider: sel.provider,
      model: sel.model,
      temperature: sel.temperature,
      maxTokens: sel.maxTokens,
      autonomy: sel.autonomy,
      systemPrompt: sel.systemPrompt,
      active: sel.active,
    });
    else setForm(null);
  }, [sel]);

  if (!sel || !form) return null;
  const set = (k, v) => setForm((f) => ({ ...f, [k]: v }));
  const isDirty =
    form.code !== sel.code ||
    form.name !== sel.name ||
    form.description !== (sel.description || '') ||
    form.provider !== sel.provider ||
    form.model !== sel.model ||
    Number(form.temperature) !== Number(sel.temperature) ||
    Number(form.maxTokens) !== Number(sel.maxTokens) ||
    form.autonomy !== sel.autonomy ||
    form.systemPrompt !== sel.systemPrompt ||
    form.active !== sel.active;

  async function handleSave() {
    if (!isDirty || saving) return;
    setSaving(true);
    try {
      const body = {
        code: form.code,
        name: form.name,
        description: form.description || null,
        provider: form.provider,
        model: form.model,
        temperature: Number(form.temperature),
        maxTokens: Number(form.maxTokens),
        autonomy: form.autonomy,
        systemPrompt: form.systemPrompt,
        active: form.active,
      };
      const r = await fetch(`/api/config/ai-agents/${encodeURIComponent(sel.id)}`, {
        method: 'PATCH',
        headers: actorHeaders(user?.id),
        credentials: 'same-origin',
        body: JSON.stringify(body),
      });
      const j = await r.json().catch(() => ({}));
      if (!r.ok) throw new Error(j.error || 'HTTP ' + r.status);
      onSaved(j.data);
      pushToast({ title: 'Agent salvato', tone: 'ok' });
      onClose();
    } catch (e) {
      pushToast({ title: 'Errore', desc: String(e), tone: 'err' });
    } finally {
      setSaving(false);
    }
  }

  return (
    <Modal
      open={!!sel}
      onClose={onClose}
      title={`Agent · ${sel.name}`}
      size="lg"
      footer={
        <>
          <Btn variant="ghost" size="sm" onClick={onClose}>Chiudi</Btn>
          {sel.active && <Btn variant="ghost" size="sm" onClick={() => onDelete(sel.id)}><Icon name="trash" size={11}/> Disattiva</Btn>}
          <Btn variant="primary" size="sm" disabled={!isDirty || saving} onClick={handleSave}>
            {saving ? 'Salvataggio…' : (isDirty ? 'Salva' : 'Salvato')}
          </Btn>
        </>
      }
    >
      <div className="col" style={{ gap: 14 }}>
        <div className="grid grid-3">
          <div className="field"><label>Code</label><input value={form.code} onChange={(e) => set('code', e.target.value)}/></div>
          <div className="field"><label>Nome</label><input value={form.name} onChange={(e) => set('name', e.target.value)}/></div>
          <div className="field"><label>Provider</label>
            <select value={form.provider} onChange={(e) => set('provider', e.target.value)}>
              <option value="claude">claude</option>
              <option value="gemini">gemini</option>
              <option value="deepseek">deepseek</option>
              <option value="openai">openai</option>
            </select>
          </div>
        </div>
        <div className="grid grid-3">
          <div className="field"><label>Modello</label><input value={form.model} onChange={(e) => set('model', e.target.value)}/></div>
          <div className="field"><label>Temperature</label><input type="number" min={0} max={2} step={0.05} value={form.temperature} onChange={(e) => set('temperature', Number(e.target.value))}/></div>
          <div className="field"><label>Max tokens</label><input type="number" min={1} max={200000} value={form.maxTokens} onChange={(e) => set('maxTokens', Number(e.target.value))}/></div>
        </div>
        <div className="grid grid-2">
          <div className="field"><label>Autonomia</label>
            <select value={form.autonomy} onChange={(e) => set('autonomy', e.target.value)}>
              <option value="suggest">suggest</option>
              <option value="execute_with_review">execute_with_review</option>
              <option value="auto">auto</option>
            </select>
          </div>
          <div className="field"><label>Stato</label>
            <select value={form.active ? 'on' : 'off'} onChange={(e) => set('active', e.target.value === 'on')}>
              <option value="on">Attivo</option>
              <option value="off">Disattivato</option>
            </select>
          </div>
        </div>
        <div className="field"><label>Descrizione</label>
          <input value={form.description} onChange={(e) => set('description', e.target.value)}/>
        </div>
        <div className="field"><label>System prompt</label>
          <textarea rows={10} value={form.systemPrompt} onChange={(e) => set('systemPrompt', e.target.value)} style={{ fontFamily: 'var(--font-mono)', fontSize: 11.5 }}/>
        </div>
      </div>
    </Modal>
  );
}

function AiAgentNewModal({ open, onClose, onCreated }) {
  const { user, pushToast } = useStore();
  const [form, setForm] = React.useState({
    code: '',
    name: '',
    provider: 'claude',
    model: 'claude-sonnet-4-5',
    temperature: 0.3,
    maxTokens: 2048,
    autonomy: 'suggest',
    systemPrompt: 'Sei un agent CAPEX di Veridanto. Rispondi in italiano, conciso, professionale.',
    active: true,
  });
  const [saving, setSaving] = React.useState(false);

  React.useEffect(() => {
    if (open) setForm({
      code: '',
      name: '',
      provider: 'claude',
      model: 'claude-sonnet-4-5',
      temperature: 0.3,
      maxTokens: 2048,
      autonomy: 'suggest',
      systemPrompt: 'Sei un agent CAPEX di Veridanto. Rispondi in italiano, conciso, professionale.',
      active: true,
    });
  }, [open]);

  if (!open) return null;
  const set = (k, v) => setForm((f) => ({ ...f, [k]: v }));
  const valid = form.code.trim() && form.name.trim() && form.model.trim() && form.systemPrompt.trim();

  async function handleCreate() {
    if (!valid || saving) return;
    setSaving(true);
    try {
      const body = {
        code: form.code.trim(),
        name: form.name.trim(),
        provider: form.provider,
        model: form.model.trim(),
        temperature: Number(form.temperature),
        maxTokens: Number(form.maxTokens),
        autonomy: form.autonomy,
        systemPrompt: form.systemPrompt,
        active: form.active,
      };
      const r = await fetch('/api/config/ai-agents', {
        method: 'POST',
        headers: actorHeaders(user?.id),
        credentials: 'same-origin',
        body: JSON.stringify(body),
      });
      const j = await r.json().catch(() => ({}));
      if (!r.ok) throw new Error(j.error || 'HTTP ' + r.status);
      onCreated(j.data);
      pushToast({ title: 'Agent creato', tone: 'ok' });
      onClose();
    } catch (e) {
      pushToast({ title: 'Errore', desc: String(e), tone: 'err' });
    } finally {
      setSaving(false);
    }
  }

  return (
    <Modal
      open={open}
      onClose={onClose}
      title="Nuovo agent AI"
      size="lg"
      footer={
        <>
          <Btn variant="ghost" size="sm" onClick={onClose}>Annulla</Btn>
          <Btn variant="primary" size="sm" disabled={!valid || saving} onClick={handleCreate}>{saving ? 'Creazione…' : 'Crea agent'}</Btn>
        </>
      }
    >
      <div className="col" style={{ gap: 14 }}>
        <div className="grid grid-3">
          <div className="field"><label>Code <span style={{ color: 'var(--err)' }}>*</span></label>
            <input value={form.code} onChange={(e) => set('code', e.target.value.toUpperCase())} placeholder="es. RDA_DRAFTER"/>
          </div>
          <div className="field"><label>Nome <span style={{ color: 'var(--err)' }}>*</span></label>
            <input value={form.name} onChange={(e) => set('name', e.target.value)} placeholder="es. Compositore RdA"/>
          </div>
          <div className="field"><label>Provider</label>
            <select value={form.provider} onChange={(e) => set('provider', e.target.value)}>
              <option value="claude">claude</option>
              <option value="gemini">gemini</option>
              <option value="deepseek">deepseek</option>
              <option value="openai">openai</option>
            </select>
          </div>
        </div>
        <div className="grid grid-3">
          <div className="field"><label>Modello <span style={{ color: 'var(--err)' }}>*</span></label>
            <input value={form.model} onChange={(e) => set('model', e.target.value)}/>
          </div>
          <div className="field"><label>Temperature</label>
            <input type="number" min={0} max={2} step={0.05} value={form.temperature} onChange={(e) => set('temperature', Number(e.target.value))}/>
          </div>
          <div className="field"><label>Max tokens</label>
            <input type="number" min={1} max={200000} value={form.maxTokens} onChange={(e) => set('maxTokens', Number(e.target.value))}/>
          </div>
        </div>
        <div className="field"><label>System prompt <span style={{ color: 'var(--err)' }}>*</span></label>
          <textarea rows={9} value={form.systemPrompt} onChange={(e) => set('systemPrompt', e.target.value)} style={{ fontFamily: 'var(--font-mono)', fontSize: 11.5 }}/>
        </div>
        <div className="row" style={{ gap: 14 }}>
          <div className="field" style={{ minWidth: 200 }}><label>Autonomia</label>
            <select value={form.autonomy} onChange={(e) => set('autonomy', e.target.value)}>
              <option value="suggest">suggest</option>
              <option value="execute_with_review">execute_with_review</option>
              <option value="auto">auto</option>
            </select>
          </div>
          <label className="row" style={{ gap: 6, fontSize: 11.5, cursor: 'pointer' }}>
            <input type="checkbox" checked={form.active} onChange={(e) => set('active', e.target.checked)}/>
            Attivo subito
          </label>
        </div>
      </div>
    </Modal>
  );
}

function AiPolicyEditModal({ open, onClose, policy, onSaved }) {
  const { user, pushToast } = useStore();
  const [form, setForm] = React.useState(null);
  const [saving, setSaving] = React.useState(false);

  React.useEffect(() => {
    if (open && policy) {
      setForm({
        mode: policy.mode,
        primaryProvider: policy.primaryProvider,
        fallbackProvider: policy.fallbackProvider || '',
        defaultModel: policy.defaultModel,
        fallbackModel: policy.fallbackModel || '',
        dataResidency: policy.dataResidency,
        piiRedaction: !!policy.piiRedaction,
        rateLimitPerUser: policy.rateLimit?.perUser ?? 100,
        rateLimitPerHour: policy.rateLimit?.perHour ?? 500,
        retentionLogDays: policy.retentionLogDays,
      });
    }
  }, [open, policy]);

  if (!open || !form) return null;
  const set = (k, v) => setForm((f) => ({ ...f, [k]: v }));

  async function handleSave() {
    if (saving) return;
    setSaving(true);
    try {
      const body = {
        mode: form.mode,
        primaryProvider: form.primaryProvider,
        fallbackProvider: form.fallbackProvider || null,
        defaultModel: form.defaultModel,
        fallbackModel: form.fallbackModel || null,
        dataResidency: form.dataResidency,
        piiRedaction: form.piiRedaction,
        rateLimitPerUser: Number(form.rateLimitPerUser),
        rateLimitPerHour: Number(form.rateLimitPerHour),
        retentionLogDays: Number(form.retentionLogDays),
      };
      const r = await fetch('/api/config/ai-policy', {
        method: 'PATCH',
        headers: actorHeaders(user?.id),
        credentials: 'same-origin',
        body: JSON.stringify(body),
      });
      const j = await r.json().catch(() => ({}));
      if (!r.ok) throw new Error(j.error || 'HTTP ' + r.status);
      onSaved(j.data);
      pushToast({ title: 'Policy aggiornata', tone: 'ok' });
      onClose();
    } catch (e) {
      pushToast({ title: 'Errore', desc: String(e), tone: 'err' });
    } finally {
      setSaving(false);
    }
  }

  return (
    <Modal
      open={open}
      onClose={onClose}
      title="AI policy globale"
      size="md"
      footer={
        <>
          <Btn variant="ghost" size="sm" onClick={onClose}>Annulla</Btn>
          <Btn variant="primary" size="sm" disabled={saving} onClick={handleSave}>{saving ? 'Salvataggio…' : 'Salva policy'}</Btn>
        </>
      }
    >
      <div className="col" style={{ gap: 14 }}>
        <div className="grid grid-3">
          <div className="field"><label>Mode</label>
            <select value={form.mode} onChange={(e) => set('mode', e.target.value)}>
              <option value="simulated">simulated</option>
              <option value="hybrid">hybrid</option>
              <option value="live">live</option>
            </select>
          </div>
          <div className="field"><label>Provider primario</label>
            <select value={form.primaryProvider} onChange={(e) => set('primaryProvider', e.target.value)}>
              <option value="claude">claude</option>
              <option value="gemini">gemini</option>
              <option value="deepseek">deepseek</option>
              <option value="openai">openai</option>
            </select>
          </div>
          <div className="field"><label>Provider fallback</label>
            <select value={form.fallbackProvider} onChange={(e) => set('fallbackProvider', e.target.value)}>
              <option value="">— nessuno —</option>
              <option value="claude">claude</option>
              <option value="gemini">gemini</option>
              <option value="deepseek">deepseek</option>
              <option value="openai">openai</option>
            </select>
          </div>
        </div>
        <div className="grid grid-2">
          <div className="field"><label>Modello default</label><input value={form.defaultModel} onChange={(e) => set('defaultModel', e.target.value)}/></div>
          <div className="field"><label>Modello fallback</label><input value={form.fallbackModel} onChange={(e) => set('fallbackModel', e.target.value)}/></div>
        </div>
        <div className="grid grid-3">
          <div className="field"><label>Data residency</label>
            <select value={form.dataResidency} onChange={(e) => set('dataResidency', e.target.value)}>
              <option value="EU">EU</option>
              <option value="US">US</option>
              <option value="GLOBAL">GLOBAL</option>
            </select>
          </div>
          <div className="field"><label>Rate /user (chiamate)</label><input type="number" min={1} max={10000} value={form.rateLimitPerUser} onChange={(e) => set('rateLimitPerUser', Number(e.target.value))}/></div>
          <div className="field"><label>Rate /hour (chiamate)</label><input type="number" min={1} max={100000} value={form.rateLimitPerHour} onChange={(e) => set('rateLimitPerHour', Number(e.target.value))}/></div>
        </div>
        <div className="row" style={{ gap: 14 }}>
          <label className="row" style={{ gap: 6, fontSize: 11.5, cursor: 'pointer' }}>
            <input type="checkbox" checked={form.piiRedaction} onChange={(e) => set('piiRedaction', e.target.checked)}/>
            PII redaction
          </label>
          <div className="field"><label>Retention log (giorni)</label><input type="number" min={1} max={3650} value={form.retentionLogDays} onChange={(e) => set('retentionLogDays', Number(e.target.value))}/></div>
        </div>
      </div>
    </Modal>
  );
}

// =============================================================
// AiAutonomyPolicyEditModal — editor matrice autonomia tool×level (FASE 11.G)
// Carica /api/config/ai-tools (lista tool definitions) + /api/config/ai-autonomy-policy
// (singleton policy del tenant). Editor diviso in 3 sezioni:
//   1) Default + tool levels (matrice)
//   2) Soglie amount per-tool (auto_below_eur / forbidden_above_eur)
//   3) Override per ruolo
// PATCH /api/config/ai-autonomy-policy on Save (audit + diff field-level).
// =============================================================
const AUTONOMY_LEVELS_UI = ['auto', 'require_approval', 'forbidden'];
const AUTONOMY_LEVEL_LABELS = {
  auto: 'auto',
  require_approval: 'approva',
  forbidden: 'vietato',
};
const AUTONOMY_LEVEL_TONES = {
  auto: 'ok',
  require_approval: 'warn',
  forbidden: 'err',
};

function AiAutonomyPolicyEditModal({ open, onClose }) {
  const { user, pushToast, seedCustom } = useStore();
  const roles = seedCustom?.ROLES || [];
  const [tools, setTools] = React.useState(null);
  const [policy, setPolicy] = React.useState(null);
  const [form, setForm] = React.useState(null);
  const [loading, setLoading] = React.useState(false);
  const [saving, setSaving] = React.useState(false);
  const [error, setError] = React.useState(null);

  React.useEffect(() => {
    if (!open) return;
    let cancelled = false;
    setLoading(true);
    setError(null);
    Promise.all([
      fetch('/api/config/ai-tools', { credentials: 'same-origin', cache: 'no-store' }).then((r) => r.json()),
      fetch('/api/config/ai-autonomy-policy', {
        credentials: 'same-origin',
        cache: 'no-store',
        headers: actorHeaders(user?.id),
      }).then((r) => r.json()),
    ])
      .then(([toolsResp, policyResp]) => {
        if (cancelled) return;
        const toolsData = Array.isArray(toolsResp?.data) ? toolsResp.data : [];
        setTools(toolsData);
        const p = policyResp?.data || null;
        setPolicy(p);
        setForm({
          defaultLevel: p?.defaultLevel || 'auto',
          toolLevels: { ...(p?.toolLevels || {}) },
          toolThresholds: { ...(p?.toolThresholds || {}) },
          roleOverrides: JSON.parse(JSON.stringify(p?.roleOverrides || {})),
        });
      })
      .catch((e) => !cancelled && setError(String(e?.message || e)))
      .finally(() => !cancelled && setLoading(false));
    return () => { cancelled = true; };
  }, [open, user?.id]);

  if (!open) return null;
  if (loading) {
    return (
      <Modal open={open} onClose={onClose} title="Autonomia tool AI" size="lg" footer={<Btn variant="ghost" size="sm" onClick={onClose}>Chiudi</Btn>}>
        <div style={{ padding: 20, color: 'var(--text-3)' }}>Caricamento policy + tool definitions…</div>
      </Modal>
    );
  }
  if (!form || !tools) {
    return (
      <Modal open={open} onClose={onClose} title="Autonomia tool AI" size="lg" footer={<Btn variant="ghost" size="sm" onClick={onClose}>Chiudi</Btn>}>
        <div style={{ padding: 20, color: 'var(--err)' }}>
          {error ? `Errore: ${error}` : 'Policy o tool definitions non disponibili.'}
        </div>
      </Modal>
    );
  }

  const set = (k, v) => setForm((f) => ({ ...f, [k]: v }));

  function setToolLevel(toolName, level) {
    setForm((f) => {
      const next = { ...f.toolLevels };
      if (!level || level === '__default__') {
        delete next[toolName];
      } else {
        next[toolName] = level;
      }
      return { ...f, toolLevels: next };
    });
  }

  function setThresholdField(toolName, field, value) {
    setForm((f) => {
      const t = { ...(f.toolThresholds[toolName] || {}) };
      if (value === '' || value === null || Number.isNaN(Number(value))) {
        delete t[field];
      } else {
        t[field] = Number(value);
      }
      const next = { ...f.toolThresholds };
      if (t.auto_below_eur === undefined && t.forbidden_above_eur === undefined) {
        delete next[toolName];
      } else {
        next[toolName] = t;
      }
      return { ...f, toolThresholds: next };
    });
  }

  function removeThreshold(toolName) {
    setForm((f) => {
      const next = { ...f.toolThresholds };
      delete next[toolName];
      return { ...f, toolThresholds: next };
    });
  }

  function addRoleOverride(roleId, toolName, level) {
    if (!roleId || !toolName || !level) return;
    setForm((f) => {
      const next = JSON.parse(JSON.stringify(f.roleOverrides));
      if (!next[roleId]) next[roleId] = {};
      next[roleId][toolName] = level;
      return { ...f, roleOverrides: next };
    });
  }

  function removeRoleOverride(roleId, toolName) {
    setForm((f) => {
      const next = JSON.parse(JSON.stringify(f.roleOverrides));
      if (next[roleId]) {
        delete next[roleId][toolName];
        if (Object.keys(next[roleId]).length === 0) delete next[roleId];
      }
      return { ...f, roleOverrides: next };
    });
  }

  const before = {
    defaultLevel: policy?.defaultLevel || 'auto',
    toolLevels: policy?.toolLevels || {},
    toolThresholds: policy?.toolThresholds || {},
    roleOverrides: policy?.roleOverrides || {},
  };
  const isDirty =
    form.defaultLevel !== before.defaultLevel ||
    JSON.stringify(form.toolLevels) !== JSON.stringify(before.toolLevels) ||
    JSON.stringify(form.toolThresholds) !== JSON.stringify(before.toolThresholds) ||
    JSON.stringify(form.roleOverrides) !== JSON.stringify(before.roleOverrides);

  async function handleSave() {
    if (!isDirty || saving) return;
    setSaving(true);
    setError(null);
    try {
      const body = {
        defaultLevel: form.defaultLevel,
        toolLevels: form.toolLevels,
        toolThresholds: form.toolThresholds,
        roleOverrides: form.roleOverrides,
      };
      const r = await fetch('/api/config/ai-autonomy-policy', {
        method: 'PATCH',
        headers: actorHeaders(user?.id),
        credentials: 'same-origin',
        body: JSON.stringify(body),
      });
      const j = await r.json().catch(() => ({}));
      if (!r.ok) {
        const msg = j?.error === 'validation_error'
          ? `Validazione: ${(j.issues || []).map((i) => `${(i.path || []).join('.')} ${i.message}`).join(' · ')}`
          : (j?.error || `HTTP ${r.status}`);
        setError(msg);
        return;
      }
      if (j?.data) setPolicy(j.data);
      pushToast({
        title: 'Policy autonomia salvata',
        desc: j?.changed === false ? 'Nessuna modifica rilevata' : 'Audit registrato',
        tone: 'ok',
      });
      onClose();
    } catch (e) {
      setError(String(e?.message || e));
    } finally {
      setSaving(false);
    }
  }

  const writeTools = tools.filter((t) => t.isWrite);
  const readTools = tools.filter((t) => !t.isWrite);

  return (
    <Modal
      open={open}
      onClose={onClose}
      title="Autonomia tool AI"
      size="lg"
      footer={
        <>
          <Btn variant="ghost" size="sm" onClick={onClose}>Chiudi</Btn>
          <Btn variant="primary" size="sm" disabled={!isDirty || saving} onClick={handleSave}>
            {saving ? 'Salvataggio…' : (isDirty ? 'Salva' : 'Salvato')}
          </Btn>
        </>
      }
    >
      <div className="col" style={{ gap: 16 }}>
        <div style={{ fontSize: 11.5, color: 'var(--text-2)', lineHeight: 1.5 }}>
          Matrice di autonomia per i tool che l'AI può chiamare. Risoluzione gerarchica: <b>roleOverrides</b> &gt; <b>toolThresholds</b> (su <code>amountEur</code>) &gt; <b>toolLevels</b> &gt; <b>defaultLevel</b>.
          {' '}Vedi pattern <code>autonomy-resolution-gerarchica</code>.
        </div>

        {error && (
          <div style={{ padding: 10, background: 'color-mix(in oklch, var(--err) 12%, var(--bg-1))', border: '1px solid var(--err)', borderRadius: 6, color: 'var(--err)', fontSize: 12 }}>
            ⚠ {error}
          </div>
        )}

        {/* Default level */}
        <div>
          <div className="eyebrow" style={{ marginBottom: 6 }}>Livello default (per tool non listati)</div>
          <div className="card" style={{ padding: 12 }}>
            <div className="row" style={{ gap: 8 }}>
              {AUTONOMY_LEVELS_UI.map((lvl) => (
                <button
                  key={lvl}
                  className={`btn sm ${form.defaultLevel === lvl ? 'primary' : 'ghost'}`}
                  onClick={() => set('defaultLevel', lvl)}
                  type="button"
                >
                  {AUTONOMY_LEVEL_LABELS[lvl]}
                </button>
              ))}
            </div>
            <div style={{ fontSize: 10.5, color: 'var(--text-3)', marginTop: 6 }}>
              Livello applicato se il tool non è esplicitamente in <code>toolLevels</code> e nessuna soglia/override applica.
            </div>
          </div>
        </div>

        {/* Tool matrix */}
        <div>
          <div className="eyebrow" style={{ marginBottom: 6 }}>
            Tool individuali ({tools.length}) · {writeTools.length} write · {readTools.length} read
          </div>
          <div className="card" style={{ padding: 0, overflow: 'hidden' }}>
            <table className="tbl dense">
              <thead>
                <tr>
                  <th style={{ width: 200 }}>Tool</th>
                  <th>Descrizione</th>
                  <th style={{ width: 80, textAlign: 'center' }}>Tipo</th>
                  <th style={{ width: 220, textAlign: 'center' }}>Livello</th>
                </tr>
              </thead>
              <tbody>
                {tools.map((t) => {
                  const lvl = form.toolLevels[t.name] || '__default__';
                  return (
                    <tr key={t.name}>
                      <td><code style={{ fontSize: 11 }}>{t.name}</code></td>
                      <td style={{ fontSize: 11, color: 'var(--text-2)' }}>{t.description.slice(0, 110)}{t.description.length > 110 ? '…' : ''}</td>
                      <td style={{ textAlign: 'center' }}>
                        {t.isWrite
                          ? <Chip kind="warn" dot>write</Chip>
                          : <Chip kind="info" dot>read</Chip>}
                      </td>
                      <td style={{ textAlign: 'center' }}>
                        <select value={lvl} onChange={(e) => setToolLevel(t.name, e.target.value)} style={{ fontSize: 11, padding: '4px 6px' }}>
                          <option value="__default__">— default ({AUTONOMY_LEVEL_LABELS[form.defaultLevel]}) —</option>
                          {AUTONOMY_LEVELS_UI.map((opt) => (
                            <option key={opt} value={opt}>{AUTONOMY_LEVEL_LABELS[opt]}</option>
                          ))}
                        </select>
                      </td>
                    </tr>
                  );
                })}
              </tbody>
            </table>
          </div>
        </div>

        {/* Thresholds */}
        <ThresholdSection
          form={form}
          tools={tools}
          onSetField={setThresholdField}
          onRemove={removeThreshold}
        />

        {/* Role overrides */}
        <RoleOverrideSection
          form={form}
          tools={tools}
          roles={roles}
          onAdd={addRoleOverride}
          onRemove={removeRoleOverride}
        />

        <AutonomyPolicyAuditLog/>

        <div style={{ fontSize: 10.5, color: 'var(--text-3)', padding: '8px 10px', background: 'var(--bg-2)', borderRadius: 4, lineHeight: 1.5 }}>
          <Icon name="info" size={10}/> <code>PATCH /api/config/ai-autonomy-policy</code> persiste in DB con audit log (diff field-level). La modifica è effettiva immediatamente per il prossimo cycle AI: <code>/api/ai/stream</code> rilegge la policy ad ogni tool call (no cache).
        </div>
      </div>
    </Modal>
  );
}

/**
 * FASE 11.I — Pannello inline audit log filtrato per entity_type=ai_autonomy_policy.
 * Mostra le ultime 20 modifiche alla policy con diff before→after collassabile.
 * Read-only (GET /api/audit-log?entityType=ai_autonomy_policy).
 */
function AutonomyPolicyAuditLog() {
  const [rows, setRows] = React.useState(null);
  const [error, setError] = React.useState(null);
  const [expanded, setExpanded] = React.useState(null);

  React.useEffect(() => {
    let cancelled = false;
    (async () => {
      try {
        const r = await fetch('/api/audit-log?entityType=ai_autonomy_policy&limit=20', {
          credentials: 'same-origin',
          cache: 'no-store',
        });
        if (!r.ok) throw new Error('HTTP ' + r.status);
        const j = await r.json();
        if (!cancelled) setRows(j.data || []);
      } catch (e) {
        if (!cancelled) setError(String(e?.message || e));
      }
    })();
    return () => { cancelled = true; };
  }, []);

  return (
    <div>
      <div className="eyebrow" style={{ marginBottom: 6 }}>
        Storico modifiche policy ({rows?.length ?? '…'})
      </div>
      <div className="card" style={{ padding: 0, overflow: 'hidden', maxHeight: 320, overflowY: 'auto' }}>
        {rows === null ? (
          <div style={{ padding: 12, color: 'var(--text-3)', fontSize: 11 }}>Caricamento audit log…</div>
        ) : error ? (
          <div style={{ padding: 12, color: 'var(--err)', fontSize: 11 }}>Errore: {error}</div>
        ) : rows.length === 0 ? (
          <div style={{ padding: 12, color: 'var(--text-3)', fontSize: 11 }}>
            Nessuna modifica registrata. Le PATCH sulla policy vengono auditate qui.
          </div>
        ) : (
          <table className="tbl dense">
            <thead>
              <tr>
                <th style={{ width: 130 }}>Quando</th>
                <th style={{ width: 100 }}>Attore</th>
                <th style={{ width: 80 }}>Azione</th>
                <th>Campi cambiati</th>
                <th style={{ width: 60 }}/>
              </tr>
            </thead>
            <tbody>
              {rows.map((r) => {
                const diff = r.diff || {};
                const fields = Object.keys((diff.after || {}));
                const isOpen = expanded === r.id;
                return (
                  <React.Fragment key={r.id}>
                    <tr className="clickable" onClick={() => setExpanded(isOpen ? null : r.id)}>
                      <td className="mono" style={{ fontSize: 10.5 }}>
                        {new Date(r.ts).toLocaleString('it-IT', { day: '2-digit', month: '2-digit', hour: '2-digit', minute: '2-digit', second: '2-digit' })}
                      </td>
                      <td style={{ fontSize: 11 }}>{r.actorPersonaId || <em style={{color:'var(--text-3)'}}>(system)</em>}</td>
                      <td><Chip kind="info">{r.action}</Chip></td>
                      <td style={{ fontSize: 11 }}>
                        {fields.length === 0 ? <em style={{color:'var(--text-3)'}}>—</em> : fields.map((f) => (
                          <span key={f} style={{ display: 'inline-block', padding: '1px 6px', marginRight: 4, fontSize: 10, background: 'var(--bg-2)', borderRadius: 3 }}>{f}</span>
                        ))}
                      </td>
                      <td style={{ textAlign: 'right' }}>
                        <Icon name={isOpen ? 'chevron-down' : 'chevron-right'} size={11}/>
                      </td>
                    </tr>
                    {isOpen && (
                      <tr>
                        <td colSpan={5} style={{ background: 'var(--bg-2)', padding: 10 }}>
                          <div className="grid grid-2" style={{ gap: 10, fontSize: 10.5 }}>
                            <div>
                              <div className="eyebrow" style={{ marginBottom: 4 }}>Before</div>
                              <pre style={{ margin: 0, padding: 8, background: 'var(--bg-1)', borderRadius: 4, overflow: 'auto', maxHeight: 200, fontSize: 10 }}>
                                {JSON.stringify(diff.before, null, 2)}
                              </pre>
                            </div>
                            <div>
                              <div className="eyebrow" style={{ marginBottom: 4 }}>After</div>
                              <pre style={{ margin: 0, padding: 8, background: 'var(--bg-1)', borderRadius: 4, overflow: 'auto', maxHeight: 200, fontSize: 10 }}>
                                {JSON.stringify(diff.after, null, 2)}
                              </pre>
                            </div>
                          </div>
                        </td>
                      </tr>
                    )}
                  </React.Fragment>
                );
              })}
            </tbody>
          </table>
        )}
      </div>
    </div>
  );
}

function ThresholdSection({ form, tools, onSetField, onRemove }) {
  const [addingTool, setAddingTool] = React.useState('');
  const writeTools = tools.filter((t) => t.isWrite);
  const tooledWithThreshold = Object.keys(form.toolThresholds);
  const candidates = writeTools.filter((t) => !tooledWithThreshold.includes(t.name));

  return (
    <div>
      <div className="row" style={{ alignItems: 'center', marginBottom: 6, gap: 8 }}>
        <div className="eyebrow">Soglie amount per tool ({tooledWithThreshold.length})</div>
        <span className="spacer"/>
        <select value={addingTool} onChange={(e) => setAddingTool(e.target.value)} style={{ fontSize: 11, padding: '4px 6px' }}>
          <option value="">+ Aggiungi soglia su tool…</option>
          {candidates.map((t) => (
            <option key={t.name} value={t.name}>{t.name}</option>
          ))}
        </select>
        <Btn
          variant="ghost"
          size="sm"
          disabled={!addingTool}
          onClick={() => {
            if (!addingTool) return;
            onSetField(addingTool, 'auto_below_eur', 0);
            setAddingTool('');
          }}
        >
          <Icon name="plus" size={11}/> Aggiungi
        </Btn>
      </div>
      <div className="card" style={{ padding: 12 }}>
        {tooledWithThreshold.length === 0 && (
          <div style={{ fontSize: 11, color: 'var(--text-3)' }}>
            Nessuna soglia configurata. Le soglie applicano solo a tool che forniscono <code>amountEur</code> nel context al momento della chiamata.
          </div>
        )}
        {tooledWithThreshold.map((toolName) => {
          const t = form.toolThresholds[toolName] || {};
          return (
            <div key={toolName} className="row" style={{ gap: 10, alignItems: 'center', padding: '6px 0', borderBottom: '1px solid var(--line)' }}>
              <code style={{ width: 160, fontSize: 11 }}>{toolName}</code>
              <div className="field" style={{ minWidth: 140 }}>
                <label style={{ fontSize: 10 }}>auto_below_eur</label>
                <input
                  type="number"
                  min={0}
                  max={1_000_000_000}
                  value={t.auto_below_eur ?? ''}
                  placeholder="—"
                  onChange={(e) => onSetField(toolName, 'auto_below_eur', e.target.value)}
                />
              </div>
              <div className="field" style={{ minWidth: 140 }}>
                <label style={{ fontSize: 10 }}>forbidden_above_eur</label>
                <input
                  type="number"
                  min={0}
                  max={1_000_000_000}
                  value={t.forbidden_above_eur ?? ''}
                  placeholder="—"
                  onChange={(e) => onSetField(toolName, 'forbidden_above_eur', e.target.value)}
                />
              </div>
              <span className="spacer"/>
              <Btn variant="ghost" size="sm" onClick={() => onRemove(toolName)}>
                <Icon name="trash" size={11}/>
              </Btn>
            </div>
          );
        })}
      </div>
    </div>
  );
}

function RoleOverrideSection({ form, tools, roles, onAdd, onRemove }) {
  const [draft, setDraft] = React.useState({ roleId: '', toolName: '', level: 'auto' });

  function tryAdd() {
    if (!draft.roleId || !draft.toolName || !draft.level) return;
    onAdd(draft.roleId, draft.toolName, draft.level);
    setDraft({ roleId: '', toolName: '', level: 'auto' });
  }

  const overrideRows = [];
  for (const [roleId, map] of Object.entries(form.roleOverrides)) {
    for (const [toolName, level] of Object.entries(map)) {
      overrideRows.push({ roleId, toolName, level });
    }
  }
  const roleLabel = (id) => {
    const r = roles.find((x) => x.id === id);
    return r ? `${r.name} (${id})` : id;
  };

  return (
    <div>
      <div className="eyebrow" style={{ marginBottom: 6 }}>Override per ruolo ({overrideRows.length})</div>
      <div className="card" style={{ padding: 12 }}>
        <div className="row" style={{ gap: 8, alignItems: 'flex-end', marginBottom: 10 }}>
          <div className="field" style={{ minWidth: 160 }}>
            <label style={{ fontSize: 10 }}>Ruolo</label>
            <window.Autocomplete value={draft.roleId} onChange={(v) => setDraft((d) => ({ ...d, roleId: v }))}
              options={roles.map((r) => ({ value: r.id, label: r.name, sublabel: r.id }))}
              placeholder="Scegli ruolo… (spazio per lista)" testId="cust-aiautonomy-role-ac" />
          </div>
          <div className="field" style={{ minWidth: 180 }}>
            <label style={{ fontSize: 10 }}>Tool</label>
            <select value={draft.toolName} onChange={(e) => setDraft((d) => ({ ...d, toolName: e.target.value }))}>
              <option value="">— scegli tool —</option>
              {tools.map((t) => (
                <option key={t.name} value={t.name}>{t.name}</option>
              ))}
            </select>
          </div>
          <div className="field" style={{ minWidth: 150 }}>
            <label style={{ fontSize: 10 }}>Livello forzato</label>
            <select value={draft.level} onChange={(e) => setDraft((d) => ({ ...d, level: e.target.value }))}>
              {AUTONOMY_LEVELS_UI.map((lvl) => (
                <option key={lvl} value={lvl}>{AUTONOMY_LEVEL_LABELS[lvl]}</option>
              ))}
            </select>
          </div>
          <Btn variant="ghost" size="sm" disabled={!draft.roleId || !draft.toolName} onClick={tryAdd}>
            <Icon name="plus" size={11}/> Aggiungi override
          </Btn>
        </div>
        {overrideRows.length === 0 && (
          <div style={{ fontSize: 11, color: 'var(--text-3)' }}>
            Nessun override configurato. Override = bypass dei livelli di default per ruoli specifici (priorità massima nella risoluzione gerarchica).
          </div>
        )}
        {overrideRows.length > 0 && (
          <table className="tbl dense">
            <thead>
              <tr>
                <th>Ruolo</th>
                <th>Tool</th>
                <th style={{ width: 140 }}>Livello</th>
                <th style={{ width: 60 }}/>
              </tr>
            </thead>
            <tbody>
              {overrideRows.map((row) => (
                <tr key={`${row.roleId}::${row.toolName}`}>
                  <td style={{ fontSize: 11 }}>{roleLabel(row.roleId)}</td>
                  <td><code style={{ fontSize: 11 }}>{row.toolName}</code></td>
                  <td><Chip kind={AUTONOMY_LEVEL_TONES[row.level] || 'info'}>{AUTONOMY_LEVEL_LABELS[row.level] || row.level}</Chip></td>
                  <td style={{ textAlign: 'right' }}>
                    <Btn variant="ghost" size="sm" onClick={() => onRemove(row.roleId, row.toolName)}>
                      <Icon name="trash" size={11}/>
                    </Btn>
                  </td>
                </tr>
              ))}
            </tbody>
          </table>
        )}
      </div>
    </div>
  );
}

// =============================================================
// CustUsers — list + new + edit + reset password + delete
// =============================================================
function CustUsers() {
  const { user, pushToast, seedCustom } = useStore();
  const roles = seedCustom.ROLES || [];
  const { items: users, setItems: setUsers, loading, error, reload } = useApiList('/api/config/users');

  const [sel, setSel] = React.useState(null);
  const [showNew, setShowNew] = React.useState(false);
  const [showReset, setShowReset] = React.useState(null); // user object

  // Sessione 100 — ricerca + filtri + paginazione 10/pagina.
  const [search, setSearch] = React.useState('');
  const [roleFilter, setRoleFilter] = React.useState('');
  const [statusFilter, setStatusFilter] = React.useState('');
  const filtered = React.useMemo(() => {
    const q = search.trim().toLowerCase();
    return (users || []).filter((u) => {
      if (q && !`${u.name} ${u.email}`.toLowerCase().includes(q)) return false;
      if (roleFilter && !(u.roleIds || []).includes(roleFilter)) return false;
      if (statusFilter === 'active' && !u.active) return false;
      if (statusFilter === 'off' && u.active) return false;
      return true;
    });
  }, [users, search, roleFilter, statusFilter]);
  const pg = usePaginated(filtered, 10);
  React.useEffect(() => { pg.setPage(1); }, [search, roleFilter, statusFilter]);

  async function deleteUser(id) {
    if (!confirm('Disattivare l\'utente? Soft-delete: non potrà più loggarsi.')) return;
    try {
      const r = await fetch(`/api/config/users/${encodeURIComponent(id)}`, {
        method: 'DELETE',
        headers: actorHeaders(user?.id),
        credentials: 'same-origin',
      });
      const j = await r.json().catch(() => ({}));
      if (!r.ok) throw new Error(j.error || 'HTTP ' + r.status);
      setUsers((prev) => (prev || []).map((x) => x.id === id ? j.data : x));
      pushToast({ title: 'Utente disattivato', tone: 'ok' });
    } catch (e) {
      pushToast({ title: 'Errore', desc: String(e), tone: 'err' });
    }
  }

  return (
    <>
      <div className="row" style={{ marginBottom: 12, alignItems: 'center' }}>
        <div className="eyebrow">Utenti ({filtered.length}{filtered.length !== (users || []).length ? ` / ${(users || []).length}` : ''})</div>
        <span className="spacer" />
        <ConfigWriteBtn perm="user.create" onClick={() => setShowNew(true)}><Icon name="plus" size={11}/> Nuovo utente</ConfigWriteBtn>
      </div>

      {!loading && !error && (
        <div className="row" style={{ marginBottom: 10, gap: 8, flexWrap: 'wrap', alignItems: 'center' }}>
          <input
            value={search}
            onChange={(e) => setSearch(e.target.value)}
            placeholder="Cerca per nome o email…"
            style={{ flex: '1 1 220px', minWidth: 160 }}
          />
          <div style={{ minWidth: 170 }}>
            <window.Autocomplete value={roleFilter} onChange={(v) => setRoleFilter(v)}
              options={roles.map((r) => ({ value: r.id, label: (r.code || r.id), sublabel: r.name }))}
              placeholder="Tutti i ruoli · cerca…" testId="cust-users-rolefilter-ac" />
          </div>
          <select value={statusFilter} onChange={(e) => setStatusFilter(e.target.value)} title="Filtra per stato">
            <option value="">Tutti gli stati</option>
            <option value="active">Solo attivi</option>
            <option value="off">Solo disattivati</option>
          </select>
          {(search || roleFilter || statusFilter) && (
            <Btn variant="ghost" size="sm" onClick={() => { setSearch(''); setRoleFilter(''); setStatusFilter(''); }}>Azzera filtri</Btn>
          )}
        </div>
      )}

      {loading && <div style={{ fontSize: 12, color: 'var(--text-3)' }}>Caricamento…</div>}
      {error && <div style={{ fontSize: 12, color: 'var(--err)' }}>Errore: {error}</div>}

      {!loading && !error && (
        <table className="tbl dense">
          <thead><tr>
            <th>Nome</th>
            <th>Email</th>
            <th style={{ width: 200 }}>Ruoli</th>
            <th style={{ width: 110, textAlign: 'center' }}>Login</th>
            <th style={{ width: 90, textAlign: 'center' }}>Stato</th>
            <th style={{ width: 130 }}>Ultimo accesso</th>
          </tr></thead>
          <tbody>
            {pg.slice.map((u) => (
              <tr key={u.id} className="clickable" onClick={() => setSel(u)}>
                <td style={{ fontWeight: 500 }}>{u.name}</td>
                <td style={{ fontSize: 12 }}>{u.email}</td>
                <td>
                  <div style={{ display: 'flex', gap: 3, flexWrap: 'wrap' }}>
                    {(u.roleIds || []).slice(0, 3).map((rid) => (
                      <Chip key={rid}>{(roles.find((r) => r.id === rid)?.code) || rid}</Chip>
                    ))}
                    {(u.roleIds || []).length > 3 && <Chip>+{(u.roleIds || []).length - 3}</Chip>}
                  </div>
                </td>
                <td style={{ textAlign: 'center' }}>
                  {u.locked ? <Chip kind="err" dot>locked</Chip>
                    : u.canLogin ? <Chip kind="ok" dot>ok</Chip>
                    : <Chip kind="" dot>off</Chip>}
                </td>
                <td style={{ textAlign: 'center' }}><Chip kind={u.active ? 'ok' : ''} dot>{u.active ? 'attivo' : 'off'}</Chip></td>
                <td style={{ fontSize: 11, fontFamily: 'var(--font-mono)', color: 'var(--text-3)' }}>{u.lastLoginAt ? u.lastLoginAt.slice(0, 10) : '—'}</td>
              </tr>
            ))}
          </tbody>
        </table>
      )}
      {!loading && !error && filtered.length === 0 && (
        <div style={{ fontSize: 12, color: 'var(--text-3)', padding: '14px 4px' }}>
          Nessun utente corrisponde ai filtri.
        </div>
      )}
      {!loading && !error && filtered.length > 0 && <Pagination {...pg} />}

      <UserEditModal
        sel={sel}
        roles={roles}
        onClose={() => setSel(null)}
        onSaved={(u) => setUsers((prev) => (prev || []).map((x) => x.id === u.id ? u : x))}
        onDelete={deleteUser}
        onResetPwd={(u) => { setSel(null); setShowReset(u); }}
      />
      <UserNewModal
        open={showNew}
        onClose={() => setShowNew(false)}
        roles={roles}
        onCreated={(u) => { setUsers((prev) => [u, ...(prev || [])]); reload(); }}
      />
      <UserResetPasswordModal
        target={showReset}
        onClose={() => setShowReset(null)}
        onDone={() => { setShowReset(null); reload(); }}
      />
    </>
  );
}

function UserEditModal({ sel, roles, onClose, onSaved, onDelete, onResetPwd }) {
  const { user, pushToast } = useStore();
  const [form, setForm] = React.useState(null);
  const [saving, setSaving] = React.useState(false);

  React.useEffect(() => {
    if (sel) setForm({
      name: sel.name,
      email: sel.email,
      roleIds: sel.roleIds || [],
      tenantId: sel.tenantId || '',
      active: sel.active,
    });
    else setForm(null);
  }, [sel]);

  if (!sel || !form) return null;
  const set = (k, v) => setForm((f) => ({ ...f, [k]: v }));
  const isDirty =
    form.name !== sel.name ||
    form.email !== sel.email ||
    JSON.stringify(form.roleIds) !== JSON.stringify(sel.roleIds || []) ||
    form.tenantId !== (sel.tenantId || '') ||
    form.active !== sel.active;

  function toggleRole(id) {
    if (form.roleIds.includes(id)) set('roleIds', form.roleIds.filter((x) => x !== id));
    else set('roleIds', [...form.roleIds, id]);
  }

  async function handleSave() {
    if (!isDirty || saving) return;
    setSaving(true);
    try {
      const body = {
        name: form.name,
        email: form.email,
        roleIds: form.roleIds,
        tenantId: form.tenantId || 'tenant-default',
        active: form.active,
      };
      const r = await fetch(`/api/config/users/${encodeURIComponent(sel.id)}`, {
        method: 'PATCH',
        headers: actorHeaders(user?.id),
        credentials: 'same-origin',
        body: JSON.stringify(body),
      });
      const j = await r.json().catch(() => ({}));
      if (!r.ok) throw new Error(j.error || 'HTTP ' + r.status);
      onSaved(j.data);
      pushToast({ title: 'Utente aggiornato', tone: 'ok' });
      onClose();
    } catch (e) {
      pushToast({ title: 'Errore', desc: String(e), tone: 'err' });
    } finally {
      setSaving(false);
    }
  }

  return (
    <Modal
      open={!!sel}
      onClose={onClose}
      title={`Utente · ${sel.name}`}
      size="lg"
      footer={
        <>
          <Btn variant="ghost" size="sm" onClick={onClose}>Chiudi</Btn>
          <Btn variant="ghost" size="sm" onClick={() => onResetPwd(sel)}><Icon name="lock" size={11}/> Reset password</Btn>
          {sel.active && sel.id !== user?.id && (
            <Btn variant="ghost" size="sm" onClick={() => onDelete(sel.id)}><Icon name="trash" size={11}/> Disattiva</Btn>
          )}
          <Btn variant="primary" size="sm" disabled={!isDirty || saving} onClick={handleSave}>{saving ? 'Salvataggio…' : (isDirty ? 'Salva' : 'Salvato')}</Btn>
        </>
      }
    >
      <div className="col" style={{ gap: 14 }}>
        <div className="grid grid-2">
          <div className="field"><label>Nome</label><input value={form.name} onChange={(e) => set('name', e.target.value)}/></div>
          <div className="field"><label>Email</label><input type="email" value={form.email} onChange={(e) => set('email', e.target.value)}/></div>
        </div>
        <div className="grid grid-2">
          <div className="field"><label>Tenant ID</label><input value={form.tenantId} onChange={(e) => set('tenantId', e.target.value)} style={{ fontFamily: 'var(--font-mono)', fontSize: 11.5 }}/></div>
          <div className="field"><label>Stato</label>
            <select value={form.active ? 'on' : 'off'} onChange={(e) => set('active', e.target.value === 'on')} disabled={sel.id === user?.id}>
              <option value="on">Attivo</option>
              <option value="off">Disattivato</option>
            </select>
            {sel.id === user?.id && <div style={{ fontSize: 10, color: 'var(--text-3)' }}>Non puoi disattivare te stesso.</div>}
          </div>
        </div>
        <div>
          <div className="eyebrow" style={{ marginBottom: 6 }}>Ruoli ({form.roleIds.length})</div>
          <div style={{ display: 'flex', flexWrap: 'wrap', gap: 4, padding: 8, border: '1px solid var(--line)', borderRadius: 4, maxHeight: 160, overflowY: 'auto' }}>
            {roles.map((r) => {
              const sel2 = form.roleIds.includes(r.id);
              return (
                <button key={r.id} className={`btn sm ${sel2 ? 'primary' : 'ghost'}`} style={{ fontSize: 10, padding: '2px 6px' }} onClick={() => toggleRole(r.id)}>
                  {r.code || r.id}
                </button>
              );
            })}
          </div>
        </div>
        <div className="row" style={{ gap: 14, padding: 10, background: 'var(--bg-2)', borderRadius: 8, fontSize: 11 }}>
          <div><div className="eyebrow">Login</div>{sel.canLogin ? <Chip kind="ok">attivo</Chip> : <Chip kind="">disattivato</Chip>}</div>
          <div><div className="eyebrow">Locked</div>{sel.locked ? <Chip kind="err">sì</Chip> : <Chip>no</Chip>}</div>
          <div><div className="eyebrow">Failed attempts</div>{sel.failedAttempts}</div>
          {sel.lastLoginAt && <div><div className="eyebrow">Ultimo accesso</div>{sel.lastLoginAt.slice(0, 16).replace('T', ' ')}</div>}
        </div>
      </div>
    </Modal>
  );
}

function UserNewModal({ open, onClose, roles, onCreated }) {
  const { user, pushToast } = useStore();
  const [form, setForm] = React.useState({ name: '', email: '', password: '', roleIds: [], tenantId: 'tenant-default', active: true });
  const [saving, setSaving] = React.useState(false);

  React.useEffect(() => {
    if (open) setForm({ name: '', email: '', password: '', roleIds: [], tenantId: 'tenant-default', active: true });
  }, [open]);

  if (!open) return null;
  const set = (k, v) => setForm((f) => ({ ...f, [k]: v }));
  const missing = [];
  if (!form.name.trim()) missing.push('nome');
  if (!/\S+@\S+\.\S+/.test(form.email)) missing.push('email valida');
  if (form.password.length < 8) missing.push('password (min 8 caratteri)');
  const valid = missing.length === 0;

  function toggleRole(id) {
    if (form.roleIds.includes(id)) set('roleIds', form.roleIds.filter((x) => x !== id));
    else set('roleIds', [...form.roleIds, id]);
  }

  async function handleCreate() {
    if (!valid || saving) return;
    setSaving(true);
    try {
      const body = {
        name: form.name.trim(),
        email: form.email.trim().toLowerCase(),
        password: form.password,
        roleIds: form.roleIds,
        tenantId: form.tenantId.trim() || 'tenant-default',
        active: form.active,
      };
      const r = await fetch('/api/config/users', {
        method: 'POST',
        headers: actorHeaders(user?.id),
        credentials: 'same-origin',
        body: JSON.stringify(body),
      });
      const j = await r.json().catch(() => ({}));
      if (!r.ok) {
        if (j.error === 'email_already_exists') throw new Error('Email già usata da un altro utente');
        throw new Error(j.error || 'HTTP ' + r.status);
      }
      onCreated(j.data);
      pushToast({ title: 'Utente creato', desc: 'Login attivo con la password fornita.', tone: 'ok' });
      onClose();
    } catch (e) {
      pushToast({ title: 'Errore', desc: String(e?.message || e), tone: 'err' });
    } finally {
      setSaving(false);
    }
  }

  return (
    <Modal
      open={open}
      onClose={onClose}
      title="Nuovo utente"
      size="md"
      footer={
        <>
          <Btn variant="ghost" size="sm" onClick={onClose}>Annulla</Btn>
          <Btn variant="primary" size="sm" disabled={!valid || saving} onClick={handleCreate}>{saving ? 'Creazione…' : 'Crea utente'}</Btn>
        </>
      }
    >
      <div className="col" style={{ gap: 14 }}>
        <div className="grid grid-2">
          <div className="field"><label>Nome <span style={{ color: 'var(--err)' }}>*</span></label><input value={form.name} onChange={(e) => set('name', e.target.value)}/></div>
          <div className="field"><label>Email <span style={{ color: 'var(--err)' }}>*</span></label><input type="email" value={form.email} onChange={(e) => set('email', e.target.value)}/></div>
        </div>
        <div className="grid grid-2">
          <div className="field"><label>Password iniziale <span style={{ color: 'var(--err)' }}>*</span></label>
            <input type="password" value={form.password} onChange={(e) => set('password', e.target.value)} placeholder="min 8 char"/>
          </div>
          <div className="field"><label>Tenant ID</label><input value={form.tenantId} onChange={(e) => set('tenantId', e.target.value)} style={{ fontFamily: 'var(--font-mono)', fontSize: 11.5 }}/></div>
        </div>
        <div>
          <div className="eyebrow" style={{ marginBottom: 6 }}>Ruoli ({form.roleIds.length})</div>
          <div style={{ display: 'flex', flexWrap: 'wrap', gap: 4, padding: 8, border: '1px solid var(--line)', borderRadius: 4, maxHeight: 140, overflowY: 'auto' }}>
            {roles.map((r) => {
              const sel = form.roleIds.includes(r.id);
              return (
                <button key={r.id} className={`btn sm ${sel ? 'primary' : 'ghost'}`} style={{ fontSize: 10, padding: '2px 6px' }} onClick={() => toggleRole(r.id)}>
                  {r.code || r.id}
                </button>
              );
            })}
          </div>
        </div>
        <label className="row" style={{ gap: 6, fontSize: 11.5, cursor: 'pointer' }}>
          <input type="checkbox" checked={form.active} onChange={(e) => set('active', e.target.checked)}/>
          Attivo subito (può loggarsi)
        </label>
        {missing.length > 0 && (
          <div style={{ fontSize: 11, color: 'var(--warn)', borderTop: '1px solid var(--line)', paddingTop: 10 }}>
            Per creare l'utente, completa: {missing.join(' · ')}.
          </div>
        )}
      </div>
    </Modal>
  );
}

function UserResetPasswordModal({ target, onClose, onDone }) {
  const { user, pushToast } = useStore();
  const [pwd, setPwd] = React.useState('');
  const [saving, setSaving] = React.useState(false);

  React.useEffect(() => { if (target) setPwd(''); }, [target]);
  if (!target) return null;
  const valid = pwd.length >= 8;

  async function handleReset() {
    if (!valid || saving) return;
    setSaving(true);
    try {
      const r = await fetch(`/api/config/users/${encodeURIComponent(target.id)}/reset-password`, {
        method: 'POST',
        headers: actorHeaders(user?.id),
        credentials: 'same-origin',
        body: JSON.stringify({ newPassword: pwd }),
      });
      const j = await r.json().catch(() => ({}));
      if (!r.ok) throw new Error(j.error || 'HTTP ' + r.status);
      pushToast({ title: 'Password reset', desc: `${target.email}: comunica la nuova password manualmente.`, tone: 'ok' });
      onDone();
    } catch (e) {
      pushToast({ title: 'Errore', desc: String(e?.message || e), tone: 'err' });
    } finally {
      setSaving(false);
    }
  }

  return (
    <Modal
      open={!!target}
      onClose={onClose}
      title={`Reset password · ${target.name}`}
      size="sm"
      footer={
        <>
          <Btn variant="ghost" size="sm" onClick={onClose}>Annulla</Btn>
          <Btn variant="primary" size="sm" disabled={!valid || saving} onClick={handleReset}>{saving ? 'Salvataggio…' : 'Reimposta'}</Btn>
        </>
      }
    >
      <div className="col" style={{ gap: 12 }}>
        <div style={{ fontSize: 12, color: 'var(--text-2)' }}>
          Imposterai una nuova password per <strong>{target.email}</strong>. Le credenziali esistenti verranno invalidate; eventuali lock saranno rimossi.
        </div>
        <div className="field"><label>Nuova password (min 8)</label>
          <input type="password" value={pwd} onChange={(e) => setPwd(e.target.value)} autoFocus/>
        </div>
        <div style={{ fontSize: 10.5, color: 'var(--text-3)' }}>
          <Icon name="info" size={10}/> La password viene cifrata con bcrypt cost=12 (FASE 22). Solo `audit_log` registra l'evento (senza payload).
        </div>
      </div>
    </Modal>
  );
}

Object.assign(window, {
  CustChecklists, CustAI, CustUsers,
  ChecklistRuleEditModal, ChecklistRuleNewModal,
  AiAgentEditModal, AiAgentNewModal, AiPolicyEditModal,
  AiAutonomyPolicyEditModal, ThresholdSection, RoleOverrideSection, AutonomyPolicyAuditLog,
  UserEditModal, UserNewModal, UserResetPasswordModal,
});
