// ============================================================
// Customizing.jsx — Shell con sotto-pagine per ogni aspetto
// di configurazione funzionale del sistema.
// ============================================================

const CUST_SECTIONS = [
  {
    group: 'Struttura organizzativa',
    items: [
      { id: 'org',        label: 'Legal entity · BU · Plant',  icon: 'users',     sub: 'Gerarchia aziendale e siti' },
      { id: 'users',      label: 'Utenti & profili',            icon: 'users',     sub: 'Anagrafiche, invito, sicurezza' },
      { id: 'roles',      label: 'Ruoli & permessi',            icon: 'shield',    sub: 'RACI, scope, deleghe' },
      { id: 'projectroles', label: 'Ruoli di progetto',         icon: 'users',     sub: 'PM, Sponsor, Eng Lead… per il team del progetto' },
      { id: 'delegations',label: 'Deleghe & sostituzioni',      icon: 'users',     sub: 'Backup approver temporanei' },
      { id: 'calendars',  label: 'Calendari lavorativi',        icon: 'calendar',  sub: 'Festività e orari per paese' },
    ],
  },
  {
    group: 'Catalogo operativo',
    items: [
      { id: 'capex',      label: 'Classi CAPEX',                icon: 'coins',     sub: 'Replacement, new capacity, compliance…' },
      { id: 'categories', label: 'Categorie merceologiche',     icon: 'package',   sub: 'Lead time e workflow default' },
      { id: 'doctypes',   label: 'Tipi documento',              icon: 'docs',      sub: 'MIME, firma digitale, AI extraction' },
      { id: 'templates',  label: 'Template & clausole',         icon: 'docs',      sub: 'PO, contratti, notifiche' },
    ],
  },
  {
    group: 'Regole di processo',
    items: [
      { id: 'checklists', label: 'Regole checklist',            icon: 'check',     sub: 'Documenti richiesti per scenario' },
      { id: 'workflows',  label: 'Approval workflows',          icon: 'workflow',  sub: 'Step, approver, gate documentali' },
      { id: 'matrix',     label: 'Matrice autorizzativa',       icon: 'grid',      sub: 'Soglie firma per ruolo/scope' },
      { id: 'sla',        label: 'SLA & escalation',            icon: 'bell',      sub: 'Policy temporali e azioni' },
    ],
  },
  {
    group: 'AI & comunicazione',
    items: [
      { id: 'aiagents',   label: 'AI agents & prompt',          icon: 'sparkle',   sub: 'Modelli, temperature, tools, guardrail' },
      { id: 'healthpolicy', label: 'Pesi salute progetto',      icon: 'pulse',     sub: 'Pesi 5 dimensioni health score · AI suggest portafoglio' },
      { id: 'anomalies',  label: 'Anomalie',                    icon: 'alert-triangle', sub: 'Flag aperte da AI · resolve / dismiss / audit' },
      { id: 'toolexec',   label: 'AI Tool Executions',          icon: 'audit',     sub: 'Audit append-only · ogni runTool() · KPI latency + error rate' },
      { id: 'notif',      label: 'Notifiche & eventi',          icon: 'bell',      sub: 'Mappatura evento → audience → canale' },
      { id: 'webhooks',   label: 'Webhook outbound',            icon: 'link',      sub: 'Endpoint esterni firmati HMAC SHA-256' },
    ],
  },
  {
    group: 'Configurazione globale',
    items: [
      { id: 'tenants',    label: 'Tenants',                      icon: 'users',     sub: 'Onboarding tenant + admin persona' },
      { id: 'branding',   label: 'Branding & localizzazione',   icon: 'palette',   sub: 'Logo, colori, formati, lingue' },
      { id: 'storage',    label: 'Storage backend',             icon: 'cloud',     sub: 'MinIO · AWS S3 · Filesystem locale' },
      { id: 'versions',   label: 'Versioni configurazione',     icon: 'history',   sub: 'Publish, rollback, audit config' },
    ],
  },
];

