/* global React */
// =======================================================================
// BlazeConnector Admin v3 — Control plane (live)
//   · TenantProviders  — editor REAL de provider configs (PUT /configs, merge)
//   · TenantHealthPanel — diagnóstico de completitud (GET /tenants/:id/health)
//   · PageServiceTokens — CRUD de service tokens (S2S: BlazeERP/BlazeOCR…)
// Los secretos NUNCA llegan en claro: getConfigs los enmascara como "__SET__".
// El editor manda secretos en blanco como "__SET__" → el backend preserva el
// valor actual (merge). Ver internal/transport/http/handlers/admin/configs.go.
// =======================================================================
const { useState: _cs, useEffect: _ce, useMemo: _cm } = React;

const MASK = '__SET__';

// Esquema de campos por provider (espejo de tenant.ProviderConfigs en Go).
// type: text | secret | bool | number | select.
const PROVIDER_SCHEMA = {
  wacloud:    { label: 'WhatsApp Cloud', group: 'Mensajería', icon: 'message-square', fields: [
    { key: 'phone_number_id', label: 'Phone Number ID', type: 'text' },
    { key: 'token', label: 'Access Token', type: 'secret' },
    { key: 'business_id', label: 'Business ID', type: 'text' },
    { key: 'verify_token', label: 'Verify Token (webhook)', type: 'secret' },
    { key: 'app_secret', label: 'App Secret (HMAC)', type: 'secret' } ] },
  telegram:   { label: 'Telegram', group: 'Mensajería', icon: 'send', fields: [
    { key: 'bot_token', label: 'Bot Token', type: 'secret' },
    { key: 'default_chat_id', label: 'Default Chat ID', type: 'text' },
    { key: 'secret_token', label: 'Secret Token (webhook header)', type: 'secret' } ] },
  chatwoot:   { label: 'Chatwoot', group: 'Mensajería', icon: 'messages-square', fields: [
    { key: 'api_url', label: 'API URL', type: 'text' },
    { key: 'token', label: 'API Token', type: 'secret' },
    { key: 'account_id', label: 'Account ID', type: 'text' },
    { key: 'hmac_secret', label: 'HMAC Secret', type: 'secret' } ] },
  mikrowisp:  { label: 'Mikrowisp', group: 'Billing / OSS', icon: 'router', fields: [
    { key: 'api_url', label: 'API URL', type: 'text' }, { key: 'token', label: 'Token', type: 'secret' } ] },
  wisphub:    { label: 'WispHub', group: 'Billing / OSS', icon: 'router', fields: [
    { key: 'api_url', label: 'API URL', type: 'text' }, { key: 'token', label: 'Token', type: 'secret' } ] },
  domiisp:    { label: 'DomiISP', group: 'Billing / OSS', icon: 'router', fields: [
    { key: 'api_url', label: 'API URL (api.php)', type: 'text' }, { key: 'token', label: 'X-API-Key', type: 'secret' } ] },
  smartolt:   { label: 'SmartOLT', group: 'Billing / OSS', icon: 'network', fields: [
    { key: 'api_url', label: 'API URL', type: 'text' }, { key: 'token', label: 'Token', type: 'secret' } ] },
  oficable:   { label: 'Oficable', group: 'Billing / OSS', icon: 'router', fields: [
    { key: 'api_url', label: 'API URL', type: 'text' },
    { key: 'api_espejo', label: 'API Espejo (opcional)', type: 'text' },
    { key: 'relay_enabled', label: 'Relay v3 activo', type: 'bool' },
    { key: 'relay_cutoff', label: 'Relay cutoff (YYYY-MM-DD)', type: 'text' } ] },
  voltage:    { label: 'Voltage', group: 'Billing / OSS', icon: 'zap', fields: [
    { key: 'low_threshold', label: 'Umbral bajo (V)', type: 'number' },
    { key: 'high_threshold', label: 'Umbral alto (V)', type: 'number' },
    { key: 'alert_phone', label: 'Teléfono alerta (E.164)', type: 'text' },
    { key: 'alert_chat_id', label: 'Telegram chat ID', type: 'text' },
    { key: 'min_interval_sec', label: 'Intervalo mínimo (s)', type: 'number' } ] },
  cardnet:    { label: 'CardNET', group: 'Pagos', icon: 'credit-card', fields: [
    { key: 'api_url', label: 'API URL', type: 'text' },
    { key: 'merchant_id', label: 'Merchant ID', type: 'text' },
    { key: 'terminal_id', label: 'Terminal ID', type: 'text' },
    { key: 'api_key', label: 'API Key', type: 'secret' } ] },
  azul:       { label: 'Azul', group: 'Pagos', icon: 'credit-card', fields: [
    { key: 'api_url', label: 'API URL', type: 'text' },
    { key: 'merchant_id', label: 'Merchant ID', type: 'text' },
    { key: 'auth1', label: 'Auth1', type: 'secret' },
    { key: 'auth2', label: 'Auth2', type: 'secret' } ] },
  paypal:     { label: 'PayPal', group: 'Pagos', icon: 'credit-card', fields: [
    { key: 'client_id', label: 'Client ID', type: 'text' },
    { key: 'client_secret', label: 'Client Secret', type: 'secret' },
    { key: 'webhook_id', label: 'Webhook ID', type: 'text' },
    { key: 'mode', label: 'Modo', type: 'select', options: ['sandbox', 'live'] } ] },
  cybersource: { label: 'CyberSource', group: 'Pagos', icon: 'credit-card', fields: [
    { key: 'merchant_id', label: 'Merchant ID', type: 'text' },
    { key: 'api_key', label: 'API Key (Shared Secret)', type: 'secret' },
    { key: 'secret_key', label: 'Secret Key (HTTP Sig)', type: 'secret' },
    { key: 'mode', label: 'Modo', type: 'select', options: ['test', 'live'] },
    { key: 'currency_default', label: 'Moneda default', type: 'text' } ] },
  blazeteams: { label: 'BlazeTeams', group: 'Otros', icon: 'users', fields: [
    { key: 'api_url', label: 'API URL', type: 'text' },
    { key: 'token', label: 'Token (bt_…)', type: 'secret' },
    { key: 'hmac_secret', label: 'HMAC Secret (webhook)', type: 'secret' } ] },
  monitoring: { label: 'Monitoring (NMS)', group: 'Otros', icon: 'activity', fields: [
    { key: 'inbound_token', label: 'Inbound Token (?token=)', type: 'secret' },
    { key: 'min_interval_sec', label: 'Anti-spam (s)', type: 'number' } ] }
};

