// Admin views — one component per tab. Data is loaded on mount; each view
// owns its own loading state. Shell handles tab switching.

function useLoader(loader, deps) {
  const [data, setData] = React.useState(null);
  const [loading, setLoading] = React.useState(true);
  const [error, setError] = React.useState(null);
  const [reloadKey, setReloadKey] = React.useState(0);
  React.useEffect(() => {
    let cancelled = false;
    setLoading(true); setError(null);
    Promise.resolve()
      .then(() => loader())
      .then((d) => { if (!cancelled) { setData(d); setLoading(false); } })
      .catch((e) => { if (!cancelled) { setError(e.message || String(e)); setLoading(false); } });
    return () => { cancelled = true; };
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [...(deps || []), reloadKey]);
  return { data, loading, error, reload: () => setReloadKey((k) => k + 1) };
}
window.useLoader = useLoader;

// ── Overview ────────────────────────────────────────────────────────────
function OverviewView() {
  const { data, loading, error, reload } = useLoader(() => API.get('/api/admin/overview'));
  if (loading) return <div className="empty"><Spinner /> Cargando overview…</div>;
  if (error) return <div className="empty">Error: {error}</div>;
  const c = data.counts, g = data.growth, s = data.sparkline;

  const KPI = ({ title, value, foot, spark, color }) => (
    <Card title={title} value={F.num(value)} foot={foot}>
      {spark && <Sparkline data={spark} color={color} />}
    </Card>
  );

  return (
    <div className="col" style={{ gap: 22 }}>
      <div className="cards">
        <KPI title="Usuarios" value={c.users} foot={<>+{F.num(g.newUsersLast7d)} esta semana · {F.num(c.usersVerified)} verificados</>} spark={s.signups} color="#60A5FA" />
        <KPI title="Workspaces" value={c.workspaces} foot={<>{F.num(c.projects)} proyectos · {F.num(c.projectsActive)} activos</>} />
        <KPI title="Documentos" value={c.documents} foot={<>{F.num(c.documentVersions)} versiones · +{g.newDocsLast7d} esta semana</>} spark={s.docs} color="#A78BFA" />
        <KPI title="Tasks" value={c.tasks} foot={<>+{g.newTasksLast7d} esta semana</>} spark={s.tasks} color="#34D399" />
      </div>
      <div className="cards">
        <KPI title="Comments" value={c.comments} foot={<>+{g.commentsLast7d} esta semana</>} />
        <KPI title="Actividad" value={c.activity} foot="eventos en la bitácora" spark={s.activity} color="#FBBF24" />
        <KPI title="MCP tokens activos" value={c.oauthTokensActive} foot={c.oauthTokensActive === 0 ? 'nadie conectado todavía' : 'tokens OAuth vigentes'} />
        <KPI title="Sesiones de Claude Code" value={c.codeSessions} foot={<>{g.inflightCodeTurns} turno{g.inflightCodeTurns === 1 ? '' : 's'} activo{g.inflightCodeTurns === 1 ? '' : 's'}</>} />
      </div>
      <div className="cards cards-3">
        <Card title="Activos últimos 7d" value={F.num(g.activeUsersLast7d)} foot="actores con presencia reciente" />
        <Card title="VPS registrados" value={F.num(c.vpsAgents)} foot="agentes Claude Code remotos" />
        <Card title="Pagos / hitos" value={F.num(c.paymentMilestones)} foot="milestones registrados" />
      </div>
      <div className="row" style={{ justifyContent: 'flex-end' }}>
        <button className="btn ghost" onClick={reload}><Icon name="refresh-ccw" size={13} /> Refrescar</button>
        <span className="muted" style={{ fontSize: 11 }}>Actualizado {F.ago(data.generatedAt)}</span>
      </div>
    </div>
  );
}
window.OverviewView = OverviewView;

// ── Users ───────────────────────────────────────────────────────────────
function UsersView() {
  const [filters, setFilters] = React.useState({ verified: 'all', hasMcp: 'all', q: '' });
  const [draftQ, setDraftQ] = React.useState('');
  const [selected, setSelected] = React.useState(null);
  const { data, loading, error, reload } = useLoader(
    () => API.get('/api/admin/users', { ...filters, limit: 200 }),
    [JSON.stringify(filters)]
  );
  React.useEffect(() => {
    const t = setTimeout(() => setFilters((f) => ({ ...f, q: draftQ })), 300);
    return () => clearTimeout(t);
  }, [draftQ]);

  return (
    <div className="col" style={{ gap: 14 }}>
      <div className="row gap-12 wrap">
        <SearchBar value={draftQ} onChange={setDraftQ} placeholder="Buscar por email o nombre…" />
        <select className="select" style={{ width: 180 }} value={filters.verified} onChange={(e) => setFilters({ ...filters, verified: e.target.value })}>
          <option value="all">Todos (verificación)</option>
          <option value="yes">Verificados</option>
          <option value="no">Sin verificar</option>
        </select>
        <select className="select" style={{ width: 200 }} value={filters.hasMcp} onChange={(e) => setFilters({ ...filters, hasMcp: e.target.value })}>
          <option value="all">Todos (MCP)</option>
          <option value="yes">Con MCP / Claude.ai</option>
          <option value="no">Sin MCP</option>
        </select>
        <button className="btn ghost" onClick={reload}><Icon name="refresh-ccw" size={13} /></button>
      </div>
      {loading && <div className="empty"><Spinner /> Cargando usuarios…</div>}
      {error && <div className="empty">Error: {error}</div>}
      {data && (
        <div className="tbl-wrap">
          <table className="tbl">
            <thead>
              <tr>
                <th>Email</th><th>Nombre</th><th>Verificado</th><th>MCP</th>
                <th>Workspaces</th><th>Signup</th><th>Último login</th>
              </tr>
            </thead>
            <tbody>
              {data.rows.map((u) => (
                <tr key={u.id} className="tbl-row-clickable" onClick={() => setSelected(u.id)}>
                  <td className="mono">{u.email}</td>
                  <td>{u.displayName}</td>
                  <td><VerifiedPill verified={u.verified} /></td>
                  <td><McpPill on={u.mcpConnected} lastUsedAt={u.lastMcpUseAt} /></td>
                  <td>
                    {u.ownedWorkspaces.length > 0 && <Badge kind="violet">owner: {u.ownedWorkspaces.map((w) => w.name).join(', ')}</Badge>}
                    {u.memberOfWorkspaces.length > 0 && <Badge>{u.memberOfWorkspaces.length} miembro</Badge>}
                  </td>
                  <td>{F.date(u.signupAt)}</td>
                  <td>{F.ago(u.lastSeenAt)}</td>
                </tr>
              ))}
              {data.rows.length === 0 && <tr><td colSpan={7} className="muted" style={{ padding: 20 }}>Sin resultados.</td></tr>}
            </tbody>
          </table>
        </div>
      )}
      {data && <div className="muted" style={{ fontSize: 11 }}>{data.rows.length} de {data.total}</div>}
      {selected && <UserDetailDrawer userId={selected} onClose={() => setSelected(null)} />}
    </div>
  );
}
window.UsersView = UsersView;

function UserDetailDrawer({ userId, onClose }) {
  const { data, loading, error } = useLoader(() => API.get('/api/admin/users/' + userId), [userId]);
  const u = data?.user;
  return (
    <Drawer onClose={onClose} title={u?.displayName || 'Usuario'}>
      {loading && <Spinner />}
      {error && <div className="empty">Error: {error}</div>}
      {data && (
        <div className="col" style={{ gap: 14 }}>
          <div className="kv">
            <div className="k">Email</div><div className="v mono">{u.email}</div>
            <div className="k">Nombre</div><div className="v">{u.displayName}</div>
            <div className="k">Verificado</div><div className="v"><VerifiedPill verified={u.verified} /> {u.verifiedAt && <span className="muted">· {F.dateTime(u.verifiedAt)}</span>}</div>
            <div className="k">Signup</div><div className="v">{F.dateTime(u.signupAt)}</div>
            <div className="k">Último login</div><div className="v">{F.ago(u.lastSeenAt)}</div>
            <div className="k">Timezone</div><div className="v">{u.timezone}</div>
            <div className="k">Actor ID</div><div className="v mono">{u.legacyActorId}</div>
          </div>

          <h4>Contribuciones</h4>
          <div className="cards cards-3">
            <Card title="Documentos" value={F.num(data.contributions.documents)} />
            <Card title="Tasks asignadas" value={F.num(data.contributions.tasksAssigned)} />
            <Card title="Comments" value={F.num(data.contributions.comments)} />
          </div>

          <h4>Workspaces</h4>
          {data.workspaces.owned.length === 0 && data.workspaces.member.length === 0 && <div className="muted">Ninguno.</div>}
          <div className="col">
            {data.workspaces.owned.map((w) => (
              <div key={w.id} className="row spread" style={{ padding: '6px 0', borderBottom: '1px solid var(--line-soft)' }}>
                <span><Badge kind="violet">owner</Badge> {w.name}</span>
                <span className="muted">{F.date(w.createdAt)}</span>
              </div>
            ))}
            {data.workspaces.member.map((w) => (
              <div key={w.workspaceId} className="row spread" style={{ padding: '6px 0', borderBottom: '1px solid var(--line-soft)' }}>
                <span><Badge>{w.roleName}</Badge> {w.workspaceName}</span>
                <span className="muted">desde {F.date(w.joinedAt)}</span>
              </div>
            ))}
          </div>

          <h4>OAuth tokens (MCP)</h4>
          {data.oauthTokens.length === 0 && <div className="muted">Sin tokens.</div>}
          {data.oauthTokens.map((t) => (
            <div key={t.id} className="row spread" style={{ padding: '6px 0', borderBottom: '1px solid var(--line-soft)' }}>
              <span>{t.clientName}{t.scope && <span className="muted"> · {t.scope}</span>}</span>
              <span>{t.expired ? <Badge kind="bad">expirado</Badge> : <Badge kind="ok">vigente</Badge>} <span className="muted">{F.ago(t.lastUsedAt || t.createdAt)}</span></span>
            </div>
          ))}

          <h4>Sesiones de Claude Code</h4>
          {data.codeSessions.length === 0 && <div className="muted">Sin sesiones.</div>}
          {data.codeSessions.map((s) => (
            <div key={s.id} className="col" style={{ padding: '6px 0', borderBottom: '1px solid var(--line-soft)' }}>
              <div className="row spread"><span>{s.title}</span><Badge kind={s.status === 'running' ? 'info' : s.status === 'errored' ? 'bad' : ''}>{s.status}</Badge></div>
              <div className="muted mono" style={{ fontSize: 11 }}>{s.workingDir}</div>
              <div className="muted" style={{ fontSize: 11 }}>{F.dateTime(s.startedAt)} · último mensaje {F.ago(s.lastMessageAt)}</div>
            </div>
          ))}

          <h4>Actividad reciente</h4>
          {data.recentActivity.length === 0 && <div className="muted">Sin actividad.</div>}
          {data.recentActivity.slice(0, 10).map((a) => (
            <div key={a.id} className="row spread" style={{ padding: '4px 0', fontSize: 12 }}>
              <span><Badge>{a.entityType}</Badge> {a.action} · <span className="muted mono">{a.entityId}</span></span>
              <span className="muted">{F.ago(a.createdAt)}</span>
            </div>
          ))}
        </div>
      )}
    </Drawer>
  );
}

// ── Workspaces ──────────────────────────────────────────────────────────
function WorkspacesView() {
  const { data, loading, error, reload } = useLoader(() => API.get('/api/admin/workspaces'));
  const [selected, setSelected] = React.useState(null);
  return (
    <div className="col" style={{ gap: 14 }}>
      <div className="row spread">
        <div className="muted">{data?.rows.length ?? '—'} workspaces</div>
        <button className="btn ghost" onClick={reload}><Icon name="refresh-ccw" size={13} /></button>
      </div>
      {loading && <div className="empty"><Spinner /> Cargando…</div>}
      {error && <div className="empty">Error: {error}</div>}
      {data && (
        <div className="tbl-wrap">
          <table className="tbl">
            <thead><tr>
              <th>Nombre</th><th>Owner</th><th>Miembros</th><th>Proyectos</th>
              <th>Docs</th><th>Tasks</th><th>MCP</th><th>Última actividad</th><th>Creado</th>
            </tr></thead>
            <tbody>
              {data.rows.map((w) => (
                <tr key={w.id} className="tbl-row-clickable" onClick={() => setSelected(w.id)}>
                  <td>{w.name}</td>
                  <td className="mono">{w.ownerEmail}</td>
                  <td className="num">{w.memberCount}</td>
                  <td className="num">{w.projectCount}</td>
                  <td className="num">{w.docCount}</td>
                  <td className="num">{w.taskCount}</td>
                  <td className="num">{w.oauthTokenCount}</td>
                  <td>{F.ago(w.lastActivityAt)}</td>
                  <td>{F.date(w.createdAt)}</td>
                </tr>
              ))}
            </tbody>
          </table>
        </div>
      )}
      {selected && <WorkspaceDetailDrawer workspaceId={selected} onClose={() => setSelected(null)} />}
    </div>
  );
}
window.WorkspacesView = WorkspacesView;

function WorkspaceDetailDrawer({ workspaceId, onClose }) {
  const { data, loading, error } = useLoader(() => API.get('/api/admin/workspaces/' + workspaceId), [workspaceId]);
  return (
    <Drawer onClose={onClose} title={data?.workspace.name || 'Workspace'}>
      {loading && <Spinner />}
      {error && <div className="empty">Error: {error}</div>}
      {data && (
        <div className="col" style={{ gap: 14 }}>
          <div className="kv">
            <div className="k">Slug</div><div className="v mono">{data.workspace.slug}</div>
            <div className="k">Owner</div><div className="v">{data.workspace.ownerName} <span className="muted mono">{data.workspace.ownerEmail}</span></div>
            <div className="k">Tier</div><div className="v"><Badge kind="violet">{data.workspace.tier}</Badge></div>
            <div className="k">Creado</div><div className="v">{F.dateTime(data.workspace.createdAt)}</div>
            <div className="k">Última actividad</div><div className="v">{F.ago(data.counts.lastActivityAt)}</div>
          </div>
          <div className="cards cards-3">
            <Card title="Documentos" value={F.num(data.counts.documents)} />
            <Card title="Tasks" value={F.num(data.counts.tasks)} />
            <Card title="Comments" value={F.num(data.counts.comments)} />
          </div>
          <h4>Miembros ({data.members.length})</h4>
          <div className="tbl-wrap" style={{ maxHeight: 220 }}>
            <table className="tbl">
              <thead><tr><th>Email</th><th>Nombre</th><th>Rol</th><th>Joined</th></tr></thead>
              <tbody>
                {data.members.map((m) => (
                  <tr key={m.userId}>
                    <td className="mono">{m.email}</td>
                    <td>{m.displayName}</td>
                    <td><Badge>{m.roleName}</Badge></td>
                    <td>{F.date(m.joinedAt)}</td>
                  </tr>
                ))}
              </tbody>
            </table>
          </div>
          <h4>Proyectos ({data.projects.length})</h4>
          <div className="tbl-wrap" style={{ maxHeight: 220 }}>
            <table className="tbl">
              <thead><tr><th>Nombre</th><th>Status</th><th>RootDir</th><th>Creado</th></tr></thead>
              <tbody>
                {data.projects.map((p) => (
                  <tr key={p.id}>
                    <td>{p.name}</td>
                    <td><Badge kind={p.status === 'active' ? 'ok' : ''}>{p.status}</Badge></td>
                    <td className="mono softer">{p.rootDir}</td>
                    <td>{F.date(p.createdAt)}</td>
                  </tr>
                ))}
              </tbody>
            </table>
          </div>
          <h4>OAuth tokens ({data.oauthTokens.length})</h4>
          {data.oauthTokens.length === 0 ? <div className="muted">Sin tokens.</div> : (
            <div className="col">
              {data.oauthTokens.map((t) => (
                <div key={t.id} className="row spread" style={{ padding: '4px 0', borderBottom: '1px solid var(--line-soft)' }}>
                  <span>{t.clientName} <span className="muted">· {t.userName || t.userEmail}</span></span>
                  <span>{t.expired ? <Badge kind="bad">expirado</Badge> : <Badge kind="ok">vigente</Badge>} <span className="muted">{F.ago(t.lastUsedAt)}</span></span>
                </div>
              ))}
            </div>
          )}
        </div>
      )}
    </Drawer>
  );
}

// ── Projects ────────────────────────────────────────────────────────────
function ProjectsView() {
  const { data, loading, error, reload } = useLoader(() => API.get('/api/admin/projects'));
  return (
    <div className="col" style={{ gap: 14 }}>
      <div className="row spread">
        <div className="muted">{data?.rows.length ?? '—'} proyectos</div>
        <button className="btn ghost" onClick={reload}><Icon name="refresh-ccw" size={13} /></button>
      </div>
      {loading && <div className="empty"><Spinner /> Cargando…</div>}
      {error && <div className="empty">Error: {error}</div>}
      {data && (
        <div className="tbl-wrap">
          <table className="tbl">
            <thead><tr>
              <th>Nombre</th><th>Workspace</th><th>Owner</th><th>Status</th>
              <th>Miembros</th><th>Docs</th><th>Tasks</th><th>Code sessions</th>
              <th>VPS</th><th>Última actividad</th><th>Creado</th>
            </tr></thead>
            <tbody>
              {data.rows.map((p) => (
                <tr key={p.id}>
                  <td>{p.name} <span className="muted mono">{p.id}</span></td>
                  <td>{p.workspaceName || <span className="muted">—</span>}</td>
                  <td className="mono softer">{p.ownerEmail || '—'}</td>
                  <td><Badge kind={p.status === 'active' ? 'ok' : ''}>{p.status}</Badge></td>
                  <td className="num">{p.memberCount}</td>
                  <td className="num">{p.docCount}</td>
                  <td className="num">{p.taskCount}</td>
                  <td className="num">{p.codeSessionCount}</td>
                  <td>{p.vpsName ? <Badge kind={p.vpsStatus === 'online' ? 'ok' : 'warn'}>{p.vpsName}</Badge> : <span className="muted">local</span>}</td>
                  <td>{F.ago(p.lastActivityAt)}</td>
                  <td>{F.date(p.createdAt)}</td>
                </tr>
              ))}
            </tbody>
          </table>
        </div>
      )}
    </div>
  );
}
window.ProjectsView = ProjectsView;

// ── Activity ────────────────────────────────────────────────────────────
function ActivityView() {
  const { data, loading, error, reload } = useLoader(() => API.get('/api/admin/activity'));
  if (loading) return <div className="empty"><Spinner /> Cargando…</div>;
  if (error) return <div className="empty">Error: {error}</div>;
  return (
    <div className="col" style={{ gap: 18 }}>
      <div className="cards cards-2">
        <div className="panel">
          <div className="card-head"><span>Top contributors · últimos 30 días</span></div>
          <BarList rows={data.topContributorsLast30d.map((r) => ({ label: (r.actorName || r.actorId) + (r.email ? ' · ' + r.email : ''), count: r.count }))} />
        </div>
        <div className="panel">
          <div className="card-head"><span>Acciones · últimos 7 días</span></div>
          <BarList rows={data.actionsLast7d.map((r) => ({ label: r.entityType + ' · ' + r.action, count: r.count }))} />
        </div>
      </div>
      <div className="panel">
        <div className="card-head"><span>Actividad diaria · últimos 30 días</span></div>
        <div className="row" style={{ gap: 16, marginTop: 8 }}>
          <Sparkline data={data.heatmapLast30d} height={56} color="#FBBF24" />
        </div>
      </div>
      <div className="row" style={{ justifyContent: 'flex-end' }}>
        <button className="btn ghost" onClick={reload}><Icon name="refresh-ccw" size={13} /></button>
      </div>
    </div>
  );
}
window.ActivityView = ActivityView;

// ── Funnel ──────────────────────────────────────────────────────────────
function FunnelView() {
  const { data, loading, error, reload } = useLoader(() => API.get('/api/admin/funnel'));
  if (loading) return <div className="empty"><Spinner /></div>;
  if (error) return <div className="empty">Error: {error}</div>;
  const total = data.stages[0]?.count || 1;
  return (
    <div className="col" style={{ gap: 14 }}>
      <div className="panel">
        <div className="col" style={{ gap: 14 }}>
          {data.stages.map((s, i) => {
            const pct = (s.count / total) * 100;
            const prev = i > 0 ? data.stages[i - 1].count : null;
            const dropoff = prev ? (1 - s.count / Math.max(1, prev)) * 100 : 0;
            return (
              <div key={s.key} className="col" style={{ gap: 6 }}>
                <div className="row spread">
                  <span>{s.label}</span>
                  <span className="muted">{F.num(s.count)} · {pct.toFixed(0)}% del total{prev !== null && prev !== s.count ? ` · −${dropoff.toFixed(0)}% vs paso anterior` : ''}</span>
                </div>
                <div className="bg-bar" style={{ height: 16, borderRadius: 6 }}>
                  <div style={{ width: pct.toFixed(1) + '%', height: '100%', borderRadius: 6,
                    background: 'linear-gradient(90deg, #3B82F6 0%, #60A5FA 100%)' }} />
                </div>
              </div>
            );
          })}
        </div>
      </div>
      <div className="row" style={{ justifyContent: 'flex-end' }}>
        <button className="btn ghost" onClick={reload}><Icon name="refresh-ccw" size={13} /></button>
      </div>
    </div>
  );
}
window.FunnelView = FunnelView;

// ── MCP ─────────────────────────────────────────────────────────────────
function McpView() {
  const { data, loading, error, reload } = useLoader(() => API.get('/api/admin/mcp'));
  if (loading) return <div className="empty"><Spinner /></div>;
  if (error) return <div className="empty">Error: {error}</div>;
  return (
    <div className="col" style={{ gap: 18 }}>
      <div className="cards cards-3">
        <Card title="Tokens activos" value={F.num(data.tokensActive)} />
        <Card title="Tokens expirados" value={F.num(data.tokensExpired)} />
        <Card title="Clientes OAuth" value={F.num(data.byClient.length)} />
      </div>
      <div className="panel">
        <div className="card-head"><span>Por cliente</span></div>
        <BarList rows={data.byClient.map((r) => ({ label: r.clientName, count: r.count }))} />
      </div>
      <div className="panel">
        <div className="card-head"><span>Tokens recientes</span></div>
        <div className="tbl-wrap" style={{ maxHeight: 360 }}>
          <table className="tbl">
            <thead><tr><th>Usuario</th><th>Workspace</th><th>Cliente</th><th>Estado</th><th>Último uso</th><th>Creado</th></tr></thead>
            <tbody>
              {data.recent.map((t) => (
                <tr key={t.id}>
                  <td className="mono softer">{t.userEmail || '—'}</td>
                  <td>{t.workspaceName || '—'}</td>
                  <td>{t.clientName}</td>
                  <td>{t.expired ? <Badge kind="bad">expirado</Badge> : <Badge kind="ok">vigente</Badge>}</td>
                  <td>{F.ago(t.lastUsedAt)}</td>
                  <td>{F.date(t.createdAt)}</td>
                </tr>
              ))}
            </tbody>
          </table>
        </div>
      </div>
      <div className="row" style={{ justifyContent: 'flex-end' }}>
        <button className="btn ghost" onClick={reload}><Icon name="refresh-ccw" size={13} /></button>
      </div>
    </div>
  );
}
window.McpView = McpView;

// ── Code sessions ───────────────────────────────────────────────────────
function CodeView() {
  const { data, loading, error, reload } = useLoader(() => API.get('/api/admin/code-sessions'));
  if (loading) return <div className="empty"><Spinner /></div>;
  if (error) return <div className="empty">Error: {error}</div>;
  return (
    <div className="col" style={{ gap: 18 }}>
      <div className="cards">
        <Card title="In-flight ahora" value={F.num(data.inflight)} foot="turnos vivos" />
        {data.byStatus.map((s) => <Card key={s.status} title={s.status} value={F.num(s.count)} />)}
      </div>
      <div className="panel">
        <div className="card-head"><span>Sesiones recientes</span></div>
        <div className="tbl-wrap" style={{ maxHeight: 480 }}>
          <table className="tbl">
            <thead><tr>
              <th>Título</th><th>Actor</th><th>Proyecto</th><th>Status</th>
              <th>Modelo</th><th>Working dir</th><th>Tokens</th><th>Costo</th><th>Último mensaje</th>
            </tr></thead>
            <tbody>
              {data.recent.map((s) => (
                <tr key={s.id}>
                  <td>{s.title}</td>
                  <td className="mono softer">{s.actorName} {s.actorEmail && <span className="muted">{s.actorEmail}</span>}</td>
                  <td>{s.projectName || s.projectId || '—'}</td>
                  <td><Badge kind={s.status === 'running' ? 'info' : s.status === 'errored' ? 'bad' : s.status === 'completed' ? 'ok' : ''}>{s.status}</Badge></td>
                  <td>{s.model}{s.effort ? ' · ' + s.effort : ''}</td>
                  <td className="mono softer">{s.workingDir}</td>
                  <td className="num">{F.num(s.tokensIn + s.tokensOut)}</td>
                  <td className="num">${s.totalCostUsd.toFixed(2)}</td>
                  <td>{F.ago(s.lastMessageAt)}</td>
                </tr>
              ))}
            </tbody>
          </table>
        </div>
      </div>
      <div className="row" style={{ justifyContent: 'flex-end' }}>
        <button className="btn ghost" onClick={reload}><Icon name="refresh-ccw" size={13} /></button>
      </div>
    </div>
  );
}
window.CodeView = CodeView;

// ── Health ──────────────────────────────────────────────────────────────
function HealthView() {
  const { data, loading, error, reload } = useLoader(() => API.get('/api/admin/health'));
  if (loading) return <div className="empty"><Spinner /></div>;
  if (error) return <div className="empty">Error: {error}</div>;
  return (
    <div className="col" style={{ gap: 18 }}>
      <div className="cards cards-3">
        <Card title="DB latency" value={data.db.latencyMs + ' ms'} foot={<Badge kind={data.db.latencyMs < 50 ? 'ok' : data.db.latencyMs < 200 ? 'warn' : 'bad'}>{data.db.ok ? 'OK' : 'down'}</Badge>} />
        <Card title="Presencia (5 min)" value={F.num(data.presence.liveActorsLast5m)} foot="actores con heartbeat reciente" />
        <Card title="Code turns in-flight" value={F.num(data.inflightCodeTurns)} />
      </div>
      <div className="panel">
        <div className="card-head"><span>Tablas Postgres</span></div>
        <div className="tbl-wrap" style={{ maxHeight: 480 }}>
          <table className="tbl">
            <thead><tr><th>Tabla</th><th>Rows</th><th>Tamaño total</th></tr></thead>
            <tbody>
              {data.tableSizes.map((t) => (
                <tr key={t.table}>
                  <td className="mono">{t.table}</td>
                  <td className="num">{F.num(t.liveRows)}</td>
                  <td className="num">{F.bytes(t.totalBytes)}</td>
                </tr>
              ))}
            </tbody>
          </table>
        </div>
      </div>
      <div className="row" style={{ justifyContent: 'flex-end' }}>
        <button className="btn ghost" onClick={reload}><Icon name="refresh-ccw" size={13} /> Refrescar</button>
        <span className="muted" style={{ fontSize: 11 }}>Snapshot {F.ago(data.generatedAt)}</span>
      </div>
    </div>
  );
}
window.HealthView = HealthView;