function Customizing() {
  const { seedCustom, routeParam } = useStore();
  const [section, setSection] = React.useState(() => localStorage.getItem('lgs.cust.section') || 'doctypes');
  React.useEffect(() => { localStorage.setItem('lgs.cust.section', section); }, [section]);

  // Sessione 59: se navigate('customizing', '<section>') ha settato routeParam
  // a una section valida, applicarla al mount (entry-point dal widget Dashboard).
  React.useEffect(() => {
    if (!routeParam) return;
    const valid = CUST_SECTIONS.flatMap((g) => g.items).some((i) => i.id === routeParam);
    if (valid && section !== routeParam) setSection(routeParam);
  }, [routeParam]);

  if (!seedCustom) {
    return (
      <div className="page fade-in">
        <div className="card"><div className="card-body">Caricamento configurazione…</div></div>
      </div>
    );
  }

  const flatItems = CUST_SECTIONS.flatMap((g) => g.items);
  const current = flatItems.find((i) => i.id === section) || flatItems[0];

  // Stats globali header
  const stats = {
    docTypes: (seedCustom.DOC_TYPES || []).length,
    checkRules: (seedCustom.CHECKLIST_RULES || []).length,
    workflows: (seedCustom.APPROVAL_WORKFLOWS || []).length,
    matrixRows: (seedCustom.APPROVAL_MATRIX || []).length,
    roles: (seedCustom.ROLES || []).length,
    sla: (seedCustom.SLA_POLICIES || []).length,
    notif: (seedCustom.NOTIFICATION_RULES || []).length,
    agents: (seedCustom.AI_AGENTS || []).length,
  };

  return (
    <div className="page fade-in">
      <div className="page-header">
        <div>
          <div className="eyebrow">Governance · Amministrazione</div>
          <h1 className="page-title">Customizing</h1>
          <div className="page-sub">Configurazione funzionale del sistema: organizzazione, catalogo, regole di processo, AI e notifiche. Ogni modifica è versionata e auditata.</div>
        </div>
        <div className="actions">
          <Chip kind="info" dot>Config v7 · published</Chip>
          <Btn variant="ghost" size="sm"><Icon name="history" size={11}/> Versioni</Btn>
          <Btn variant="ghost" size="sm"><Icon name="download" size={11}/> Export YAML</Btn>
          <Btn variant="primary" size="sm"><Icon name="check" size={11}/> Publish draft</Btn>
        </div>
      </div>

      {/* KPI overview */}
      <div className="grid grid-4" style={{ marginBottom: 14, gap: 10 }}>
        <div className="card"><Stat label="Tipi documento" value={stats.docTypes} delta={`${(seedCustom.DOC_TYPES||[]).filter(d=>d.active).length} attivi`} tone="flat"/></div>
        <div className="card"><Stat label="Regole checklist" value={stats.checkRules} delta={`${new Set((seedCustom.CHECKLIST_RULES||[]).map(r=>r.entityType)).size} entity types`} tone="flat"/></div>
        <div className="card"><Stat label="Workflow attivi" value={stats.workflows} delta={`${(seedCustom.APPROVAL_WORKFLOWS||[]).reduce((a,w)=>a+(w.activeInstances||0),0)} istanze`} tone="flat"/></div>
        <div className="card"><Stat label="Regole autoriz." value={stats.matrixRows} delta={`${stats.roles} ruoli`} tone="flat"/></div>
      </div>

      {/* Split: sidebar interna + content */}
      <div className="card flush" style={{ display: 'grid', gridTemplateColumns: '230px 1fr', minHeight: 640, overflow: 'hidden' }}>
        {/* Left nav */}
        <div style={{ borderRight: '1px solid var(--line)', background: 'var(--bg-1)', padding: '10px 8px', overflowY: 'auto' }}>
          {CUST_SECTIONS.map((g) => (
            <div key={g.group} style={{ marginBottom: 10 }}>
              <div className="eyebrow" style={{ padding: '6px 10px 4px' }}>{g.group}</div>
              {g.items.map((it) => (
                <button
                  key={it.id}
                  onClick={() => setSection(it.id)}
                  className={`cust-nav${section === it.id ? ' active' : ''}`}
                >
                  <Icon name={it.icon} size={13}/>
                  <div style={{ textAlign: 'left' }}>
                    <div style={{ fontSize: 12.5, fontWeight: section === it.id ? 600 : 500 }}>{it.label}</div>
                    <div style={{ fontSize: 10.5, color: 'var(--text-3)', marginTop: 1, lineHeight: 1.35 }}>{it.sub}</div>
                  </div>
                </button>
              ))}
            </div>
          ))}
          <style>{`
            .cust-nav{
              display: grid; grid-template-columns: 16px 1fr; gap: 10px; align-items: start;
              width: 100%; padding: 8px 10px; border: 0; border-radius: 6px;
              background: transparent; cursor: pointer; color: var(--text-2);
              text-align: left; margin-bottom: 2px;
            }
            .cust-nav:hover{ background: var(--bg-2); color: var(--text-1); }
            .cust-nav.active{ background: color-mix(in oklch, var(--accent) 14%, var(--bg-1)); color: var(--text-1); }
            .cust-nav.active svg{ color: var(--accent); }
          `}</style>
        </div>

        {/* Right panel */}
        <div style={{ padding: 16, overflow: 'auto' }}>
          <div style={{ marginBottom: 14 }}>
            <div className="row" style={{ gap: 8, alignItems: 'center' }}>
              <Icon name={current.icon} size={16}/>
              <div style={{ fontSize: 15, fontWeight: 600 }}>{current.label}</div>
              <span className="spacer"/>
              <Chip kind="info">v{(seedCustom.CONFIG_VERSIONS||[]).length||'7'} · published</Chip>
            </div>
            <div style={{ fontSize: 12, color: 'var(--text-2)', marginTop: 4 }}>{current.sub}</div>
          </div>

          {section === 'doctypes'   && <CustDocTypes/>}
          {section === 'checklists' && <CustChecklists/>}
          {section === 'workflows'  && <CustWorkflows/>}
          {section === 'matrix'     && <CustMatrix/>}
          {section === 'roles'      && <CustRoles/>}
          {section === 'projectroles' && <CustProjectRoles/>}
          {section === 'delegations'&& <CustDelegations/>}
          {section === 'categories' && <CustCategories/>}
          {section === 'capex'      && <CustCapexClasses/>}
          {section === 'org'        && <CustSites/>}
          {section === 'calendars'  && <CustCalendars/>}
          {section === 'users'      && <CustUsers/>}
          {section === 'sla'        && <CustSLA/>}
          {section === 'notif'      && <CustNotifications/>}
          {section === 'aiagents'   && <CustAI/>}
          {section === 'healthpolicy' && (window.CustHealthPolicy ? <window.CustHealthPolicy/> : <div>Caricamento…</div>)}
          {section === 'anomalies'  && <CustAnomalies/>}
          {section === 'toolexec'   && <CustToolExecutions/>}
          {section === 'tenants'    && <CustTenants/>}
          {section === 'templates'  && <CustTemplates/>}
          {section === 'branding'   && <CustBrandingPlaceholder/>}
          {section === 'storage'    && <CustStorage/>}
          {section === 'versions'   && <CustVersions/>}
          {section === 'webhooks'   && <CustWebhooks/>}
        </div>
      </div>
    </div>
  );
}