const PROVIDER_GROUP_ORDER = ['Mensajería', 'Billing / OSS', 'Pagos', 'Otros'];

// ---- Editor modal de un provider -------------------------------------
function ProviderConfigModal({ tenant, provider, masked, onClose, onSaved }) {
  const schema = PROVIDER_SCHEMA[provider];
  const current = (masked && masked[provider]) || {};
  const [form, setForm] = _cs(() => {
    const f = {};
    schema.fields.forEach((fd) => {
      if (fd.type === 'secret') { f[fd.key] = ''; }                 // nunca pre-rellenamos secretos
      else if (fd.type === 'bool') { f[fd.key] = !!current[fd.key]; }
      else { f[fd.key] = current[fd.key] != null ? String(current[fd.key]) : ''; }
    });
    return f;
  });
  const [busy, setBusy] = _cs(false);
  const [err, setErr] = _cs(null);

  function setField(k, v) { setForm((p) => Object.assign({}, p, { [k]: v })); }

  async function submit() {
    setBusy(true); setErr(null);
    // Construye el sub-config del provider. Secretos en blanco → MASK (mantener).
    const out = {};
    schema.fields.forEach((fd) => {
      const v = form[fd.key];
      if (fd.type === 'secret') { out[fd.key] = v === '' ? MASK : v; }
      else if (fd.type === 'bool') { out[fd.key] = !!v; }
      else if (fd.type === 'number') { out[fd.key] = v === '' ? 0 : Number(v); }
      else { out[fd.key] = v; }
    });
    try {
      await window.BC.updateConfigs(tenant.uuid, { [provider]: out });
      onSaved && onSaved();
      onClose();
    } catch (e) { setErr(e.message); setBusy(false); }
  }

  return (
    <window.Modal title={'Configurar ' + schema.label} sub={'PUT /admin/tenants/:uuid/configs · merge seguro'} onClose={onClose}
      actions={<>
        <button className="btn btn-ghost" onClick={onClose} disabled={busy}>Cancelar</button>
        <button className="btn btn-primary" onClick={submit} disabled={busy}>{busy ? 'Guardando…' : 'Guardar'}</button>
      </>}>
      <div style={{ display: 'flex', flexDirection: 'column', gap: 12 }}>
        <div style={{ fontSize: 11.5, color: 'var(--app-fg3)', display: 'flex', gap: 8, alignItems: 'flex-start' }}>
          <window.Icon name="shield" size={14} style={{ color: 'var(--st-info-fg)', flex: '0 0 14px', marginTop: 1 }} />
          <span>Los secretos no se muestran. Deja un campo de secreto <strong>vacío</strong> para conservar el valor actual; escribe uno nuevo para reemplazarlo.</span>
        </div>
        {schema.fields.map((fd) => {
          const isSet = current[fd.key] === MASK || (fd.type !== 'secret' && current[fd.key] != null && current[fd.key] !== '');
          return (
            <label key={fd.key} style={{ display: 'flex', flexDirection: 'column', gap: 4 }}>
              <span style={{ fontSize: 11, fontWeight: 700, color: 'var(--app-fg2)', display: 'flex', alignItems: 'center', gap: 6 }}>
                {fd.label}
                {fd.type === 'secret' && (current[fd.key] === MASK
                  ? <span className="sb success sb-square" style={{ fontSize: 9 }}>configurado</span>
                  : <span className="sb neutral sb-square" style={{ fontSize: 9 }}>sin configurar</span>)}
              </span>
              {fd.type === 'bool'
                ? <button type="button" onClick={() => setField(fd.key, !form[fd.key])} className={'fb-chip' + (form[fd.key] ? ' is-active' : '')} style={{ alignSelf: 'flex-start' }}>{form[fd.key] ? 'Activado' : 'Desactivado'}</button>
                : fd.type === 'select'
                  ? <select value={form[fd.key]} onChange={(e) => setField(fd.key, e.target.value)} style={inputStyle}>
                      <option value="">—</option>
                      {fd.options.map((o) => <option key={o} value={o}>{o}</option>)}
                    </select>
                  : <input type={fd.type === 'secret' ? 'password' : fd.type === 'number' ? 'number' : 'text'}
                      value={form[fd.key]} onChange={(e) => setField(fd.key, e.target.value)}
                      placeholder={fd.type === 'secret' && isSet ? '•••••••• (dejar vacío para mantener)' : ''}
                      autoComplete="off" style={inputStyle} />}
            </label>
          );
        })}
        {err && <div style={errStyle}>{err}</div>}
      </div>
    </window.Modal>
  );
}

