(() => {
  const { useState, useEffect, apiFetch, ROLE_LABEL, ROLE_COLOR, fmtDate, setDateTimeConfig } = window.DC;

  const AUTH_EVENT_LABEL = {
    "auth.login": "Login",
    "auth.logout": "Logout",
    "auth.change_password": "Cambio de clave",
  };

  const DATE_TIME_OPTIONS = {
    timeZones: [
      { value: "America/Lima", label: "America/Lima (UTC-5)" },
      { value: "UTC", label: "UTC" },
      { value: "America/Bogota", label: "America/Bogota (UTC-5)" },
      { value: "America/Mexico_City", label: "America/Mexico_City" },
      { value: "America/Santiago", label: "America/Santiago" },
      { value: "America/Argentina/Buenos_Aires", label: "America/Argentina/Buenos_Aires" },
      { value: "Europe/Madrid", label: "Europe/Madrid" },
    ],
    locales: [
      { value: "es-PE", label: "Español (Perú)" },
      { value: "es", label: "Español" },
      { value: "es-CO", label: "Español (Colombia)" },
      { value: "es-MX", label: "Español (México)" },
      { value: "en-US", label: "English (US)" },
    ],
  };

  function SectionTabs({ tabs, activeTab, onChange }) {
    return (
      <div className="card" style={{ marginBottom: 14 }}>
        <div className="card-body" style={{ padding: 12 }}>
          <div className="nav-tabs-wrap settings-tabs-wrap internal-tabs-wrap">
            <div className="nav-tabs settings-tabs internal-tabs">
              {tabs.map((tab) => (
                <button
                  key={tab.key}
                  type="button"
                  onClick={() => onChange(tab.key)}
                  className={`nav-tab${activeTab === tab.key ? " active" : ""}`}
                  title={tab.label}
                >
                  <span className="nav-tab-icon" aria-hidden="true">{tab.icon}</span>
                  <span className="nav-tab-text">{tab.label}</span>
                </button>
              ))}
            </div>
          </div>
        </div>
      </div>
    );
  }

  function MonitorSettingsCard({ toast }) {
    const [form, setForm] = useState({});
    const [saving, setSaving] = useState(false);
    const [testingTelegram, setTestingTelegram] = useState(false);

    useEffect(() => {
      apiFetch("/monitor/settings").then((data) => setForm(data || {}));
    }, []);

    const persistSettings = async ({ silentSuccess = false } = {}) => {
      setSaving(true);
      const response = await apiFetch("/monitor/settings", { method: "PUT", body: form });
      if (response.ok && !silentSuccess) toast("Configuración de alertas guardada");
      if (!response.ok) toast(response.error || "Error", "err");
      setSaving(false);
      return response;
    };

    const save = async () => {
      await persistSettings();
    };

    const testTelegram = async () => {
      setTestingTelegram(true);
      const saveResponse = await persistSettings({ silentSuccess: true });
      if (!saveResponse.ok) {
        setTestingTelegram(false);
        return;
      }

      const response = await apiFetch("/monitor/settings/test-telegram", { method: "POST" });
      if (response.ok) {
        const summary = response.summary || {};
        const telegram = response.telegram || {};
        toast(
          `Telegram enviado a ${telegram.sent || 0}/${telegram.total || 0} destino(s). Total: ${summary.total || 0}, OK: ${summary.ok || 0}, warning: ${summary.warning || 0}, críticos: ${summary.critical || 0}, errores: ${summary.error || 0}`
        );
      } else {
        toast(response.error || "No se pudo enviar la prueba a Telegram", "err");
      }
      setTestingTelegram(false);
    };

    return (
      <div className="card">
        <div className="card-head">🔔 Configuración de alertas de monitoreo</div>
        <div className="card-body">
          <div className="alert alert-info" style={{ marginBottom: 14, fontSize: 11 }}>
            Configura uno o varios canales para avisos automáticos cuando el monitor detecte caídas, lentitud o degradación de servicios.
          </div>
          <div style={{ fontFamily: "Syne,sans-serif", fontWeight: 700, fontSize: 11, marginBottom: 10, color: "#64748b", letterSpacing: "1px" }}>
            EMAIL (GMAIL)
          </div>
          <div className="grid-2" style={{ marginBottom: 16 }}>
            {[
              ["email_from", "Email remitente (Gmail)", "text", "alerts@gmail.com"],
              ["email_pass", "Contraseña de aplicación", "password", "xxxx xxxx xxxx xxxx"],
              ["email_to", "Email(s) destino", "text", "admin@datacole.com, otro@gmail.com"],
              ["alert_cooldown", "Cooldown entre alertas (seg)", "number", "1800"],
            ].map(([key, label, type, placeholder]) => (
              <div key={key} className="field">
                <label>{label}</label>
                <input
                  type={type}
                  value={form[key] || ""}
                  onChange={(event) => setForm((prev) => ({ ...prev, [key]: event.target.value }))}
                  className="input"
                  placeholder={placeholder}
                />
              </div>
            ))}
          </div>
          <div style={{ fontFamily: "Syne,sans-serif", fontWeight: 700, fontSize: 11, marginBottom: 10, color: "#64748b", letterSpacing: "1px" }}>
            TELEGRAM
          </div>
          <div className="grid-2" style={{ marginBottom: 16 }}>
            {[
              ["telegram_bot_token", "Bot token", "password", "123456:AA..."],
              ["telegram_chat_id", "Chat ID(s) destino", "text", "5398383613, 123456789, -1001234567890"],
            ].map(([key, label, type, placeholder]) => (
              <div key={key} className="field">
                <label>{label}</label>
                <input
                  type={type}
                  value={form[key] || ""}
                  onChange={(event) => setForm((prev) => ({ ...prev, [key]: event.target.value }))}
                  className="input"
                  placeholder={placeholder}
                />
              </div>
            ))}
          </div>
          <div className="alert alert-info" style={{ marginBottom: 16, fontSize: 11 }}>
            Usa un solo bot token y separa varios Chat ID con coma o salto de línea para alertar a varias personas o grupos. Recomendado: usar un grupo de operaciones.
          </div>
          <div className="alert alert-info" style={{ marginBottom: 16, fontSize: 11 }}>
            La prueba de Telegram envía un resumen fresco del estado actual de todos los servidores antes de mandar el mensaje.
          </div>
          <div style={{ fontFamily: "Syne,sans-serif", fontWeight: 700, fontSize: 11, marginBottom: 10, color: "#64748b", letterSpacing: "1px" }}>
            SLACK / WEBHOOK
          </div>
          <div className="grid-2" style={{ marginBottom: 16 }}>
            <div className="field">
              <label>Webhook URL</label>
              <input
                type="password"
                value={form.slack_webhook_url || ""}
                onChange={(event) => setForm((prev) => ({ ...prev, slack_webhook_url: event.target.value }))}
                className="input"
                placeholder="https://hooks.slack.com/services/..."
              />
            </div>
          </div>
          <div style={{ fontFamily: "Syne,sans-serif", fontWeight: 700, fontSize: 11, marginBottom: 10, color: "#64748b", letterSpacing: "1px" }}>
            UMBRALES DE ALERTA
          </div>
          <div className="grid-3" style={{ marginBottom: 16 }}>
            {[
              ["cpu_warn", "CPU advertencia %", "70"],
              ["cpu_crit", "CPU crítico %", "90"],
              ["ram_warn", "RAM advertencia %", "75"],
              ["ram_crit", "RAM crítico %", "90"],
              ["disk_warn", "Disco advertencia %", "80"],
              ["disk_crit", "Disco crítico %", "95"],
              ["resp_warn", "Respuesta lenta (ms)", "2000"],
              ["resp_crit", "Respuesta crítica (ms)", "5000"],
            ].map(([key, label, placeholder]) => (
              <div key={key} className="field">
                <label>{label}</label>
                <input
                  type="number"
                  value={form[key] || ""}
                  onChange={(event) => setForm((prev) => ({ ...prev, [key]: event.target.value }))}
                  className="input input-sm"
                  placeholder={placeholder}
                />
              </div>
            ))}
          </div>
          <div className="btn-row">
            <button onClick={save} disabled={saving || testingTelegram} className="btn btn-primary" style={{ opacity: saving || testingTelegram ? 0.6 : 1 }}>
              {saving ? "Guardando..." : "Guardar configuración de alertas"}
            </button>
            <button onClick={testTelegram} disabled={saving || testingTelegram} className="btn btn-secondary">
              {testingTelegram ? "Probando Telegram..." : "Probar Telegram con estado actual"}
            </button>
          </div>
        </div>
      </div>
    );
  }

  function GitHubSettingsCard({
    ghToken,
    setGhToken,
    ghRepo,
    setGhRepo,
    ghBranch,
    setGhBranch,
    ghMasked,
    saveGitHub,
    savingGh,
  }) {
    return (
      <div className="card">
        <div className="card-head">🔑 GitHub — Token y repositorio</div>
        <div className="card-body">
          <div className="alert alert-info" style={{ marginBottom: 14 }}>
            Controla el repositorio principal y la branch por defecto usada por el sistema de deploy.
          </div>
          <div className="grid-2">
            <div className="field" style={{ gridColumn: "1/-1" }}>
              <label>
                GitHub Token {ghMasked && <span style={{ color: "#22d3a5" }}>— {ghMasked}</span>}
              </label>
              <input
                type="password"
                value={ghToken}
                onChange={(event) => setGhToken(event.target.value)}
                className="input"
                placeholder={ghMasked ? "Dejar vacío = no cambiar" : "ghp_xxxxxxxxxxxx"}
              />
            </div>
            <div className="field">
              <label>Repositorio</label>
              <input value={ghRepo} onChange={(event) => setGhRepo(event.target.value)} className="input" placeholder="alexis-pablo/datacole" />
            </div>
            <div className="field">
              <label>Branch por defecto</label>
              <input value={ghBranch} onChange={(event) => setGhBranch(event.target.value)} className="input" placeholder="main" />
            </div>
          </div>
          <button onClick={saveGitHub} disabled={savingGh} className="btn btn-primary" style={{ opacity: savingGh ? 0.6 : 1 }}>
            {savingGh ? "Guardando..." : "Guardar GitHub"}
          </button>
        </div>
      </div>
    );
  }

  function SshSettingsCard({ sshPass, setSshPass, sshMasked, saveSsh, savingSsh }) {
    return (
      <div className="card">
        <div className="card-head">🔐 Contraseña SSH global</div>
        <div className="card-body">
          <div className="alert alert-success" style={{ marginBottom: 14 }}>
            Se usa automáticamente en Deploy, Scan y como respaldo del ambiente de test. {sshMasked && `Configurada: ${sshMasked}`}
          </div>
          <div className="field">
            <label>Nueva contraseña SSH</label>
            <input
              type="password"
              value={sshPass}
              onChange={(event) => setSshPass(event.target.value)}
              className="input"
              placeholder={sshMasked ? "Dejar vacío = no cambiar" : "Contraseña SSH"}
            />
          </div>
          <button onClick={saveSsh} disabled={savingSsh} className="btn btn-green" style={{ width: "100%", opacity: savingSsh ? 0.6 : 1 }}>
            {savingSsh ? "Guardando..." : "Guardar contraseña SSH"}
          </button>
        </div>
      </div>
    );
  }

  function MigrationHookSettingsCard({ migrationToken, setMigrationToken, migrationTokenConfigured, saveMigrationToken, savingMigration }) {
    return (
      <div className="card">
        <div className="card-head">🛢️ Migraciones post-deploy — Token global</div>
        <div className="card-body">
          <div className="alert alert-info" style={{ marginBottom: 14, fontSize: 12 }}>
El hook de migraciones está <strong>activado por defecto</strong> para todo virt_user con deploy; podés desactivarlo o ajustar la ruta por usuario en Servidores. Acá se configura el <strong>token global opcional</strong> que se envía como header <code>X-Deploy-Token</code> en cada llamada a <code>https://&lt;dominio&gt;/&lt;carpeta&gt;/migrations</code>, para que el endpoint valide que la petición viene del sistema de deploy.
          </div>
          <div className="field">
            <label>
              Token de migraciones {migrationTokenConfigured && <span style={{ color: "#22d3a5" }}>— configurado</span>}
            </label>
            <input
              type="password"
              value={migrationToken}
              onChange={(event) => setMigrationToken(event.target.value)}
              className="input"
              placeholder={migrationTokenConfigured ? "Dejar vacío = no cambiar" : "Token compartido (opcional)"}
            />
          </div>
          <button onClick={saveMigrationToken} disabled={savingMigration} className="btn btn-primary" style={{ width: "100%", opacity: savingMigration ? 0.6 : 1 }}>
            {savingMigration ? "Guardando..." : "Guardar token de migraciones"}
          </button>
        </div>
      </div>
    );
  }

  function ClaudeReviewSettingsCard({ claudeToken, setClaudeToken, claudeMasked, claudeReviewEnabled, setClaudeReviewEnabled, saveClaudeReview, savingClaude, testClaudeToken, testingClaude }) {
    return (
      <div className="card">
        <div className="card-head">🤖 Revisión IA pre-deploy (Claude Code)</div>
        <div className="card-body">
          <div className="alert alert-info" style={{ marginBottom: 14, fontSize: 12 }}>
Antes de copiar archivos a producción, Claude Code revisa el diff y puede <strong>bloquear</strong> el deploy ante problemas graves (fatales, seguridad, pérdida de datos). Usa el <strong>token de tu suscripción</strong> (<code>claude setup-token</code>), no una API key. Si falla o no hay token, hace <strong>fail-open</strong> (avisa pero no frena). Los checks de sintaxis y migraciones funcionan aparte, siempre.
          </div>
          <div className="field" style={{ display: "flex", alignItems: "center", gap: 8 }}>
            <input
              type="checkbox"
              id="claude_review_enabled"
              checked={claudeReviewEnabled}
              onChange={(event) => setClaudeReviewEnabled(event.target.checked)}
            />
            <label htmlFor="claude_review_enabled" style={{ margin: 0 }}>Activar revisión IA bloqueante</label>
          </div>
          <div className="field">
            <label>
              Token de Claude Code {claudeMasked && <span style={{ color: "#22d3a5" }}>— configurado ({claudeMasked})</span>}
            </label>
            <input
              type="password"
              value={claudeToken}
              onChange={(event) => setClaudeToken(event.target.value)}
              className="input"
              placeholder={claudeMasked ? "Dejar vacío = no cambiar" : "Token de claude setup-token"}
            />
          </div>
          <div style={{ display: "flex", gap: 8 }}>
            <button onClick={testClaudeToken} disabled={testingClaude} className="btn" style={{ flex: 1, opacity: testingClaude ? 0.6 : 1 }}>
              {testingClaude ? "Probando..." : "Probar token"}
            </button>
            <button onClick={saveClaudeReview} disabled={savingClaude} className="btn btn-primary" style={{ flex: 1, opacity: savingClaude ? 0.6 : 1 }}>
              {savingClaude ? "Guardando..." : "Guardar revisión IA"}
            </button>
          </div>
        </div>
      </div>
    );
  }

  function ClientDbSettingsCard({ clientDbForm, setClientDbForm, clientDbMasked, saveClientDb, savingClientDb }) {
    return (
      <div className="card">
        <div className="card-head">🔑 Credenciales BD de clientes (global)</div>
        <div className="card-body">
          <div className="alert alert-info" style={{ marginBottom: 14, fontSize: 12 }}>
            Usuario y contraseña que el agente NOD usa para conectarse a la BD MySQL de cualquier cliente. Como en datacole las credenciales SQL son las mismas para todos los clientes, se configuran una sola vez acá. La <code>data_base</code> específica de cada cliente la resuelve el registro central por dominio.
          </div>
          <div className="grid-2">
            <div className="field">
              <label>Usuario MySQL</label>
              <input
                value={clientDbForm.client_db_user}
                onChange={(event) => setClientDbForm((prev) => ({ ...prev, client_db_user: event.target.value }))}
                className="input"
                placeholder="datacole"
              />
            </div>
            <div className="field">
              <label>
                Contraseña MySQL {clientDbMasked && <span style={{ color: "#22d3a5" }}>— {clientDbMasked}</span>}
              </label>
              <input
                type="password"
                value={clientDbForm.client_db_password}
                onChange={(event) => setClientDbForm((prev) => ({ ...prev, client_db_password: event.target.value }))}
                className="input"
                placeholder={clientDbMasked ? "Vacío = mantener actual" : "Contraseña"}
              />
            </div>
            <div className="field">
              <label>Puerto MySQL</label>
              <input
                value={clientDbForm.client_db_port}
                onChange={(event) => setClientDbForm((prev) => ({ ...prev, client_db_port: event.target.value }))}
                className="input"
                placeholder="3306"
              />
            </div>
            <div className="field">
              <label>Driver</label>
              <select
                value={clientDbForm.client_db_driver}
                onChange={(event) => setClientDbForm((prev) => ({ ...prev, client_db_driver: event.target.value }))}
                className="input"
              >
                <option value="mysqli">mysqli (MariaDB/MySQL)</option>
                <option value="mysql">mysql</option>
                <option value="pgsql">pgsql (PostgreSQL)</option>
              </select>
            </div>
          </div>
          <button onClick={saveClientDb} disabled={savingClientDb} className="btn btn-primary" style={{ width: "100%", opacity: savingClientDb ? 0.6 : 1 }}>
            {savingClientDb ? "Guardando..." : "Guardar credenciales BD de clientes"}
          </button>
        </div>
      </div>
    );
  }

  function ServiceApiKeyCard({ apiKeyMasked, generating, revoking, onGenerate, onRevoke }) {
    return (
      <div className="card">
        <div className="card-head">🤖 API Key de servicio</div>
        <div className="card-body">
          <div className="alert alert-success" style={{ marginBottom: 14 }}>
            Clave estática sin vencimiento para integraciones externas (agente, scripts). No reemplaza el login de usuarios.
            {apiKeyMasked && <span> Activa: <code>{apiKeyMasked}</code></span>}
          </div>
          <div style={{ display: "flex", gap: 10 }}>
            <button onClick={onGenerate} disabled={generating || revoking} className="btn btn-primary" style={{ flex: 1, opacity: generating ? 0.6 : 1 }}>
              {generating ? "Generando..." : apiKeyMasked ? "Regenerar clave" : "Generar clave"}
            </button>
            {apiKeyMasked && (
              <button onClick={onRevoke} disabled={generating || revoking} className="btn btn-danger" style={{ opacity: revoking ? 0.6 : 1 }}>
                {revoking ? "Revocando..." : "Revocar"}
              </button>
            )}
          </div>
        </div>
      </div>
    );
  }

  function DateTimeSettingsCard({
    dateTimeForm,
    setDateTimeForm,
    saveDateTime,
    savingDateTime,
  }) {
    return (
      <div className="card">
        <div className="card-head">🕒 Fecha y hora del sistema</div>
        <div className="card-body">
          <div className="alert alert-info" style={{ marginBottom: 14 }}>
            Esta configuración define cómo se muestran las fechas en toda la app y también cómo interpreta el scheduler las programaciones `cron`.
          </div>
          <div className="grid-2">
            <div className="field">
              <label>Zona horaria</label>
              <select
                value={dateTimeForm.app_time_zone}
                onChange={(event) => setDateTimeForm((prev) => ({ ...prev, app_time_zone: event.target.value }))}
                className="input select"
              >
                {DATE_TIME_OPTIONS.timeZones.map((timeZone) => (
                  <option key={timeZone.value} value={timeZone.value}>{timeZone.label}</option>
                ))}
              </select>
            </div>
            <div className="field">
              <label>Locale</label>
              <select
                value={dateTimeForm.app_locale}
                onChange={(event) => setDateTimeForm((prev) => ({ ...prev, app_locale: event.target.value }))}
                className="input select"
              >
                {DATE_TIME_OPTIONS.locales.map((locale) => (
                  <option key={locale.value} value={locale.value}>{locale.label}</option>
                ))}
              </select>
            </div>
          </div>
          <div className="alert alert-success" style={{ marginBottom: 14 }}>
            Recomendado para Lima: zona horaria `America/Lima` y locale `es-PE`.
          </div>
          <button onClick={saveDateTime} disabled={savingDateTime} className="btn btn-primary" style={{ opacity: savingDateTime ? 0.6 : 1 }}>
            {savingDateTime ? "Guardando..." : "Guardar fecha y hora"}
          </button>
        </div>
      </div>
    );
  }

  function TestSyncSettingsCard({ testSyncForm, setTestSyncForm, testSyncMasked, saveTestSync, savingTestSync }) {
    return (
      <div className="card">
        <div className="card-head">🧪 Ambiente de test</div>
        <div className="card-body">
          <div className="alert alert-info" style={{ marginBottom: 14 }}>
            Configura aquí el servidor de validación al que se le hará `git pull` después de unir cambios a `main`.
          </div>
          <div className="grid-2">
            <div className="field">
              <label>Habilitado</label>
              <select
                value={testSyncForm.test_sync_enabled ? "1" : "0"}
                onChange={(event) => setTestSyncForm((prev) => ({ ...prev, test_sync_enabled: event.target.value === "1" }))}
                className="input"
              >
                <option value="1">Sí</option>
                <option value="0">No</option>
              </select>
            </div>
            <div className="field">
              <label>Nombre visible</label>
              <input
                value={testSyncForm.test_sync_label}
                onChange={(event) => setTestSyncForm((prev) => ({ ...prev, test_sync_label: event.target.value }))}
                className="input"
                placeholder="Test"
              />
            </div>
            <div className="field">
              <label>Host o IP</label>
              <input
                value={testSyncForm.test_sync_host}
                onChange={(event) => setTestSyncForm((prev) => ({ ...prev, test_sync_host: event.target.value }))}
                className="input"
                placeholder="37.60.230.126"
              />
            </div>
            <div className="field">
              <label>Puerto SSH</label>
              <input
                value={testSyncForm.test_sync_port}
                onChange={(event) => setTestSyncForm((prev) => ({ ...prev, test_sync_port: event.target.value }))}
                className="input"
                placeholder="22"
              />
            </div>
            <div className="field">
              <label>Usuario SSH</label>
              <input
                value={testSyncForm.test_sync_username}
                onChange={(event) => setTestSyncForm((prev) => ({ ...prev, test_sync_username: event.target.value }))}
                className="input"
                placeholder="root"
              />
            </div>
            <div className="field">
              <label>Branch</label>
              <input
                value={testSyncForm.test_sync_branch}
                onChange={(event) => setTestSyncForm((prev) => ({ ...prev, test_sync_branch: event.target.value }))}
                className="input"
                placeholder="main"
              />
            </div>
            <div className="field" style={{ gridColumn: "1/-1" }}>
              <label>Ruta del repo remoto</label>
              <input
                value={testSyncForm.test_sync_repo_path}
                onChange={(event) => setTestSyncForm((prev) => ({ ...prev, test_sync_repo_path: event.target.value }))}
                className="input"
                placeholder="/home/onboarding/public_html"
              />
            </div>
            <div className="field" style={{ gridColumn: "1/-1" }}>
              <label>Carpeta destino de test</label>
              <input
                value={testSyncForm.test_sync_target_dir || ""}
                onChange={(event) => setTestSyncForm((prev) => ({ ...prev, test_sync_target_dir: event.target.value }))}
                className="input"
                placeholder="test"
              />
              <div style={{ fontSize: 10, color: "#64748b", marginTop: 6 }}>
                Carpeta relativa dentro de la ruta remota base donde quieres copiar los cambios del ambiente de prueba.
              </div>
            </div>
            <div className="field" style={{ gridColumn: "1/-1" }}>
              <label>URL base del ambiente test</label>
              <input
                value={testSyncForm.test_sync_base_url || ""}
                onChange={(event) => setTestSyncForm((prev) => ({ ...prev, test_sync_base_url: event.target.value }))}
                className="input"
                placeholder="https://midominio.com/test"
              />
              <div style={{ fontSize: 10, color: "#64748b", marginTop: 6 }}>
                Se usa para las acciones HTTP del ambiente test. Ejemplo: `https://dominio/test`.
              </div>
            </div>
            <div className="field" style={{ gridColumn: "1/-1" }}>
              <label>
                Contraseña SSH específica {testSyncMasked && <span style={{ color: "#22d3a5" }}>— {testSyncMasked}</span>}
              </label>
              <input
                type="password"
                value={testSyncForm.test_sync_password}
                onChange={(event) => setTestSyncForm((prev) => ({ ...prev, test_sync_password: event.target.value }))}
                className="input"
                placeholder={testSyncMasked ? "Vacío = mantener actual; si no hay, usará la SSH global" : "Opcional: si queda vacío usará la SSH global"}
              />
            </div>
          </div>
          <button onClick={saveTestSync} disabled={savingTestSync} className="btn btn-primary" style={{ opacity: savingTestSync ? 0.6 : 1 }}>
            {savingTestSync ? "Guardando..." : "Guardar ambiente de test"}
          </button>
        </div>
      </div>
    );
  }

  function CentralDbSettingsCard({ centralDbForm, setCentralDbForm, centralDbMasked, saveCentralDb, savingCentralDb }) {
    return (
      <div className="card" style={{ marginTop: 16 }}>
        <div className="card-head">🗂️ Registro central de bases de datos</div>
        <div className="card-body">
          <div style={{ fontSize: 12, color: "#94a3b8", marginBottom: 12 }}>
            Servidor que aloja la tabla maestra <code>dc_dbs</code> (mapea dominio → BD del cliente).
            El agente NOD lo consulta para saber qué base usar antes de operar.
          </div>
          <div className="grid-2">
            <div className="field">
              <label>Host o IP del registro</label>
              <input
                value={centralDbForm.central_db_host}
                onChange={(event) => setCentralDbForm((prev) => ({ ...prev, central_db_host: event.target.value }))}
                className="input"
                placeholder="86.48.28.225"
              />
            </div>
            <div className="field">
              <label>Puerto SSH</label>
              <input
                value={centralDbForm.central_db_ssh_port}
                onChange={(event) => setCentralDbForm((prev) => ({ ...prev, central_db_ssh_port: event.target.value }))}
                className="input"
                placeholder="22"
              />
            </div>
            <div className="field">
              <label>Usuario SSH</label>
              <input
                value={centralDbForm.central_db_ssh_user}
                onChange={(event) => setCentralDbForm((prev) => ({ ...prev, central_db_ssh_user: event.target.value }))}
                className="input"
                placeholder="root"
              />
            </div>
            <div className="field">
              <label>
                Contraseña SSH {centralDbMasked.ssh && <span style={{ color: "#22d3a5" }}>— {centralDbMasked.ssh}</span>}
              </label>
              <input
                type="password"
                value={centralDbForm.central_db_ssh_password}
                onChange={(event) => setCentralDbForm((prev) => ({ ...prev, central_db_ssh_password: event.target.value }))}
                className="input"
                placeholder={centralDbMasked.ssh ? "Vacío = mantener actual" : "Si queda vacío usa la SSH global"}
              />
            </div>
            <div className="field">
              <label>Usuario MySQL</label>
              <input
                value={centralDbForm.central_db_user}
                onChange={(event) => setCentralDbForm((prev) => ({ ...prev, central_db_user: event.target.value }))}
                className="input"
                placeholder="root"
              />
            </div>
            <div className="field">
              <label>
                Contraseña MySQL {centralDbMasked.db && <span style={{ color: "#22d3a5" }}>— {centralDbMasked.db}</span>}
              </label>
              <input
                type="password"
                value={centralDbForm.central_db_password}
                onChange={(event) => setCentralDbForm((prev) => ({ ...prev, central_db_password: event.target.value }))}
                className="input"
                placeholder={centralDbMasked.db ? "Vacío = mantener actual" : "Si queda vacío usa la contraseña SSH"}
              />
            </div>
            <div className="field">
              <label>Database</label>
              <input
                value={centralDbForm.central_db_database}
                onChange={(event) => setCentralDbForm((prev) => ({ ...prev, central_db_database: event.target.value }))}
                className="input"
                placeholder="ticempresarial_central"
              />
            </div>
            <div className="field">
              <label>Tabla</label>
              <input
                value={centralDbForm.central_db_table}
                onChange={(event) => setCentralDbForm((prev) => ({ ...prev, central_db_table: event.target.value }))}
                className="input"
                placeholder="dc_dbs"
              />
            </div>
          </div>
          <button onClick={saveCentralDb} disabled={savingCentralDb} className="btn btn-primary" style={{ opacity: savingCentralDb ? 0.6 : 1 }}>
            {savingCentralDb ? "Guardando..." : "Guardar registro central"}
          </button>
        </div>
      </div>
    );
  }

  function MyAccountCard({ cpForm, setCpForm, cpError, changePassword }) {
    return (
      <div className="card">
        <div className="card-head">🔒 Mi cuenta</div>
        <div className="card-body">
          {[
            ["current", "Contraseña actual"],
            ["next", "Nueva contraseña"],
            ["confirm", "Confirmar nueva"],
          ].map(([key, label]) => (
            <div key={key} className="field">
              <label>{label}</label>
              <input
                type="password"
                value={cpForm[key]}
                onChange={(event) => setCpForm((prev) => ({ ...prev, [key]: event.target.value }))}
                className="input"
                placeholder="••••••••"
              />
            </div>
          ))}
          {cpError && <div className="alert alert-danger">{cpError}</div>}
          <button onClick={changePassword} className="btn btn-primary">
            Cambiar contraseña
          </button>
        </div>
      </div>
    );
  }

  function summarizeAgent(userAgent) {
    const value = String(userAgent || "").trim();
    if (!value) return "Sin user-agent";
    return value.slice(0, 90);
  }

  function describeAuthEvent(event) {
    const label = AUTH_EVENT_LABEL[event.action] || event.action || "Evento";
    if (event.action === "auth.login") {
      return event.status === "ok" ? label : `${label} rechazado`;
    }
    return label;
  }

  function SystemUsersTab({
    appUsers,
    currentUser,
    showUserForm,
    setShowUserForm,
    editUser,
    setEditUser,
    userForm,
    setUserForm,
    saveUser,
    deleteUser,
    reloadAppUsers,
  }) {
    const [search, setSearch] = useState("");
    const [roleFilter, setRoleFilter] = useState("all");
    const [expandedUserId, setExpandedUserId] = useState(null);

    const filteredUsers = appUsers.filter((user) => {
      const haystack = `${user.username} ${user.full_name || ""} ${user.role}`.toLowerCase();
      const searchOk = !search.trim() || haystack.includes(search.trim().toLowerCase());
      const roleOk = roleFilter === "all" || user.role === roleFilter;
      return searchOk && roleOk;
    });

    const summary = {
      total: appUsers.length,
      active: appUsers.filter((user) => user.active).length,
      inactive: appUsers.filter((user) => !user.active).length,
      sessions: appUsers.filter((user) => user.session_active).length,
    };

    return (
      <div style={{ display: "flex", flexDirection: "column", gap: 14 }}>
        <div className="card">
          <div className="card-head">
            <span>👥 Usuarios del sistema</span>
            <button
              onClick={() => {
                setShowUserForm(true);
                setEditUser(null);
                setUserForm({ username: "", password: "", full_name: "", role: "viewer" });
              }}
              className="btn btn-ghost btn-sm"
            >
              + Nuevo
            </button>
          </div>
          <div className="card-body">
            <div className="alert alert-info" style={{ marginBottom: 14, fontSize: 11 }}>
              Esta vista concentra administración de usuarios, estado de acceso y actividad reciente de autenticación desde auditoría.
            </div>

            <div className="settings-summary-grid">
              {[
                ["Usuarios", summary.total, "#00e5ff"],
                ["Activos", summary.active, "#22d3a5"],
                ["Inactivos", summary.inactive, "#fbbf24"],
                ["Sesión persistente", summary.sessions, "#a78bfa"],
              ].map(([label, value, tone]) => (
                <div key={label} className="settings-summary-card" style={{ borderColor: `${tone}33` }}>
                  <div className="settings-summary-label">{label}</div>
                  <div className="settings-summary-value" style={{ color: tone }}>{value}</div>
                </div>
              ))}
            </div>

            <div className="grid-2" style={{ marginTop: 14, marginBottom: 14 }}>
              <div className="field" style={{ marginBottom: 0 }}>
                <label>Buscar usuario</label>
                <input
                  value={search}
                  onChange={(event) => setSearch(event.target.value)}
                  className="input"
                  placeholder="Nombre, username o rol"
                />
              </div>
              <div className="field" style={{ marginBottom: 0 }}>
                <label>Filtrar por rol</label>
                <select value={roleFilter} onChange={(event) => setRoleFilter(event.target.value)} className="input">
                  <option value="all">Todos</option>
                  <option value="superadmin">Super Admin</option>
                  <option value="admin">Admin</option>
                  <option value="viewer">Viewer</option>
                </select>
              </div>
            </div>

            {showUserForm && (
              <div className="settings-user-editor">
                <div style={{ fontFamily: "Syne,sans-serif", fontWeight: 700, fontSize: 15, marginBottom: 12 }}>
                  {editUser ? "Editar usuario" : "Crear usuario"}
                </div>
                <div className="grid-2">
                  <div className="field">
                    <label>Username</label>
                    <input
                      value={userForm.username}
                      onChange={(event) => setUserForm((prev) => ({ ...prev, username: event.target.value }))}
                      className="input"
                      disabled={!!editUser}
                    />
                  </div>
                  <div className="field">
                    <label>Nombre completo</label>
                    <input
                      value={userForm.full_name}
                      onChange={(event) => setUserForm((prev) => ({ ...prev, full_name: event.target.value }))}
                      className="input"
                    />
                  </div>
                  <div className="field">
                    <label>Contraseña {editUser && "(vacío = no cambiar)"}</label>
                    <input
                      type="password"
                      value={userForm.password}
                      onChange={(event) => setUserForm((prev) => ({ ...prev, password: event.target.value }))}
                      className="input"
                      placeholder="••••••••"
                    />
                  </div>
                  <div className="field">
                    <label>Rol</label>
                    <select
                      value={userForm.role}
                      onChange={(event) => setUserForm((prev) => ({ ...prev, role: event.target.value }))}
                      className="input"
                    >
                      <option value="viewer">Viewer</option>
                      <option value="admin">Admin</option>
                      <option value="superadmin">Super Admin</option>
                    </select>
                  </div>
                </div>
                <div className="btn-row">
                  <button
                    onClick={() => {
                      setShowUserForm(false);
                      setEditUser(null);
                    }}
                    className="btn btn-secondary"
                  >
                    Cancelar
                  </button>
                  <button onClick={saveUser} className="btn btn-primary">
                    {editUser ? "Guardar cambios" : "Crear usuario"}
                  </button>
                </div>
              </div>
            )}

            <div style={{ display: "flex", flexDirection: "column", gap: 10 }}>
              {filteredUsers.map((user) => {
                const isExpanded = expandedUserId === user.id;
                const canDelete = user.id !== currentUser?.id;
                return (
                  <div key={user.id} className="settings-user-card">
                    <div className="settings-user-top">
                      <div style={{ display: "flex", gap: 12, alignItems: "flex-start", minWidth: 0, flex: 1 }}>
                        <div
                          style={{
                            width: 42,
                            height: 42,
                            borderRadius: "50%",
                            background: `${ROLE_COLOR[user.role]}22`,
                            border: `1px solid ${ROLE_COLOR[user.role]}44`,
                            display: "flex",
                            alignItems: "center",
                            justifyContent: "center",
                            fontSize: 16,
                            flexShrink: 0,
                          }}
                        >
                          {(user.full_name || user.username)[0].toUpperCase()}
                        </div>
                        <div style={{ minWidth: 0, flex: 1 }}>
                          <div style={{ display: "flex", gap: 8, flexWrap: "wrap", alignItems: "center" }}>
                            <div style={{ fontFamily: "Syne,sans-serif", fontWeight: 800, fontSize: 18, color: "#f8fafc" }}>
                              {user.full_name || user.username}
                            </div>
                            <div style={{ fontSize: 12, color: "#64748b" }}>@{user.username}</div>
                          </div>
                          <div style={{ display: "flex", gap: 6, marginTop: 8, flexWrap: "wrap" }}>
                            <span
                              className="tag"
                              style={{
                                borderColor: `${ROLE_COLOR[user.role]}44`,
                                color: ROLE_COLOR[user.role],
                                background: `${ROLE_COLOR[user.role]}15`,
                              }}
                            >
                              {ROLE_LABEL[user.role]}
                            </span>
                            <span className="tag" style={{ borderColor: user.active ? "#22d3a544" : "#f43f5e44", color: user.active ? "#22d3a5" : "#f43f5e", background: user.active ? "#22d3a515" : "#f43f5e15" }}>
                              {user.active ? "activo" : "inactivo"}
                            </span>
                            <span className="tag" style={{ borderColor: user.session_active ? "#a78bfa44" : "#64748b44", color: user.session_active ? "#a78bfa" : "#94a3b8", background: user.session_active ? "#a78bfa15" : "#64748b15" }}>
                              {user.session_active ? "sesión persistente" : "sin sesión persistente"}
                            </span>
                          </div>
                        </div>
                      </div>

                      <div className="settings-user-actions">
                        <button
                          onClick={() => setExpandedUserId((prev) => prev === user.id ? null : user.id)}
                          className="btn btn-ghost btn-xs"
                          style={{ color: isExpanded ? "#00e5ff" : "#94a3b8", borderColor: isExpanded ? "#00e5ff44" : "#1e2a3a" }}
                        >
                          {isExpanded ? "ocultar" : "actividad"}
                        </button>
                        <button
                          onClick={() =>
                            apiFetch(`/app-users/${user.id}`, { method: "PUT", body: { active: user.active ? 0 : 1 } }).then(reloadAppUsers)
                          }
                          className="btn btn-ghost btn-xs"
                          style={{ color: user.active ? "#fbbf24" : "#22d3a5" }}
                        >
                          {user.active ? "desact." : "activar"}
                        </button>
                        <button
                          onClick={() => {
                            setEditUser(user);
                            setUserForm({
                              username: user.username,
                              password: "",
                              full_name: user.full_name || "",
                              role: user.role,
                            });
                            setShowUserForm(true);
                          }}
                          className="btn btn-ghost btn-xs"
                        >
                          ✏ editar
                        </button>
                        {canDelete && (
                          <button onClick={() => deleteUser(user.id)} className="btn btn-ghost btn-xs" style={{ color: "#f43f5e", borderColor: "#f43f5e44" }}>
                            ✕ eliminar
                          </button>
                        )}
                      </div>
                    </div>

                    <div className="settings-user-meta">
                      <div><span className="settings-user-meta-label">Último login</span> {user.last_login ? fmtDate(user.last_login) : "Nunca"}</div>
                      <div><span className="settings-user-meta-label">Última actividad auth</span> {user.last_seen_auth_at ? fmtDate(user.last_seen_auth_at) : "Sin registros"}</div>
                      <div><span className="settings-user-meta-label">Creado</span> {user.created_at ? fmtDate(user.created_at) : "—"}</div>
                      <div><span className="settings-user-meta-label">Eventos</span> {user.auth_event_count || 0}</div>
                    </div>

                    {isExpanded && (
                      <div className="settings-user-history">
                        <div style={{ fontSize: 11, textTransform: "uppercase", letterSpacing: "1px", color: "#64748b", marginBottom: 10 }}>
                          Historial reciente de sesión
                        </div>
                        {!user.auth_history?.length ? (
                          <div className="alert alert-info" style={{ marginBottom: 0, fontSize: 11 }}>
                            No hay eventos recientes de autenticación para este usuario.
                          </div>
                        ) : (
                          <div style={{ display: "flex", flexDirection: "column", gap: 8 }}>
                            {user.auth_history.map((event, index) => (
                              <div key={`${user.id}-${event.action}-${event.created_at}-${index}`} className="settings-auth-event">
                                <div style={{ display: "flex", justifyContent: "space-between", gap: 10, flexWrap: "wrap", marginBottom: 4 }}>
                                  <div style={{ color: event.status === "ok" ? "#22d3a5" : "#fbbf24", fontWeight: 700, fontSize: 12 }}>
                                    {describeAuthEvent(event)}
                                  </div>
                                  <div style={{ fontSize: 10, color: "#94a3b8" }}>
                                    {event.created_at ? fmtDate(event.created_at) : "—"}
                                  </div>
                                </div>
                                <div style={{ fontSize: 10, color: "#94a3b8", lineHeight: 1.6 }}>
                                  IP: <span style={{ color: "#e2e8f0" }}>{event.ip_address || "—"}</span>
                                  {" · "}
                                  Cliente: <span style={{ color: "#e2e8f0" }}>{summarizeAgent(event.user_agent)}</span>
                                </div>
                              </div>
                            ))}
                          </div>
                        )}
                      </div>
                    )}
                  </div>
                );
              })}
            </div>
          </div>
        </div>
      </div>
    );
  }

  function SettingsView({ currentUser, toast }) {
    const isSuperAdmin = currentUser?.role === "superadmin";
    const [activeTab, setActiveTab] = useState(() => {
      try {
        return window.localStorage.getItem("dc:tabs:settings") || (isSuperAdmin ? "users" : "account");
      } catch (_error) {
        return isSuperAdmin ? "users" : "account";
      }
    });
    const [ghToken, setGhToken] = useState("");
    const [ghRepo, setGhRepo] = useState("");
    const [ghBranch, setGhBranch] = useState("main");
    const [ghMasked, setGhMasked] = useState("");
    const [savingGh, setSavingGh] = useState(false);
    const [sshPass, setSshPass] = useState("");
    const [sshMasked, setSshMasked] = useState("");
    const [savingSsh, setSavingSsh] = useState(false);
    const [apiKeyMasked, setApiKeyMasked] = useState("");
    const [generatingApiKey, setGeneratingApiKey] = useState(false);
    const [revokingApiKey, setRevokingApiKey] = useState(false);
    const [dateTimeForm, setDateTimeForm] = useState({
      app_time_zone: "America/Lima",
      app_locale: "es-PE",
    });
    const [savingDateTime, setSavingDateTime] = useState(false);
    const [savingTestSync, setSavingTestSync] = useState(false);
    const [testSyncForm, setTestSyncForm] = useState({
      test_sync_enabled: false,
      test_sync_label: "Test",
      test_sync_host: "",
      test_sync_port: "22",
      test_sync_username: "root",
      test_sync_repo_path: "",
      test_sync_branch: "main",
      test_sync_target_dir: "",
      test_sync_base_url: "",
      test_sync_password: "",
    });
    const [testSyncMasked, setTestSyncMasked] = useState("");
    const [savingCentralDb, setSavingCentralDb] = useState(false);
    const [centralDbForm, setCentralDbForm] = useState({
      central_db_host: "",
      central_db_ssh_port: "22",
      central_db_ssh_user: "root",
      central_db_user: "root",
      central_db_database: "ticempresarial_central",
      central_db_table: "dc_dbs",
      central_db_ssh_password: "",
      central_db_password: "",
    });
    const [centralDbMasked, setCentralDbMasked] = useState({ ssh: "", db: "" });
    const [savingMigration, setSavingMigration] = useState(false);
    const [migrationToken, setMigrationToken] = useState("");
    const [migrationTokenConfigured, setMigrationTokenConfigured] = useState(false);
    const [savingClaude, setSavingClaude] = useState(false);
    const [testingClaude, setTestingClaude] = useState(false);
    const [claudeToken, setClaudeToken] = useState("");
    const [claudeMasked, setClaudeMasked] = useState("");
    const [claudeReviewEnabled, setClaudeReviewEnabled] = useState(false);
    const [savingClientDb, setSavingClientDb] = useState(false);
    const [clientDbForm, setClientDbForm] = useState({
      client_db_user: "",
      client_db_password: "",
      client_db_port: "3306",
      client_db_driver: "mysqli",
    });
    const [clientDbMasked, setClientDbMasked] = useState("");
    const [appUsers, setAppUsers] = useState([]);
    const [showUserForm, setShowUserForm] = useState(false);
    const [editUser, setEditUser] = useState(null);
    const [userForm, setUserForm] = useState({ username: "", password: "", full_name: "", role: "viewer" });
    const [cpForm, setCpForm] = useState({ current: "", next: "", confirm: "" });
    const [cpError, setCpError] = useState("");

    useEffect(() => {
      if (isSuperAdmin) {
        apiFetch("/settings").then((settings) => {
          setGhRepo(settings.gh_repo || "");
          setGhBranch(settings.gh_branch || "main");
          setGhMasked(settings.gh_token_masked || "");
          setSshMasked(settings.ssh_password_masked || "");
          setApiKeyMasked(settings.service_api_key_configured ? "configurada" : "");
          setDateTimeForm({
            app_time_zone: settings.app_time_zone || "America/Lima",
            app_locale: settings.app_locale || "es-PE",
          });
          setTestSyncMasked(settings.test_sync_password_masked || "");
          setCentralDbMasked({
            ssh: settings.central_db_ssh_password_masked || "",
            db: settings.central_db_password_masked || "",
          });
          setClientDbMasked(settings.client_db_password_masked || "");
          setMigrationTokenConfigured(Boolean(settings.migration_hook_token_configured));
          setClaudeMasked(settings.claude_code_token_masked || "");
          setClaudeReviewEnabled(settings.claude_ai_review_enabled === "1");
          setClientDbForm({
            client_db_user: settings.client_db_user || "",
            client_db_password: "",
            client_db_port: settings.client_db_port || "3306",
            client_db_driver: settings.client_db_driver || "mysqli",
          });
          setCentralDbForm({
            central_db_host: settings.central_db_host || "",
            central_db_ssh_port: settings.central_db_ssh_port || "22",
            central_db_ssh_user: settings.central_db_ssh_user || "root",
            central_db_user: settings.central_db_user || "root",
            central_db_database: settings.central_db_database || "ticempresarial_central",
            central_db_table: settings.central_db_table || "dc_dbs",
            central_db_ssh_password: "",
            central_db_password: "",
          });
          setTestSyncForm({
            test_sync_enabled: settings.test_sync_enabled === "1",
            test_sync_label: settings.test_sync_label || "Test",
            test_sync_host: settings.test_sync_host || "",
            test_sync_port: settings.test_sync_port || "22",
            test_sync_username: settings.test_sync_username || "root",
            test_sync_repo_path: settings.test_sync_repo_path || "",
            test_sync_branch: settings.test_sync_branch || "main",
            test_sync_target_dir: settings.test_sync_target_dir || "",
            test_sync_base_url: settings.test_sync_base_url || "",
            test_sync_password: "",
          });
        });
        apiFetch("/app-users").then((data) => setAppUsers(Array.isArray(data) ? data : []));
      }
    }, [isSuperAdmin]);

    useEffect(() => {
      if (!isSuperAdmin) setActiveTab("account");
    }, [isSuperAdmin]);

    const reloadAppUsers = () =>
      apiFetch("/app-users").then((data) => setAppUsers(Array.isArray(data) ? data : []));

    const saveGitHub = async () => {
      setSavingGh(true);
      const body = { gh_repo: ghRepo, gh_branch: ghBranch };
      if (ghToken) body.gh_token = ghToken;
      const response = await apiFetch("/settings", { method: "PUT", body });
      if (response.ok) {
        toast("GitHub guardado");
        setGhToken("");
        const settings = await apiFetch("/settings");
        setGhMasked(settings.gh_token_masked || "");
      } else {
        toast(response.error || "Error", "err");
      }
      setSavingGh(false);
    };

    const saveSsh = async () => {
      if (!sshPass) {
        toast("Ingresa contraseña", "err");
        return;
      }
      setSavingSsh(true);
      const response = await apiFetch("/settings", { method: "PUT", body: { ssh_password: sshPass } });
      if (response.ok) {
        toast("Contraseña SSH guardada");
        setSshPass("");
        const settings = await apiFetch("/settings");
        setSshMasked(settings.ssh_password_masked || "");
      } else {
        toast(response.error || "Error", "err");
      }
      setSavingSsh(false);
    };

    const saveMigrationToken = async () => {
      setSavingMigration(true);
      const response = await apiFetch("/settings", { method: "PUT", body: { migration_hook_token: migrationToken } });
      if (response.ok) {
        toast("Token de migraciones guardado");
        setMigrationToken("");
        const settings = await apiFetch("/settings");
        setMigrationTokenConfigured(Boolean(settings.migration_hook_token_configured));
      } else {
        toast(response.error || "Error", "err");
      }
      setSavingMigration(false);
    };

    const saveClaudeReview = async () => {
      setSavingClaude(true);
      const body = { claude_ai_review_enabled: claudeReviewEnabled };
      if (claudeToken) body.claude_code_token = claudeToken;
      const response = await apiFetch("/settings", { method: "PUT", body });
      if (response.ok) {
        toast("Revisión IA pre-deploy guardada");
        setClaudeToken("");
        const settings = await apiFetch("/settings");
        setClaudeMasked(settings.claude_code_token_masked || "");
        setClaudeReviewEnabled(settings.claude_ai_review_enabled === "1");
      } else {
        toast(response.error || "Error", "err");
      }
      setSavingClaude(false);
    };

    const testClaudeToken = async () => {
      setTestingClaude(true);
      try {
        const body = claudeToken ? { claude_code_token: claudeToken } : {};
        const response = await apiFetch("/settings/claude-code/test", { method: "POST", body });
        if (response && response.ok) {
          toast(`✅ Token operativo — ${response.message || "Claude Code respondió"}`);
        } else {
          toast(`❌ ${(response && response.message) || response?.error || "Falló la validación del token"}`, "err");
        }
      } catch (error) {
        toast(`❌ Error al probar el token: ${error.message}`, "err");
      } finally {
        setTestingClaude(false);
      }
    };

    const generateApiKey = async () => {
      setGeneratingApiKey(true);
      const response = await apiFetch("/settings/service-api-key", { method: "POST" });
      if (response.ok) {
        toast(`Clave generada (cópiala ahora, no se mostrará de nuevo): ${response.service_api_key}`);
        setApiKeyMasked("configurada");
      } else {
        toast(response.error || "Error al generar la clave", "err");
      }
      setGeneratingApiKey(false);
    };

    const revokeApiKey = async () => {
      if (!window.confirm("¿Revocar la API key? Los servicios que la usen dejarán de funcionar.")) return;
      setRevokingApiKey(true);
      const response = await apiFetch("/settings/service-api-key", { method: "DELETE" });
      if (response.ok) {
        toast("API key revocada");
        setApiKeyMasked("");
      } else {
        toast(response.error || "Error al revocar", "err");
      }
      setRevokingApiKey(false);
    };

    const saveDateTime = async () => {
      setSavingDateTime(true);
      const body = {
        app_time_zone: dateTimeForm.app_time_zone,
        app_locale: dateTimeForm.app_locale,
      };
      const response = await apiFetch("/settings", { method: "PUT", body });
      if (response.ok) {
        setDateTimeConfig({
          time_zone: dateTimeForm.app_time_zone,
          locale: dateTimeForm.app_locale,
        });
        toast("Fecha y hora del sistema guardadas");
      } else {
        toast(response.error || "Error", "err");
      }
      setSavingDateTime(false);
    };

    const saveTestSync = async () => {
      setSavingTestSync(true);
      const body = {
        test_sync_enabled: testSyncForm.test_sync_enabled,
        test_sync_label: testSyncForm.test_sync_label,
        test_sync_host: testSyncForm.test_sync_host,
        test_sync_port: testSyncForm.test_sync_port,
        test_sync_username: testSyncForm.test_sync_username,
        test_sync_repo_path: testSyncForm.test_sync_repo_path,
        test_sync_branch: testSyncForm.test_sync_branch,
        test_sync_target_dir: testSyncForm.test_sync_target_dir,
        test_sync_base_url: testSyncForm.test_sync_base_url,
      };
      if (testSyncForm.test_sync_password) body.test_sync_password = testSyncForm.test_sync_password;

      const response = await apiFetch("/settings", { method: "PUT", body });
      if (response.ok) {
        toast("Ambiente de test guardado");
        const settings = await apiFetch("/settings");
        setTestSyncMasked(settings.test_sync_password_masked || "");
        setTestSyncForm((prev) => ({ ...prev, test_sync_password: "" }));
      } else {
        toast(response.error || "Error", "err");
      }
      setSavingTestSync(false);
    };

    const saveCentralDb = async () => {
      setSavingCentralDb(true);
      const body = {
        central_db_host: centralDbForm.central_db_host,
        central_db_ssh_port: centralDbForm.central_db_ssh_port,
        central_db_ssh_user: centralDbForm.central_db_ssh_user,
        central_db_user: centralDbForm.central_db_user,
        central_db_database: centralDbForm.central_db_database,
        central_db_table: centralDbForm.central_db_table,
      };
      if (centralDbForm.central_db_ssh_password) body.central_db_ssh_password = centralDbForm.central_db_ssh_password;
      if (centralDbForm.central_db_password) body.central_db_password = centralDbForm.central_db_password;

      const response = await apiFetch("/settings", { method: "PUT", body });
      if (response.ok) {
        toast("Registro central guardado");
        const settings = await apiFetch("/settings");
        setCentralDbMasked({
          ssh: settings.central_db_ssh_password_masked || "",
          db: settings.central_db_password_masked || "",
        });
        setCentralDbForm((prev) => ({ ...prev, central_db_ssh_password: "", central_db_password: "" }));
      } else {
        toast(response.error || "Error", "err");
      }
      setSavingCentralDb(false);
    };

    const saveClientDb = async () => {
      setSavingClientDb(true);
      const body = {
        client_db_user: clientDbForm.client_db_user,
        client_db_port: clientDbForm.client_db_port,
        client_db_driver: clientDbForm.client_db_driver,
      };
      if (clientDbForm.client_db_password) body.client_db_password = clientDbForm.client_db_password;

      const response = await apiFetch("/settings", { method: "PUT", body });
      if (response.ok) {
        toast("Credenciales BD de clientes guardadas");
        const settings = await apiFetch("/settings");
        setClientDbMasked(settings.client_db_password_masked || "");
        setClientDbForm((prev) => ({ ...prev, client_db_password: "" }));
      } else {
        toast(response.error || "Error", "err");
      }
      setSavingClientDb(false);
    };

    const saveUser = async () => {
      if (!userForm.username || (!editUser && !userForm.password)) {
        toast("Completa campos requeridos", "err");
        return;
      }

      const response = editUser
        ? await apiFetch(`/app-users/${editUser.id}`, { method: "PUT", body: userForm })
        : await apiFetch("/app-users", { method: "POST", body: userForm });

      if (response.ok || response.id) {
        toast(editUser ? "Actualizado" : "Creado");
        setShowUserForm(false);
        setEditUser(null);
        setUserForm({ username: "", password: "", full_name: "", role: "viewer" });
        reloadAppUsers();
      } else {
        toast(response.error || "Error", "err");
      }
    };

    const deleteUser = async (id) => {
      if (!confirm("¿Eliminar?")) return;
      await apiFetch(`/app-users/${id}`, { method: "DELETE" });
      toast("Eliminado");
      reloadAppUsers();
    };

    const changePassword = async () => {
      setCpError("");
      if (!cpForm.current || !cpForm.next) {
        setCpError("Completa todos los campos");
        return;
      }
      if (cpForm.next !== cpForm.confirm) {
        setCpError("Las contraseñas no coinciden");
        return;
      }
      if (cpForm.next.length < 6) {
        setCpError("Mínimo 6 caracteres");
        return;
      }
      const response = await apiFetch("/auth/change-password", {
        method: "POST",
        body: { current_password: cpForm.current, new_password: cpForm.next },
      });
      if (response.ok) {
        // El servidor rota los tokens al cambiar la clave (revoca otras sesiones).
        // Adoptamos el access token nuevo para que esta sesión siga vigente.
        if (response.token) localStorage.setItem("dc_token", response.token);
        toast("Contraseña cambiada · se cerraron otras sesiones");
        setCpForm({ current: "", next: "", confirm: "" });
      } else {
        setCpError(response.error || "Error");
      }
    };

    const tabs = isSuperAdmin
      ? [
          { key: "users", icon: "👥", label: "Usuarios" },
          { key: "access", icon: "🔑", label: "Accesos" },
          { key: "datetime", icon: "🕒", label: "Fecha y hora" },
          { key: "environments", icon: "🧪", label: "Ambientes" },
          { key: "alerts", icon: "🔔", label: "Alertas" },
          { key: "account", icon: "🔒", label: "Mi cuenta" },
        ]
      : [
          { key: "account", icon: "🔒", label: "Mi cuenta" },
        ];

    useEffect(() => {
      const fallbackTab = isSuperAdmin ? "users" : "account";
      if (!tabs.some((tab) => tab.key === activeTab)) {
        setActiveTab(fallbackTab);
      }
    }, [activeTab, isSuperAdmin, tabs]);

    useEffect(() => {
      try {
        window.localStorage.setItem("dc:tabs:settings", activeTab);
      } catch (_error) {}
    }, [activeTab]);

    return (
      <div style={{ animation: "fadeIn .3s ease", display: "flex", flexDirection: "column", gap: 14 }}>
        <div style={{ fontFamily: "Syne,sans-serif", fontWeight: 800, fontSize: 20, marginBottom: 4 }}>Configuración</div>

        <SectionTabs tabs={tabs} activeTab={activeTab} onChange={setActiveTab} />

        {isSuperAdmin && activeTab === "users" && (
          <SystemUsersTab
            appUsers={appUsers}
            currentUser={currentUser}
            showUserForm={showUserForm}
            setShowUserForm={setShowUserForm}
            editUser={editUser}
            setEditUser={setEditUser}
            userForm={userForm}
            setUserForm={setUserForm}
            saveUser={saveUser}
            deleteUser={deleteUser}
            reloadAppUsers={reloadAppUsers}
          />
        )}

        {isSuperAdmin && activeTab === "access" && (
          <>
            <GitHubSettingsCard
              ghToken={ghToken}
              setGhToken={setGhToken}
              ghRepo={ghRepo}
              setGhRepo={setGhRepo}
              ghBranch={ghBranch}
              setGhBranch={setGhBranch}
              ghMasked={ghMasked}
              saveGitHub={saveGitHub}
              savingGh={savingGh}
            />
            <SshSettingsCard
              sshPass={sshPass}
              setSshPass={setSshPass}
              sshMasked={sshMasked}
              saveSsh={saveSsh}
              savingSsh={savingSsh}
            />
            <MigrationHookSettingsCard
              migrationToken={migrationToken}
              setMigrationToken={setMigrationToken}
              migrationTokenConfigured={migrationTokenConfigured}
              saveMigrationToken={saveMigrationToken}
              savingMigration={savingMigration}
            />
            <ClaudeReviewSettingsCard
              claudeToken={claudeToken}
              setClaudeToken={setClaudeToken}
              claudeMasked={claudeMasked}
              claudeReviewEnabled={claudeReviewEnabled}
              setClaudeReviewEnabled={setClaudeReviewEnabled}
              saveClaudeReview={saveClaudeReview}
              savingClaude={savingClaude}
              testClaudeToken={testClaudeToken}
              testingClaude={testingClaude}
            />
            <ClientDbSettingsCard
              clientDbForm={clientDbForm}
              setClientDbForm={setClientDbForm}
              clientDbMasked={clientDbMasked}
              saveClientDb={saveClientDb}
              savingClientDb={savingClientDb}
            />
            <ServiceApiKeyCard
              apiKeyMasked={apiKeyMasked}
              generating={generatingApiKey}
              revoking={revokingApiKey}
              onGenerate={generateApiKey}
              onRevoke={revokeApiKey}
            />
          </>
        )}

        {isSuperAdmin && activeTab === "datetime" && (
          <DateTimeSettingsCard
            dateTimeForm={dateTimeForm}
            setDateTimeForm={setDateTimeForm}
            saveDateTime={saveDateTime}
            savingDateTime={savingDateTime}
          />
        )}

        {isSuperAdmin && activeTab === "environments" && (
          <>
            <TestSyncSettingsCard
              testSyncForm={testSyncForm}
              setTestSyncForm={setTestSyncForm}
              testSyncMasked={testSyncMasked}
              saveTestSync={saveTestSync}
              savingTestSync={savingTestSync}
            />
            <CentralDbSettingsCard
              centralDbForm={centralDbForm}
              setCentralDbForm={setCentralDbForm}
              centralDbMasked={centralDbMasked}
              saveCentralDb={saveCentralDb}
              savingCentralDb={savingCentralDb}
            />
          </>
        )}

        {isSuperAdmin && activeTab === "alerts" && <MonitorSettingsCard toast={toast} />}

        {activeTab === "account" && (
          <MyAccountCard
            cpForm={cpForm}
            setCpForm={setCpForm}
            cpError={cpError}
            changePassword={changePassword}
          />
        )}
      </div>
    );
  }

  window.DCViews.SettingsView = SettingsView;
})();