/**
 * FASE 3a.13: Branding form editable.
 * Carica il singleton da /api/config/branding al mount, PATCH on Save.
 */
function CustBrandingPlaceholder() {
  const { user, pushToast } = useStore();
  const [branding, setBranding] = React.useState(null);
  const [form, setForm] = React.useState(null);
  const [loading, setLoading] = React.useState(true);
  const [saving, setSaving] = React.useState(false);
  const [error, setError] = React.useState(null);

  React.useEffect(() => {
    let cancelled = false;
    setLoading(true);
    fetch('/api/config/branding')
      .then(r => r.json())
      .then(json => {
        if (cancelled) return;
        if (json?.data) {
          setBranding(json.data);
          setForm({
            claim: json.data.claim || '',
            fontFamily: json.data.fontFamily || 'IBM Plex Sans',
            defaultDensity: json.data.defaultDensity || 'high',
            defaultTheme: json.data.defaultTheme || 'dark',
            languages: (json.data.languages || ['it']).join(', '),
            dateFormat: json.data.dateFormat || 'dd/MM/yyyy',
            accent: (json.data.colors && json.data.colors.accent) || '',
            favicon: json.data.favicon || '',
            logoLight: json.data.logoLight || '',
            logoDark: json.data.logoDark || '',
            projectCodePrefix: json.data.projectCodePrefix || 'INV-',
          });
        }
      })
      .catch(e => !cancelled && setError(String(e)))
      .finally(() => !cancelled && setLoading(false));
    return () => { cancelled = true; };
  }, []);

  if (loading) return <div style={{padding:14, color:'var(--text-3)'}}>Caricamento branding…</div>;
  if (!form) return <div style={{padding:14, color:'var(--err)'}}>Branding singleton non disponibile in DB. Inizializzare via seed.</div>;

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

  async function handleSave() {
    if (saving) return;
    setSaving(true); setError(null);
    try {
      const body = {
        claim: form.claim.trim() || null,
        fontFamily: form.fontFamily.trim() || 'IBM Plex Sans',
        defaultDensity: form.defaultDensity,
        defaultTheme: form.defaultTheme,
        languages: form.languages.split(',').map(s => s.trim()).filter(Boolean),
        dateFormat: form.dateFormat.trim() || 'dd/MM/yyyy',
        colors: { ...(branding?.colors || {}), accent: form.accent.trim() || (branding?.colors?.accent || '#FF6600') },
        favicon: form.favicon.trim() || null,
        logoLight: form.logoLight.trim() || null,
        logoDark: form.logoDark.trim() || null,
        projectCodePrefix: (form.projectCodePrefix || 'INV-').trim().toUpperCase(),
      };
      const res = await fetch('/api/config/branding', {
        method: 'PATCH',
        headers: { 'content-type': 'application/json', ...(user?.id ? { 'X-Actor-Persona-Id': user.id } : {}) },
        body: JSON.stringify(body),
      });
      const json = await res.json().catch(()=>({}));
      if (!res.ok) {
        const msg = json?.error === 'validation_error'
          ? `Validazione fallita: ${(json.issues||[]).map(i=>i.message).join(' · ')}`
          : (json?.error || `HTTP ${res.status}`);
        setError(msg);
        return;
      }
      if (json?.data) setBranding(json.data);
      pushToast({ title: 'Branding aggiornato', desc: json?.changed === false ? 'Nessuna modifica rilevata' : 'Salvato in DB. Audit registrato.', tone: 'ok' });
    } catch (e) {
      setError(String(e?.message || e));
    } finally {
      setSaving(false);
    }
  }

  return (
    <div className="col" style={{gap:14}}>
      <div className="row" style={{alignItems:'center', gap:8}}>
        <div style={{fontSize:11.5, color:'var(--text-2)'}}>
          Branding singleton del tenant. Le modifiche sono live e applicate al prossimo refresh dell'app.
        </div>
        <span className="spacer"/>
        <Btn variant="primary" size="sm" disabled={saving} onClick={handleSave}>{saving ? 'Salvataggio…' : 'Salva modifiche'}</Btn>
      </div>

      <div className="grid grid-2" style={{ gap: 14 }}>
        <div>
          <div className="eyebrow" style={{marginBottom:6}}>Identità & claim</div>
          <div className="card" style={{padding:14}}>
            <div className="field"><label>Claim / tagline</label>
              <input value={form.claim} onChange={e=>set('claim', e.target.value)} placeholder="Industrial Investment Governance"/>
            </div>
            <div className="field"><label>Logo light (URL)</label>
              <input value={form.logoLight} onChange={e=>set('logoLight', e.target.value)} placeholder="/assets/logo-light.svg"/>
            </div>
            <div className="field"><label>Logo dark (URL)</label>
              <input value={form.logoDark} onChange={e=>set('logoDark', e.target.value)} placeholder="/assets/logo-dark.svg"/>
            </div>
            <div className="field"><label>Favicon (URL)</label>
              <input value={form.favicon} onChange={e=>set('favicon', e.target.value)} placeholder="/assets/favicon.ico"/>
            </div>
            <div className="field"><label>Colore accento (hex/oklch)</label>
              <div className="row" style={{gap:8}}>
                <div style={{width:28, height:28, borderRadius:4, background: form.accent || 'var(--accent)'}}/>
                <input value={form.accent} onChange={e=>set('accent', e.target.value)} placeholder="#FF6600 o oklch(...)" style={{fontFamily:'var(--font-mono)', fontSize:12}}/>
              </div>
            </div>
          </div>
        </div>
        <div>
          <div className="eyebrow" style={{marginBottom:6}}>Localizzazione</div>
          <div className="card" style={{padding:14}}>
            <div className="field"><label>Font family</label>
              <select value={form.fontFamily} onChange={e=>set('fontFamily', e.target.value)}>
                <option>IBM Plex Sans</option>
                <option>Inter</option>
                <option>Roboto</option>
                <option>Helvetica</option>
                <option>Fraunces</option>
              </select>
            </div>
            <div className="field"><label>Densità default</label>
              <select value={form.defaultDensity} onChange={e=>set('defaultDensity', e.target.value)}>
                <option value="high">high (desktop-dense)</option>
                <option value="medium">medium</option>
                <option value="low">low (mobile)</option>
              </select>
            </div>
            <div className="field"><label>Tema default</label>
              <select value={form.defaultTheme} onChange={e=>set('defaultTheme', e.target.value)}>
                <option value="dark">dark</option>
                <option value="light">light</option>
                <option value="auto">auto (sistema)</option>
              </select>
            </div>
            <div className="field"><label>Lingue abilitate (CSV)</label>
              <input value={form.languages} onChange={e=>set('languages', e.target.value)} placeholder="it, en, de"/>
            </div>
            <div className="field"><label>Formato data</label>
              <select value={form.dateFormat} onChange={e=>set('dateFormat', e.target.value)}>
                <option value="dd/MM/yyyy">dd/MM/yyyy (it)</option>
                <option value="MM/dd/yyyy">MM/dd/yyyy (us)</option>
                <option value="yyyy-MM-dd">yyyy-MM-dd (iso)</option>
              </select>
            </div>
            <div className="field" data-testid="branding-project-code-prefix-field">
              <label>Prefisso codice progetto</label>
              <input
                value={form.projectCodePrefix}
                onChange={e => set('projectCodePrefix', e.target.value)}
                placeholder="INV- o CPX-"
                maxLength={16}
                pattern="[A-Za-z0-9]+-?"
                style={{ fontFamily: 'var(--font-mono)', fontSize: 12, textTransform: 'uppercase' }}
                data-testid="branding-project-code-prefix-input"
              />
              <div style={{fontSize:10.5, color:'var(--text-3)', marginTop:4}}>
                Es. <code>INV-</code> → <code>INV-MIL-MAC-123</code>; <code>CPX</code> → <code>CPX-ROM-IT-456</code>.
                Si applica ai nuovi codici progetto generati dal sistema (formato <code>&lt;PREFIX&gt;-&lt;SITE&gt;-&lt;CAT&gt;-NNN</code>).
              </div>
            </div>
          </div>
        </div>
      </div>

      {error && (
        <div style={{fontSize:11, color:'var(--err)', padding:'8px 10px', background:'rgba(239,68,68,0.06)', border:'1px solid var(--err)', borderRadius:4}}>
          <Icon name="alert-triangle" size={10}/> {error}
        </div>
      )}

      <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/branding</code> persiste in DB con audit log. Il record è singleton del tenant. Per applicare i cambi visivamente serve refresh dell'app.
      </div>
    </div>
  );
}