const inputStyle = { background: 'var(--app-input-bg)', border: '1px solid var(--app-border)', borderRadius: 6, padding: '8px 10px', color: 'var(--app-fg1)', fontFamily: 'inherit', fontSize: 13 };
const errStyle = { fontSize: 12, color: 'var(--st-danger-fg)', padding: '6px 8px', background: 'var(--st-danger-bg)', borderRadius: 6 };

// ---- Panel de salud (config completeness) ----------------------------
function TenantHealthPanel({ tenant }) {
  const bc = window.useBC();
  const slot = bc.healthByTenant[tenant.uuid] || { loading: true, data: null };
  const data = slot.data;
  if (!data) {
    return <window.SectionCard title="Salud de configuración" icon="stethoscope">
      {slot.loading ? <window.EmptyState icon="loader" title="Diagnosticando…" /> : <window.EmptyState icon="stethoscope" title="Sin datos" sub={slot.error || ''} />}
    </window.SectionCard>;
  }
  const tone = data.status === 'healthy' ? 'success' : 'warn';
  return (
    <window.SectionCard title="Salud de configuración" icon="stethoscope"
      sub={data.status === 'healthy' ? 'todos los módulos activos están completos' : (data.issues + ' módulo(s) con configuración incompleta')}
      actions={<window.StatusBadge tone={tone} label={data.status === 'healthy' ? 'healthy' : 'incompleto'} />}>
      {(!data.checks || data.checks.length === 0)
        ? <window.EmptyState icon="check" title="Sin módulos activos" sub="Activa módulos en la pestaña Módulos para verlos aquí." />
        : <div style={{ display: 'flex', flexDirection: 'column', gap: 6 }}>
            {data.checks.map((ch) => (
              <div key={ch.module} style={{ display: 'flex', alignItems: 'center', gap: 10, padding: '7px 0', borderBottom: '1px solid var(--app-divider)' }}>
                <window.Icon name={ch.ok ? 'check-circle-2' : 'alert-triangle'} size={15} style={{ color: ch.ok ? 'var(--st-success-fg)' : 'var(--st-warn-fg)' }} />
                <span style={{ fontWeight: 600, fontSize: 13, minWidth: 120 }}>{(PROVIDER_SCHEMA[ch.module] || {}).label || ch.module}</span>
                <span style={{ fontSize: 11.5, color: 'var(--app-fg3)', flex: 1 }}>
                  {ch.ok ? (ch.note || 'completo') : (ch.note || ('faltan: ' + (ch.missing || []).join(', ')))}
                </span>
                {!ch.enabled && ch.configured && <span className="sb neutral sb-square" style={{ fontSize: 9.5 }}>módulo off</span>}
              </div>
            ))}
          </div>}
    </window.SectionCard>
  );
}