/**
 * FASE 2c.3 (Sprint 2b cliente): Storage backend editable.
 * Singleton storage_settings. Switch tra MinIO / AWS S3 / Filesystem locale a runtime.
 * - PATCH /api/settings/storage cifra la secret_key AES-GCM e invalida la cache lib/storage.
 * - POST /api/settings/storage/test fa un round-trip put→get→delete su un blob effimero.
 */
function CustStorage() {
  const { user, pushToast } = useStore();
  const [settings, setSettings] = React.useState(null);
  const [form, setForm] = React.useState(null);
  const [loading, setLoading] = React.useState(true);
  const [saving, setSaving] = React.useState(false);
  const [testing, setTesting] = React.useState(false);
  const [testResult, setTestResult] = React.useState(null); // { ok, latencyMs, detail, step }
  const [secretDraft, setSecretDraft] = React.useState('');
  const [error, setError] = React.useState(null);

  const reload = React.useCallback(async () => {
    setLoading(true);
    setError(null);
    try {
      const res = await fetch('/api/settings/storage', { cache: 'no-store' });
      if (!res.ok) throw new Error(`HTTP ${res.status}`);
      const json = await res.json();
      const dto = json?.data;
      if (!dto) throw new Error('payload vuoto');
      setSettings(dto);
      setForm({
        backend: dto.backend,
        endpoint: dto.endpoint || '',
        region: dto.region,
        bucket: dto.bucket,
        accessKey: dto.accessKey || '',
        forcePathStyle: dto.forcePathStyle,
        localRootPath: dto.localRootPath || '',
      });
      setSecretDraft('');
    } catch (e) {
      setError(String(e?.message || e));
    } finally {
      setLoading(false);
    }
  }, []);

  React.useEffect(() => { reload(); }, [reload]);

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

  async function handleSave() {
    if (saving || !form) return;
    setSaving(true);
    setError(null);
    setTestResult(null);
    try {
      const body = {
        backend: form.backend,
        endpoint: form.backend === 'local' ? null : (form.endpoint.trim() || null),
        region: form.region.trim() || 'us-east-1',
        bucket: form.bucket.trim(),
        accessKey: form.accessKey.trim() || null,
        forcePathStyle: !!form.forcePathStyle,
        localRootPath: form.backend === 'local' ? (form.localRootPath.trim() || null) : null,
      };
      // Includi secretKey solo se l'utente ha digitato qualcosa
      if (secretDraft.trim()) body.secretKey = secretDraft.trim();
      const res = await fetch('/api/settings/storage', {
        method: 'PATCH',
        headers: {
          'content-type': 'application/json',
          ...(user?.id ? { 'X-Actor-Persona-Id': user.id } : {}),
        },
        body: JSON.stringify(body),
      });
      const json = await res.json().catch(() => ({}));
      if (!res.ok) {
        const msg = json?.error === 'validation_error'
          ? `Validazione: ${(json.issues || []).map(i => `${i.path?.join('.')||''} ${i.message}`).join(' · ')}`
          : (json?.error || `HTTP ${res.status}`);
        setError(msg);
        return;
      }
      if (json?.data) {
        setSettings(json.data);
        setSecretDraft('');
      }
      pushToast({
        title: 'Storage aggiornato',
        desc: json?.changed === false ? 'Nessuna modifica' : 'Salvato + cache invalidata',
        tone: 'ok',
      });
    } catch (e) {
      setError(String(e?.message || e));
    } finally {
      setSaving(false);
    }
  }

  async function handleTestConnection() {
    if (testing) return;
    setTesting(true);
    setTestResult(null);
    try {
      const res = await fetch('/api/settings/storage/test', {
        method: 'POST',
        headers: {
          'content-type': 'application/json',
          ...(user?.id ? { 'X-Actor-Persona-Id': user.id } : {}),
        },
      });
      const json = await res.json().catch(() => ({}));
      if (res.ok && json?.data?.ok) {
        setTestResult({ ok: true, latencyMs: json.data.latencyMs, step: 'ok' });
        pushToast({ title: 'Storage OK', desc: `${json.data.latencyMs}ms`, tone: 'ok' });
      } else {
        setTestResult({
          ok: false,
          latencyMs: json?.latencyMs ?? 0,
          step: json?.step || 'unknown',
          detail: json?.detail || 'Errore sconosciuto',
        });
        pushToast({
          title: 'Storage test fallito',
          desc: (json?.detail || 'Errore').slice(0, 160),
          tone: 'err',
        });
      }
    } catch (e) {
      setTestResult({ ok: false, latencyMs: 0, step: 'network', detail: String(e?.message || e) });
    } finally {
      setTesting(false);
    }
  }

  async function handleClearSecret() {
    if (saving) return;
    if (!window.confirm('Rimuovere la secret_key dal database?')) return;
    setSaving(true);
    try {
      const res = await fetch('/api/settings/storage', {
        method: 'PATCH',
        headers: {
          'content-type': 'application/json',
          ...(user?.id ? { 'X-Actor-Persona-Id': user.id } : {}),
        },
        body: JSON.stringify({ secretKey: null }),
      });
      const json = await res.json().catch(() => ({}));
      if (!res.ok) {
        setError(json?.error || `HTTP ${res.status}`);
        return;
      }
      if (json?.data) setSettings(json.data);
      pushToast({ title: 'secret_key rimossa', tone: 'warn' });
    } finally {
      setSaving(false);
    }
  }

  if (loading) return <div style={{padding:14, color:'var(--text-3)'}}>Caricamento storage settings…</div>;
  if (!form) return <div style={{padding:14, color:'var(--err)'}}>Storage settings non disponibile. Verifica DB.</div>;

  const isS3 = form.backend === 'minio' || form.backend === 'aws';
  const isLocal = form.backend === 'local';

  return (
    <div className="col" style={{gap:14}}>
      <div className="row" style={{alignItems:'center', gap:8}}>
        <div style={{fontSize:11.5, color:'var(--text-2)'}}>
          Backend storage per allegati ProjectDocument. Modifiche live: dopo "Salva" la cache viene invalidata e il prossimo upload usa la nuova config.
        </div>
        <span className="spacer"/>
        <Btn variant="ghost" size="sm" disabled={testing} onClick={handleTestConnection}>
          {testing ? 'Test…' : <><Icon name="check" size={11}/> Test connessione</>}
        </Btn>
        <Btn variant="primary" size="sm" disabled={saving} onClick={handleSave}>
          {saving ? 'Salvataggio…' : 'Salva modifiche'}
        </Btn>
      </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>
      )}

      {testResult && (
        <div style={{
          padding:10,
          background: testResult.ok
            ? 'color-mix(in oklch, var(--ok) 10%, var(--bg-1))'
            : 'color-mix(in oklch, var(--err) 10%, var(--bg-1))',
          border: '1px solid ' + (testResult.ok ? 'var(--ok)' : 'var(--err)'),
          borderRadius:6,
          fontSize:12,
        }}>
          {testResult.ok
            ? <span><Icon name="check" size={11}/> Connessione OK · round-trip put/get/delete riuscito · {testResult.latencyMs}ms</span>
            : <span>✗ Test fallito allo step <code>{testResult.step}</code> · {testResult.latencyMs}ms · {testResult.detail}</span>}
        </div>
      )}

      <div className="grid grid-2" style={{ gap: 14 }}>
        <div>
          <div className="eyebrow" style={{marginBottom:6}}>Tipo backend</div>
          <div className="card" style={{padding:14}}>
            <div className="row" style={{ gap: 6, flexWrap: 'wrap' }}>
              <button
                className={`btn sm ${form.backend === 'minio' ? 'primary' : 'ghost'}`}
                onClick={() => set('backend', 'minio')}
              >
                <span style={{ width: 8, height: 8, background: '#c72c48', borderRadius: '50%', display: 'inline-block', marginRight: 6 }}/>
                MinIO
              </button>
              <button
                className={`btn sm ${form.backend === 'aws' ? 'primary' : 'ghost'}`}
                onClick={() => set('backend', 'aws')}
              >
                <span style={{ width: 8, height: 8, background: '#FF9900', borderRadius: '50%', display: 'inline-block', marginRight: 6 }}/>
                AWS S3
              </button>
              <button
                className={`btn sm ${form.backend === 'local' ? 'primary' : 'ghost'}`}
                onClick={() => set('backend', 'local')}
              >
                <span style={{ width: 8, height: 8, background: '#64748b', borderRadius: '50%', display: 'inline-block', marginRight: 6 }}/>
                Filesystem locale
              </button>
            </div>
            <div style={{ fontSize: 10.5, color: 'var(--text-3)', marginTop: 8 }}>
              {form.backend === 'minio' && 'MinIO self-hosted (S3-compatible). Setup dev: docker compose -f docker/minio-compose.yml up -d'}
              {form.backend === 'aws' && 'AWS S3 prod-ready. Richiede credenziali IAM con policy s3:PutObject/GetObject/DeleteObject sul bucket.'}
              {form.backend === 'local' && 'Filesystem locale (.storage/ del repo). Solo per dev senza Docker o ambienti CI.'}
            </div>
          </div>
        </div>

        <div>
          <div className="eyebrow" style={{marginBottom:6}}>Bucket & region</div>
          <div className="card" style={{padding:14}}>
            <div className="field">
              <label>Bucket name</label>
              <input
                value={form.bucket}
                onChange={e => set('bucket', e.target.value)}
                placeholder="veridanto-documents"
                disabled={isLocal}
              />
              <div style={{ fontSize: 10.5, color: 'var(--text-3)', marginTop: 4 }}>
                S3-compatible: 3-63 caratteri, lowercase, [a-z0-9.-]
              </div>
            </div>
            <div className="field">
              <label>Region</label>
              <input
                value={form.region}
                onChange={e => set('region', e.target.value)}
                placeholder="us-east-1"
                disabled={isLocal}
              />
            </div>
          </div>
        </div>

        {isS3 && (
          <div style={{ gridColumn: '1 / -1' }}>
            <div className="eyebrow" style={{marginBottom:6}}>Endpoint & connection</div>
            <div className="card" style={{padding:14}}>
              <div className="field">
                <label>Endpoint URL</label>
                <input
                  value={form.endpoint}
                  onChange={e => set('endpoint', e.target.value)}
                  placeholder={form.backend === 'minio' ? 'http://localhost:9010' : '(vuoto = AWS S3 default per la region)'}
                />
                <div style={{ fontSize: 10.5, color: 'var(--text-3)', marginTop: 4 }}>
                  {form.backend === 'minio'
                    ? 'URL completo MinIO. Default dev: http://localhost:9010 (compose Veridanto).'
                    : 'Lascia vuoto per AWS S3 standard. Valorizza per S3 Custom Endpoint (R2, Wasabi, Backblaze).'}
                </div>
              </div>
              <div className="row" style={{gap:8, alignItems:'center'}}>
                <label style={{ display: 'flex', gap: 6, alignItems: 'center', fontSize: 12 }}>
                  <input
                    type="checkbox"
                    checked={form.forcePathStyle}
                    onChange={e => set('forcePathStyle', e.target.checked)}
                  />
                  forcePathStyle
                </label>
                <span style={{ fontSize: 10.5, color: 'var(--text-3)' }}>
                  ON per MinIO/R2/Wasabi · OFF per AWS S3 (default virtual-hosted)
                </span>
              </div>
            </div>
          </div>
        )}

        {isLocal && (
          <div style={{ gridColumn: '1 / -1' }}>
            <div className="eyebrow" style={{marginBottom:6}}>Filesystem path</div>
            <div className="card" style={{padding:14}}>
              <div className="field">
                <label>Local root path</label>
                <input
                  value={form.localRootPath}
                  onChange={e => set('localRootPath', e.target.value)}
                  placeholder="(vuoto = <repoRoot>/.storage/)"
                />
                <div style={{ fontSize: 10.5, color: 'var(--text-3)', marginTop: 4 }}>
                  Path assoluto della directory storage. Vuoto = default <code>.storage/</code> del worktree.
                </div>
              </div>
            </div>
          </div>
        )}

        {isS3 && (
          <div style={{ gridColumn: '1 / -1' }}>
            <div className="eyebrow" style={{marginBottom:6}}>Credenziali (cifrate AES-GCM in DB)</div>
            <div className="card" style={{padding:14}}>
              <div className="grid grid-2" style={{ gap: 14 }}>
                <div className="field">
                  <label>Access key ID</label>
                  <input
                    value={form.accessKey}
                    onChange={e => set('accessKey', e.target.value)}
                    placeholder={form.backend === 'minio' ? 'minioadmin' : 'AKIA...'}
                  />
                </div>
                <div className="field">
                  <label>Secret access key {settings?.hasSecretKey && (
                    <span style={{ marginLeft: 6, fontSize: 10.5, color: 'var(--text-3)' }}>
                      attuale: <code>{settings?.secretKeyHint || '••••'}</code>
                    </span>
                  )}</label>
                  <input
                    type="password"
                    value={secretDraft}
                    onChange={e => setSecretDraft(e.target.value)}
                    placeholder={settings?.hasSecretKey ? '(lascia vuoto per non modificare)' : 'minioadmin / wJalrXUtnFEMI/K7MDENG/...'}
                  />
                </div>
              </div>
              {settings?.hasSecretKey && (
                <div style={{marginTop:10}}>
                  <Btn variant="ghost" size="sm" onClick={handleClearSecret} disabled={saving}>
                    <Icon name="trash" size={11}/> Rimuovi secret_key
                  </Btn>
                </div>
              )}
            </div>
          </div>
        )}
      </div>

      <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/settings/storage</code> cifra la secret_key (AES-256-GCM) e invalida la cache di <code>getStorage()</code>. Il prossimo upload/download legge la nuova config dal DB. <code>POST /api/settings/storage/test</code> esegue put→get→delete di un blob effimero per validare la connessione. Audit registrato su <code>storage_settings</code> e <code>storage.test</code>.
      </div>
    </div>
  );
}

Object.assign(window, { Customizing });