// ---- Tab Providers (editor real, modo live) --------------------------
function TenantProviders({ tenant }) {
  const bc = window.useBC();
  const live = tenant._live === true;
  const [editing, setEditing] = _cs(null);

  _ce(() => {
    if (!live) return;
    if (!bc.configsByTenant[tenant.uuid]) window.BC.getConfigs(tenant.uuid).catch(() => {});
    window.BC.loadTenantHealth(tenant.uuid).catch(() => {});
  }, [live, tenant.uuid]);

  if (!live) {
    return <window.LiveNotice icon="plug" title="Edición de credenciales: solo en modo live">
      Conéctate al backend real (botón de conexión) para ver y editar las credenciales de proveedores de este tenant.
      En modo demo solo se muestran datos de ejemplo.
    </window.LiveNotice>;
  }

  const slot = bc.configsByTenant[tenant.uuid] || { loading: true, data: {}, present: {} };
  const present = slot.present || {};
  const masked = slot.data || {};

  // Agrupa providers por sección para el render.
  const byGroup = {};
  Object.keys(PROVIDER_SCHEMA).forEach((p) => {
    const g = PROVIDER_SCHEMA[p].group;
    (byGroup[g] = byGroup[g] || []).push(p);
  });

  return (
    <div style={{ display: 'flex', flexDirection: 'column', gap: 12 }}>
      <TenantHealthPanel tenant={tenant} />
      <window.LiveErrorBanner error={slot.error} onRetry={() => window.BC.getConfigs(tenant.uuid)} />

      {PROVIDER_GROUP_ORDER.map((g) => (
        <window.SectionCard key={g} title={g} icon={g === 'Pagos' ? 'credit-card' : g === 'Mensajería' ? 'message-square' : g === 'Otros' ? 'boxes' : 'router'} flush>
          <div className="grid-3" style={{ padding: 12 }}>
            {(byGroup[g] || []).map((p) => {
              const sch = PROVIDER_SCHEMA[p];
              const on = !!present[p];
              return (
                <div key={p} className="card" style={{ padding: 12, display: 'flex', flexDirection: 'column', gap: 8 }}>
                  <div style={{ display: 'flex', alignItems: 'center', gap: 10 }}>
                    <span style={{ width: 30, height: 30, borderRadius: 7, background: on ? 'rgba(46,204,90,0.14)' : 'var(--app-panel-3)', color: on ? 'var(--st-success-fg)' : 'var(--app-fg3)', display: 'grid', placeItems: 'center' }}>
                      <window.Icon name={sch.icon} size={15} />
                    </span>
                    <div style={{ flex: 1, minWidth: 0 }}>
                      <div style={{ fontWeight: 700, fontSize: 13 }}>{sch.label}</div>
                      <div style={{ fontSize: 10.5, color: 'var(--app-fg3)' }}>{on ? 'configurado' : 'sin configurar'}</div>
                    </div>
                    {on
                      ? <window.StatusBadge tone="success" label="set" square />
                      : <window.StatusBadge tone="neutral" label="—" square />}
                  </div>
                  <button className="btn btn-secondary btn-sm" onClick={() => setEditing(p)} disabled={slot.loading}>
                    <window.Icon name="settings-2" size={11} /> {on ? 'Editar' : 'Configurar'}
                  </button>
                </div>
              );
            })}
          </div>
        </window.SectionCard>
      ))}

      {editing && <ProviderConfigModal tenant={tenant} provider={editing} masked={masked}
        onClose={() => setEditing(null)}
        onSaved={() => window.BC.loadTenantHealth(tenant.uuid).catch(() => {})} />}
    </div>
  );
}

// =======================================================================
// Service Tokens (control plane S2S)
// =======================================================================
const ST_SCOPE_OPTIONS = ['tenants:read', 'events:read', 'events:read:global', 'webhooks:read', 'webhooks:write', 'audit:read'];

function PageServiceTokens({ role }) {
  const bc = window.useBC();
  const live = bc.mode === 'live';
  const [issuing, setIssuing] = _cs(false);
  const [raw, setRaw] = _cs(null);
  const [revoking, setRevoking] = _cs(null);
  const [search, setSearch] = _cs('');
  const canManage = window.hasPerm(role, 'apikey:create');

  _ce(() => { if (live) window.BC.loadServiceTokens({ include_revoked: true }).catch(() => {}); }, [live]);

  if (!live) {
    return <div className="page">
      <div className="page-header"><div><h1 className="page-title">Service Tokens</h1>
        <p className="page-sub">Credenciales S2S para sistemas internos (BlazeERP, BlazeOCR…)</p></div></div>
      <window.LiveNotice icon="plug" title="Service tokens: solo en modo live">
        Conéctate al backend real para emitir, listar y revocar service tokens del control plane.
      </window.LiveNotice>
    </div>;
  }

  const slot = bc.serviceTokens || { loading: true, data: [] };
  const rows = (slot.data || []).filter((s) => {
    const ql = search.toLowerCase();
    return !ql || (s.name + ' ' + s.serviceCode + ' ' + s.scopes.join(' ')).toLowerCase().includes(ql);
  });

  return (
    <div className="page">
      <div className="page-header">
        <div>
          <h1 className="page-title">Service Tokens <window.LiveTag /></h1>
          <p className="page-sub">{rows.length} tokens · {rows.filter((s) => s.status === 'active').length} activos · control plane S2S</p>
        </div>
        <div className="page-actions">
          {canManage && <button className="btn btn-primary btn-sm" onClick={() => setIssuing(true)}><window.Icon name="plus" size={12} /> Emitir token</button>}
        </div>
      </div>

      <div className="card" style={{ marginBottom: 12, background: 'var(--st-info-bg)', borderColor: 'rgba(30,123,255,0.3)' }}>
        <div className="card-body" style={{ display: 'flex', alignItems: 'center', gap: 12 }}>
          <window.Icon name="key-square" size={16} style={{ color: 'var(--st-info-fg)' }} />
          <div style={{ flex: 1, fontSize: 12.5 }}>
            <strong style={{ color: 'var(--app-fg1)' }}>El secreto (bks_…) solo se muestra una vez al emitirlo.</strong>
            <span style={{ color: 'var(--app-fg2)', marginLeft: 6 }}>Estos tokens dan acceso al <code>/service/v1/*</code> de uno o más tenants — guárdalos en un gestor de secretos.</span>
          </div>
        </div>
      </div>

      <div className="card">
        <window.FilterBar>
          <window.FilterSearch value={search} onChange={setSearch} placeholder="Buscar por nombre, service_code, scope…" />
        </window.FilterBar>
        <window.LiveErrorBanner error={slot.error} onRetry={() => window.BC.loadServiceTokens({ include_revoked: true })} />
        {slot.loading ? <window.EmptyState icon="loader" title="Cargando tokens…" />
          : rows.length === 0 ? <window.EmptyState icon="key-square" title="Sin service tokens" sub="Emite uno para que BlazeERP/BlazeOCR consuman el control plane." />
          : <window.DataTable density="cozy" columns={[
              { label: 'Token', render: (s) => <div><div style={{ fontWeight: 600, fontSize: 12.5 }}>{s.name}</div><div className="mono" style={{ fontSize: 10.5, color: 'var(--app-fg3)' }}>{s.preview}</div></div> },
              { label: 'Service', width: 140, render: (s) => <span className="mono" style={{ fontSize: 11.5, color: 'var(--st-violet-fg)' }}>{s.serviceCode}</span> },
              { label: 'Scopes', render: (s) => <div style={{ display: 'flex', gap: 4, flexWrap: 'wrap' }}>{s.scopes.map((x) => <span key={x} className="sb neutral sb-square mono" style={{ fontSize: 10 }}>{x}</span>)}</div> },
              { label: 'Tenants', width: 90, numeric: true, render: (s) => <span className="mono">{s.allowedTenants && s.allowedTenants.length ? s.allowedTenants.length : '∞'}</span> },
              { label: 'Estado', width: 90, render: (s) => <window.StatusBadge status={s.status} /> },
              { label: '', width: 90, render: (s) => s.status === 'active' ? <div style={{ display: 'flex', justifyContent: 'flex-end' }}><button className="btn btn-danger btn-sm" onClick={() => setRevoking(s)}>Revocar</button></div> : null }
            ]} rows={rows} />}
      </div>

      {issuing && <IssueServiceTokenModal onClose={() => setIssuing(false)} onIssued={(d) => { setIssuing(false); setRaw(d); }} />}
      {raw && <RawServiceTokenModal data={raw} onClose={() => setRaw(null)} />}
      {revoking && <window.ConfirmDialog title="Revocar service token" danger confirmLabel="Revocar"
        message={'"' + revoking.name + '" dejará de funcionar de inmediato para todos los sistemas que lo usen.'}
        onClose={() => setRevoking(null)} onConfirm={() => window.BC.revokeServiceToken(revoking.id).catch(() => {})} />}
    </div>
  );
}

function IssueServiceTokenModal({ onClose, onIssued }) {
  const bc = window.useBC();
  const [name, setName] = _cs('');
  const [serviceCode, setServiceCode] = _cs('');
  const [scopes, setScopes] = _cs(['tenants:read']);
  const [allowed, setAllowed] = _cs([]); // uuids; vacío = todos
  const [busy, setBusy] = _cs(false);
  const [err, setErr] = _cs(null);
  function toggleScope(s) { setScopes((p) => p.includes(s) ? p.filter((x) => x !== s) : [...p, s]); }
  function toggleTenant(uuid) { setAllowed((p) => p.includes(uuid) ? p.filter((x) => x !== uuid) : [...p, uuid]); }
  async function submit() {
    if (!name.trim() || !serviceCode.trim() || scopes.length === 0) { setErr('Nombre, service_code y al menos un scope son obligatorios'); return; }
    setBusy(true); setErr(null);
    try {
      const d = await window.BC.issueServiceToken({ name: name.trim(), service_code: serviceCode.trim(), scopes, allowed_tenants: allowed });
      onIssued(d);
    } catch (e) { setErr(e.message); setBusy(false); }
  }
  return (
    <window.Modal title="Emitir service token" sub="POST /admin/service-tokens · bks_<prefix>_<secret>" onClose={onClose}
      actions={<><button className="btn btn-ghost" onClick={onClose} disabled={busy}>Cancelar</button>
        <button className="btn btn-primary" onClick={submit} disabled={busy || !name.trim()}>{busy ? 'Emitiendo…' : 'Emitir token'}</button></>}>
      <div style={{ display: 'flex', flexDirection: 'column', gap: 12 }}>
        <label style={{ display: 'flex', flexDirection: 'column', gap: 4 }}>
          <span style={{ fontSize: 11, fontWeight: 700, color: 'var(--app-fg2)' }}>Nombre</span>
          <input value={name} onChange={(e) => setName(e.target.value)} autoFocus placeholder="ej. BlazeERP · prod" style={inputStyle} />
        </label>
        <label style={{ display: 'flex', flexDirection: 'column', gap: 4 }}>
          <span style={{ fontSize: 11, fontWeight: 700, color: 'var(--app-fg2)' }}>Service code</span>
          <input value={serviceCode} onChange={(e) => setServiceCode(e.target.value)} placeholder="ej. blaze-erp" style={inputStyle} />
        </label>
        <div>
          <div style={{ fontSize: 11, fontWeight: 700, color: 'var(--app-fg2)', marginBottom: 6 }}>Scopes <span style={{ color: 'var(--app-fg3)', fontWeight: 400 }}>({scopes.length})</span></div>
          <div style={{ display: 'flex', gap: 6, flexWrap: 'wrap' }}>
            {ST_SCOPE_OPTIONS.map((s) => <button key={s} className={'fb-chip' + (scopes.includes(s) ? ' is-active' : '')} onClick={() => toggleScope(s)}>{s}</button>)}
          </div>
        </div>
        <div>
          <div style={{ fontSize: 11, fontWeight: 700, color: 'var(--app-fg2)', marginBottom: 6 }}>Tenants permitidos <span style={{ color: 'var(--app-fg3)', fontWeight: 400 }}>({allowed.length === 0 ? 'todos' : allowed.length})</span></div>
          <div style={{ display: 'flex', gap: 6, flexWrap: 'wrap', maxHeight: 120, overflow: 'auto' }}>
            {(bc.tenants || []).map((t) => <button key={t.uuid} className={'fb-chip' + (allowed.includes(t.uuid) ? ' is-active' : '')} onClick={() => toggleTenant(t.uuid)}>{t.name}</button>)}
          </div>
          <div style={{ fontSize: 10.5, color: 'var(--app-fg3)', marginTop: 4 }}>Vacío = acceso a todos los tenants.</div>
        </div>
        {err && <div style={errStyle}>{err}</div>}
      </div>
    </window.Modal>
  );
}

function RawServiceTokenModal({ data, onClose }) {
  const raw = data.raw_token || data.raw || '';
  return (
    <window.Modal title="Tu nuevo service token" sub="⚠️ Única vez que verás el secreto completo. Cópialo ahora." onClose={onClose}
      actions={<><button className="btn btn-ghost" onClick={() => navigator.clipboard && navigator.clipboard.writeText(raw)}><window.Icon name="copy" size={12} /> Copiar</button>
        <button className="btn btn-primary" onClick={onClose}>Listo, lo guardé</button></>}>
      <div style={{ display: 'flex', flexDirection: 'column', gap: 12 }}>
        <div style={{ padding: 14, background: 'var(--st-warn-bg)', border: '1px solid rgba(242,169,0,0.3)', borderRadius: 8, display: 'flex', gap: 10 }}>
          <window.Icon name="triangle-alert" size={16} style={{ color: 'var(--st-warn-fg)', flex: '0 0 16px' }} />
          <div style={{ fontSize: 12, color: 'var(--app-fg1)' }}><strong>No podemos recuperar este secreto luego.</strong> Guárdalo en tu gestor de secretos.</div>
        </div>
        <div>
          <div style={{ fontSize: 11, fontWeight: 700, color: 'var(--app-fg2)', marginBottom: 6 }}>Token</div>
          <div style={{ display: 'flex', gap: 6 }}>
            <input readOnly value={raw} style={Object.assign({}, inputStyle, { flex: 1, borderColor: 'var(--blaze-orange)', fontFamily: 'var(--font-mono)', fontSize: 12 })} />
            <button className="btn btn-secondary" onClick={() => navigator.clipboard && navigator.clipboard.writeText(raw)}><window.Icon name="copy" size={12} /> Copiar</button>
          </div>
        </div>
        <dl className="kv">
          <dt>Nombre</dt><dd className="kv-text">{data.name}</dd>
          <dt>Service</dt><dd className="mono">{data.service_code}</dd>
          <dt>Scopes</dt><dd>{(data.scopes || []).join(', ')}</dd>
        </dl>
      </div>
    </window.Modal>
  );
}

Object.assign(window, { TenantProviders, TenantHealthPanel, PageServiceTokens, PROVIDER_SCHEMA });
