/* global React, Button, Icon, EmptyState, LoadingSpinner, Pill, Select, useApiResource, useAuth, useToast, apiFetch, formatDateTime, formatRelative, formatNumber, formatDuration, formatPercent, shortId */

// MCP Analytics — the "PostHog for Code Mode MCPs" surface. Consumes the
// telemetry that Armature-built Code Mode gateways (e.g. tsuga-code-mode-mcp)
// push to /api/mcp-analytics/ingest. Feature-flag-gated (`mcp_analytics`);
// see lib/features.js and migration 124.
//
// Tabs (all under /mcp-analytics, ?tab=… in the URL):
//   overview · sessions · intents · demand · settings
//
// Each tab is a thin component that calls one /api/mcp-analytics/* endpoint
// via useApiResource. The Run Detail drawer (one execute_script row in full)
// is a sub-component reused from Sessions and Intents.

const {
  useEffect: useEffectMa,
  useMemo: useMemoMa,
  useState: useStateMa,
} = React;

const MCP_ANALYTICS_TABS = [
  { key: 'overview', label: 'Overview', icon: 'sparkles' },
  { key: 'sessions', label: 'Sessions', icon: 'workflow' },
  { key: 'intents', label: 'Intents', icon: 'target' },
  { key: 'demand', label: 'Demand', icon: 'plug' },
  { key: 'settings', label: 'Settings', icon: 'settings' },
];

const MCP_ANALYTICS_RANGES = [
  { value: '24h', label: 'Past 24 hours' },
  { value: '7d', label: 'Past 7 days' },
  { value: '30d', label: 'Past 30 days' },
];

const FRUSTRATION_TONES = {
  low: 'ok',
  medium: 'warn',
  high: 'bad',
};

const OUTCOME_TONES = {
  success: 'ok',
  ambiguous: 'warn',
  failed: 'bad',
};

function normaliseTab(tab) {
  return MCP_ANALYTICS_TABS.find((t) => t.key === tab)?.key || 'overview';
}

function McpAnalyticsPage({ navigate, queryString = '', tab }) {
  const query = useMemoMa(() => new URLSearchParams(queryString || ''), [queryString]);
  const activeTab = normaliseTab(tab);
  const range = useMemoMa(() => {
    const r = query.get('range');
    return MCP_ANALYTICS_RANGES.find((x) => x.value === r) ? r : '7d';
  }, [query]);

  function navTab(nextTab) {
    const params = new URLSearchParams(queryString);
    params.delete('tab');
    const qs = params.toString();
    navigate(`/mcp-analytics/${nextTab}${qs ? `?${qs}` : ''}`);
  }

  function navRange(nextRange) {
    const params = new URLSearchParams(queryString);
    params.set('range', nextRange);
    navigate(`/mcp-analytics/${activeTab}?${params.toString()}`);
  }

  return (
    <div className="page page-mcp-analytics">
      <div className="page-header page-mcp-analytics-header">
        <div className="page-header-titlerow">
          <h1>MCP Analytics</h1>
          <p className="page-header-subtitle">
            Live product intelligence from your Code Mode MCP gateway —
            every agent intent, every script, every search miss.
          </p>
        </div>
        <McpAnalyticsTabBar activeTab={activeTab} onNav={navTab} />
        {activeTab !== 'settings' && (
          <McpAnalyticsRangePicker range={range} onChange={navRange} />
        )}
      </div>
      <div className="page-body mcp-analytics-body">
        {activeTab === 'overview' && <McpAnalyticsOverview range={range} navTab={navTab} />}
        {activeTab === 'sessions' && <McpAnalyticsSessions range={range} />}
        {activeTab === 'intents' && <McpAnalyticsIntents range={range} />}
        {activeTab === 'demand' && <McpAnalyticsDemand />}
        {activeTab === 'settings' && <McpAnalyticsSettings />}
      </div>
    </div>
  );
}

function McpAnalyticsTabBar({ activeTab, onNav }) {
  return (
    <div className="mcp-analytics-tabbar" role="tablist">
      {MCP_ANALYTICS_TABS.map((t) => (
        <button
          key={t.key}
          role="tab"
          aria-selected={activeTab === t.key}
          className={`mcp-analytics-tab ${activeTab === t.key ? 'is-active' : ''}`}
          onClick={() => onNav(t.key)}>
          <Icon name={t.icon} size={13} />
          <span>{t.label}</span>
        </button>
      ))}
    </div>
  );
}

function McpAnalyticsRangePicker({ range, onChange }) {
  return (
    <div className="mcp-analytics-range-picker">
      {MCP_ANALYTICS_RANGES.map((r) => (
        <button
          key={r.value}
          className={`mcp-analytics-range-chip ${range === r.value ? 'is-active' : ''}`}
          onClick={() => onChange(r.value)}>
          {r.label}
        </button>
      ))}
    </div>
  );
}

// ─── Overview ──────────────────────────────────────────────────────────

function McpAnalyticsOverview({ range, navTab }) {
  const { data, loading, error, reload } = useApiResource(
    `/api/mcp-analytics/overview?range=${encodeURIComponent(range)}`,
  );

  if (loading && !data) return <LoadingSpinner label="Loading analytics overview" />;
  if (error) {
    return (
      <EmptyState
        icon="alert"
        title="Couldn't load overview"
        body={String(error?.message || error)}
        action={<Button onClick={reload}>Retry</Button>} />
    );
  }
  const k = data?.kpis || {};
  const total = (k.executions || 0) + (k.search_only || 0);
  const successRate = k.executions > 0 ? (k.success / k.executions) : 0;

  return (
    <div className="mcp-analytics-overview">
      <div className="mcp-analytics-kpi-strip">
        <KpiTile label="Executions" value={formatNumber(k.executions || 0)} sub={`${formatNumber(k.search_only || 0)} search-only`} tone={null} />
        <KpiTile label="Success rate" value={k.executions > 0 ? formatPercent(successRate) : '—'} sub={`${formatNumber(k.errors || 0)} errors`} tone={successRate >= 0.9 ? 'ok' : successRate >= 0.7 ? 'warn' : 'bad'} />
        <KpiTile label="P50 / P90 duration" value={`${formatDuration((k.p50_duration_ms || 0) / 1000)} · ${formatDuration((k.p90_duration_ms || 0) / 1000)}`} sub={null} tone={null} />
        <KpiTile label="Actors" value={formatNumber(k.actors || 0)} sub={`${formatNumber(k.sessions || 0)} sessions`} tone={null} />
      </div>

      <div className="mcp-analytics-overview-grid">
        <div className="mcp-analytics-card">
          <div className="mcp-analytics-card-header">
            <h3>Activity</h3>
            <span className="mcp-analytics-card-sub">{total} events in window</span>
          </div>
          <McpAnalyticsActivityChart byDay={data?.by_day || []} />
        </div>

        <div className="mcp-analytics-card">
          <div className="mcp-analytics-card-header">
            <h3>Top demand clusters</h3>
            <button className="mcp-analytics-card-link" onClick={() => navTab('demand')}>Open Demand →</button>
          </div>
          {(data?.top_demand_clusters || []).length === 0 ? (
            <EmptyState icon="plug" title="No demand clusters yet" body="Demand clusters appear when users search for things the SDK doesn't expose, or when intents fail in the same way. The clustering cron groups them into themes." />
          ) : (
            <ul className="mcp-analytics-cluster-list">
              {data.top_demand_clusters.map((c) => (
                <li key={c.id} className="mcp-analytics-cluster-row">
                  <span className="mcp-analytics-cluster-label">{c.label}</span>
                  <span className="mcp-analytics-cluster-meta">
                    {c.member_count} signals · last seen {formatRelative(c.last_seen_at)}
                  </span>
                </li>
              ))}
            </ul>
          )}
        </div>

        <div className="mcp-analytics-card">
          <div className="mcp-analytics-card-header">
            <h3>Top error operations</h3>
          </div>
          {(data?.top_errors || []).length === 0 ? (
            <EmptyState icon="check" title="No errors in window" body="No failed tsuga.* calls in this range." />
          ) : (
            <ul className="mcp-analytics-error-list">
              {data.top_errors.map((e, i) => (
                <li key={`${e.operation_id}-${e.status}-${i}`} className="mcp-analytics-error-row">
                  <span className="mcp-analytics-mono">{e.operation_id || 'unknown'}</span>
                  <span className="mcp-analytics-error-status">HTTP {e.status ?? '—'}</span>
                  <span className="mcp-analytics-error-count">{e.occurrences}×</span>
                </li>
              ))}
            </ul>
          )}
        </div>
      </div>
    </div>
  );
}

function KpiTile({ label, value, sub, tone }) {
  return (
    <div className={`mcp-analytics-kpi ${tone ? `mcp-analytics-kpi-${tone}` : ''}`}>
      <div className="mcp-analytics-kpi-label">{label}</div>
      <div className="mcp-analytics-kpi-value">{value}</div>
      {sub && <div className="mcp-analytics-kpi-sub">{sub}</div>}
    </div>
  );
}

function McpAnalyticsActivityChart({ byDay }) {
  // Lightweight inline SVG sparkline. No charting dep needed.
  const days = byDay.slice(-30);
  if (days.length === 0) {
    return <div className="mcp-analytics-empty-mini">No activity yet in this window.</div>;
  }
  const max = Math.max(1, ...days.map((d) => d.exec_count || 0));
  const w = 100 / Math.max(1, days.length);
  return (
    <div className="mcp-analytics-activity-chart" aria-label="Daily execute_script counts">
      <div className="mcp-analytics-activity-bars">
        {days.map((d, i) => {
          const total = d.exec_count || 0;
          const errors = d.error_count || 0;
          const okPct = total === 0 ? 0 : ((total - errors) / max) * 100;
          const errPct = total === 0 ? 0 : (errors / max) * 100;
          return (
            <div
              key={i}
              className="mcp-analytics-activity-col"
              style={{ width: `${w}%` }}
              title={`${formatDateTime(d.day)} — ${total} runs, ${errors} errors`}>
              <div className="mcp-analytics-activity-bar mcp-analytics-activity-bar-err" style={{ height: `${errPct}%` }} />
              <div className="mcp-analytics-activity-bar mcp-analytics-activity-bar-ok" style={{ height: `${okPct}%` }} />
            </div>
          );
        })}
      </div>
    </div>
  );
}

// ─── Sessions ──────────────────────────────────────────────────────────

function McpAnalyticsSessions({ range }) {
  const { data, loading, error, reload } = useApiResource(
    `/api/mcp-analytics/sessions?range=${encodeURIComponent(range)}&limit=100`,
  );
  const [openSessionId, setOpenSessionId] = useStateMa(null);

  if (loading && !data) return <LoadingSpinner label="Loading sessions" />;
  if (error) {
    return (
      <EmptyState
        icon="alert"
        title="Couldn't load sessions"
        body={String(error?.message || error)}
        action={<Button onClick={reload}>Retry</Button>} />
    );
  }
  const sessions = data?.sessions || [];
  if (sessions.length === 0) {
    return (
      <EmptyState
        icon="workflow"
        title="No sessions in window"
        body="Sessions appear as soon as your gateway pushes its first execute_script event. See Settings to provision the ingest token." />
    );
  }

  return (
    <div className="mcp-analytics-sessions">
      <table className="mcp-analytics-table">
        <thead>
          <tr>
            <th>Last activity</th>
            <th>Actor</th>
            <th>Intent</th>
            <th className="num">Events</th>
            <th className="num">OK / Err</th>
            <th>Frustration</th>
            <th></th>
          </tr>
        </thead>
        <tbody>
          {sessions.map((s) => (
            <tr key={s.id} className="mcp-analytics-row" onClick={() => setOpenSessionId(s.id)}>
              <td>{formatRelative(s.last_event_at)}</td>
              <td>
                <span className="mcp-analytics-mono">
                  {s.resolved_identity?.display_name || `actor:${shortId(s.actor_id)}`}
                </span>
              </td>
              <td className="mcp-analytics-intent-cell">
                {s.intent_summary || <em className="mcp-analytics-dim">(not yet summarised)</em>}
              </td>
              <td className="num">{formatNumber(s.event_count)}</td>
              <td className="num">
                <span className="mcp-analytics-ok">{s.ok_count}</span>
                {' / '}
                <span className={s.error_count > 0 ? 'mcp-analytics-bad' : 'mcp-analytics-dim'}>{s.error_count}</span>
              </td>
              <td>
                {s.peak_frustration ? (
                  <Pill tone={FRUSTRATION_TONES[s.peak_frustration]}>{s.peak_frustration}</Pill>
                ) : <span className="mcp-analytics-dim">—</span>}
              </td>
              <td><Icon name="chevronRight" size={13} /></td>
            </tr>
          ))}
        </tbody>
      </table>
      {openSessionId && (
        <SessionDrawer
          sessionId={openSessionId}
          onClose={() => setOpenSessionId(null)} />
      )}
    </div>
  );
}

function SessionDrawer({ sessionId, onClose }) {
  const { data, loading, error } = useApiResource(
    `/api/mcp-analytics/sessions/${encodeURIComponent(sessionId)}`,
  );
  const [openEventId, setOpenEventId] = useStateMa(null);

  useEffectMa(() => {
    function onEsc(e) { if (e.key === 'Escape') onClose(); }
    document.addEventListener('keydown', onEsc);
    return () => document.removeEventListener('keydown', onEsc);
  }, [onClose]);

  return (
    <div className="mcp-analytics-drawer-overlay" onClick={onClose}>
      <div className="mcp-analytics-drawer" onClick={(e) => e.stopPropagation()}>
        <div className="mcp-analytics-drawer-header">
          <h2>Session detail</h2>
          <Button variant="ghost" onClick={onClose}><Icon name="x" size={13} /> Close</Button>
        </div>
        <div className="mcp-analytics-drawer-body">
          {loading && <LoadingSpinner label="Loading session" />}
          {error && <EmptyState icon="alert" title="Failed to load session" body={String(error?.message || error)} />}
          {data && (
            <>
              <SessionSummary session={data.session} />
              <div className="mcp-analytics-event-list">
                {(data.events || []).map((ev) => (
                  <SessionEventRow
                    key={ev.id}
                    event={ev}
                    onOpen={() => setOpenEventId(ev.id)} />
                ))}
              </div>
            </>
          )}
        </div>
      </div>
      {openEventId && (
        <EventDrawer
          eventId={openEventId}
          onClose={() => setOpenEventId(null)} />
      )}
    </div>
  );
}

function SessionSummary({ session }) {
  return (
    <div className="mcp-analytics-session-summary">
      <div><strong>Actor:</strong> <span className="mcp-analytics-mono">{session.resolved_identity?.display_name || `actor:${shortId(session.actor_id)}`}</span></div>
      <div><strong>Started:</strong> {formatDateTime(session.started_at)}</div>
      <div><strong>Last event:</strong> {formatRelative(session.last_event_at)}</div>
      <div><strong>Events:</strong> {session.event_count} ({session.ok_count} ok / {session.error_count} err)</div>
      <div><strong>Session key:</strong> <span className="mcp-analytics-mono">{session.session_key}</span></div>
      {session.peak_frustration && (
        <div><strong>Peak frustration:</strong> <Pill tone={FRUSTRATION_TONES[session.peak_frustration]}>{session.peak_frustration}</Pill></div>
      )}
    </div>
  );
}

function SessionEventRow({ event, onOpen }) {
  const intent = event.metadata?.intent || '(no @intent)';
  const isErr = !event.ok;
  return (
    <button className={`mcp-analytics-event-row ${isErr ? 'is-err' : ''}`} onClick={onOpen}>
      <span className="mcp-analytics-event-time">{formatDateTime(event.started_at)}</span>
      <span className="mcp-analytics-event-intent">{intent}</span>
      <span className="mcp-analytics-event-meta">
        {event.kind === 'search_sdk_standalone'
          ? <span className="mcp-analytics-event-kind">search-only</span>
          : (
            <>
              {Array.isArray(event.calls) ? event.calls.length : 0} calls
              · {formatDuration((event.duration_ms || 0) / 1000)}
              {event.outcome && <Pill tone={OUTCOME_TONES[event.outcome]}>{event.outcome}</Pill>}
              {!event.outcome && isErr && <Pill tone="bad">error</Pill>}
            </>
          )}
      </span>
    </button>
  );
}

// ─── Run Detail drawer (one execute_script event) ─────────────────────

function EventDrawer({ eventId, onClose }) {
  const { data, loading, error } = useApiResource(
    `/api/mcp-analytics/events/${encodeURIComponent(eventId)}`,
  );
  useEffectMa(() => {
    function onEsc(e) { if (e.key === 'Escape') onClose(); }
    document.addEventListener('keydown', onEsc);
    return () => document.removeEventListener('keydown', onEsc);
  }, [onClose]);

  return (
    <div className="mcp-analytics-drawer-overlay mcp-analytics-drawer-overlay-stacked" onClick={onClose}>
      <div className="mcp-analytics-drawer mcp-analytics-drawer-event" onClick={(e) => e.stopPropagation()}>
        <div className="mcp-analytics-drawer-header">
          <h2>Run detail</h2>
          <Button variant="ghost" onClick={onClose}><Icon name="x" size={13} /> Close</Button>
        </div>
        <div className="mcp-analytics-drawer-body">
          {loading && <LoadingSpinner label="Loading event" />}
          {error && <EmptyState icon="alert" title="Failed to load event" body={String(error?.message || error)} />}
          {data?.event && <EventDetail event={data.event} />}
        </div>
      </div>
    </div>
  );
}

function EventDetail({ event }) {
  const intent = event.metadata?.intent || '(no @intent)';
  const context = event.metadata?.context;
  const selfFrust = event.metadata?.frustration_level;
  const calls = Array.isArray(event.calls) ? event.calls : [];
  const searchCalls = Array.isArray(event.search_calls) ? event.search_calls : [];
  const logs = Array.isArray(event.logs) ? event.logs : [];

  return (
    <div className="mcp-analytics-event-detail">
      <div className="mcp-analytics-event-detail-header">
        <h3>{intent}</h3>
        <div className="mcp-analytics-event-detail-pills">
          {event.outcome && <Pill tone={OUTCOME_TONES[event.outcome]}>outcome: {event.outcome}</Pill>}
          {event.inferred_frustration && <Pill tone={FRUSTRATION_TONES[event.inferred_frustration]}>inferred: {event.inferred_frustration}</Pill>}
          {selfFrust && <Pill tone={FRUSTRATION_TONES[selfFrust]}>self: {selfFrust}</Pill>}
          {event.intent_category_label && <Pill tone="brand">{event.intent_category_label}</Pill>}
        </div>
      </div>
      {context && <div className="mcp-analytics-event-context"><strong>Context:</strong> {context}</div>}
      {event.outcome_reason && <div className="mcp-analytics-event-context"><strong>Outcome reasoning:</strong> {event.outcome_reason}</div>}
      <div className="mcp-analytics-event-stats">
        <span><strong>Duration:</strong> {formatDuration((event.duration_ms || 0) / 1000)}</span>
        <span><strong>Calls:</strong> {calls.length}</span>
        <span><strong>Logs:</strong> {logs.length}</span>
        <span><strong>Started:</strong> {formatDateTime(event.started_at)}</span>
      </div>

      {event.error_text && (
        <details className="mcp-analytics-event-section" open>
          <summary>Error</summary>
          <pre className="mcp-analytics-pre mcp-analytics-pre-err">{event.error_text}</pre>
        </details>
      )}

      {event.script_source && (
        <details className="mcp-analytics-event-section" open>
          <summary>Script source {event.script_source_truncated && <em>(truncated)</em>}</summary>
          <pre className="mcp-analytics-pre">{event.script_source}</pre>
        </details>
      )}

      {calls.length > 0 && (
        <details className="mcp-analytics-event-section" open>
          <summary>Call trace ({calls.length})</summary>
          <ol className="mcp-analytics-call-list">
            {calls.map((c, i) => (
              <li key={i} className={`mcp-analytics-call ${c.ok ? '' : 'is-err'}`}>
                <span className="mcp-analytics-mono">{c.operation_id || c.shape}</span>
                <span className="mcp-analytics-call-shape">{c.shape}</span>
                <span className="mcp-analytics-call-status">HTTP {c.status ?? '—'}</span>
                <span className="mcp-analytics-call-dur">{formatDuration((c.duration_ms || 0) / 1000)}</span>
                {c.network_error && <span className="mcp-analytics-call-err">{c.network_error}</span>}
              </li>
            ))}
          </ol>
        </details>
      )}

      {searchCalls.length > 0 && (
        <details className="mcp-analytics-event-section">
          <summary>search_sdk calls ({searchCalls.length})</summary>
          <ul className="mcp-analytics-search-list">
            {searchCalls.map((s, i) => (
              <li key={i} className={`mcp-analytics-search-row ${s.is_miss ? 'is-miss' : ''}`}>
                <span className="mcp-analytics-mono">"{s.query}"</span>
                <span>{s.match_count} matches</span>
                {s.is_miss && <Pill tone="warn">miss</Pill>}
              </li>
            ))}
          </ul>
        </details>
      )}

      {event.result_preview && (
        <details className="mcp-analytics-event-section">
          <summary>Result preview {event.result_truncated && <em>(truncated)</em>}</summary>
          <pre className="mcp-analytics-pre">{event.result_preview}</pre>
        </details>
      )}

      {logs.length > 0 && (
        <details className="mcp-analytics-event-section">
          <summary>console logs ({logs.length})</summary>
          <ul className="mcp-analytics-log-list">
            {logs.map((l, i) => (
              <li key={i} className={`mcp-analytics-log mcp-analytics-log-${l.level}`}>
                <span className="mcp-analytics-log-time">{l.ts ? formatDateTime(l.ts) : ''}</span>
                <span>{l.text}</span>
              </li>
            ))}
          </ul>
        </details>
      )}
    </div>
  );
}

// ─── Intents feed ──────────────────────────────────────────────────────

function McpAnalyticsIntents({ range }) {
  const [frustration, setFrustration] = useStateMa('');
  const [search, setSearch] = useStateMa('');
  const [searchDraft, setSearchDraft] = useStateMa('');
  const qs = useMemoMa(() => {
    const p = new URLSearchParams();
    p.set('range', range);
    if (frustration) p.set('frustration', frustration);
    if (search) p.set('search', search);
    p.set('limit', '100');
    return p.toString();
  }, [range, frustration, search]);
  const { data, loading, error, reload } = useApiResource(
    `/api/mcp-analytics/intents?${qs}`,
  );
  const [openEventId, setOpenEventId] = useStateMa(null);

  function applySearch(e) {
    e.preventDefault();
    setSearch(searchDraft.trim());
  }

  if (error) {
    return (
      <EmptyState
        icon="alert"
        title="Couldn't load intents"
        body={String(error?.message || error)}
        action={<Button onClick={reload}>Retry</Button>} />
    );
  }
  const intents = data?.intents || [];

  return (
    <div className="mcp-analytics-intents">
      <form className="mcp-analytics-intent-filters" onSubmit={applySearch}>
        <input
          className="input"
          placeholder="Search intents…"
          value={searchDraft}
          onChange={(e) => setSearchDraft(e.target.value)} />
        <Select
          value={frustration}
          onChange={setFrustration}
          ariaLabel="Frustration filter"
          options={[
            { value: '', label: 'All frustration' },
            { value: 'low', label: 'low' },
            { value: 'medium', label: 'medium' },
            { value: 'high', label: 'high' },
          ]} />
        <Button type="submit" variant="ghost">Apply</Button>
      </form>
      {loading && !data && <LoadingSpinner label="Loading intents" />}
      {!loading && intents.length === 0 && (
        <EmptyState icon="target" title="No intents match" body="No execute_script events with these filters yet." />
      )}
      <ul className="mcp-analytics-intent-feed">
        {intents.map((it) => {
          const isErr = !it.ok;
          return (
            <li
              key={it.id}
              className={`mcp-analytics-intent-card ${isErr ? 'is-err' : ''}`}
              onClick={() => setOpenEventId(it.id)}>
              <div className="mcp-analytics-intent-text">
                {it.metadata?.intent || <em className="mcp-analytics-dim">(no @intent)</em>}
              </div>
              <div className="mcp-analytics-intent-meta">
                <span>{formatRelative(it.started_at)}</span>
                <span>{formatDuration((it.duration_ms || 0) / 1000)}</span>
                {it.intent_category_label && <Pill tone="brand">{it.intent_category_label}</Pill>}
                {it.outcome && <Pill tone={OUTCOME_TONES[it.outcome]}>{it.outcome}</Pill>}
                {isErr && !it.outcome && <Pill tone="bad">error</Pill>}
                {(it.inferred_frustration || it.metadata?.frustration_level) && (
                  <Pill tone={FRUSTRATION_TONES[it.inferred_frustration || it.metadata?.frustration_level]}>
                    {it.inferred_frustration || it.metadata?.frustration_level}
                  </Pill>
                )}
              </div>
            </li>
          );
        })}
      </ul>
      {openEventId && (
        <EventDrawer eventId={openEventId} onClose={() => setOpenEventId(null)} />
      )}
    </div>
  );
}

// ─── Demand ────────────────────────────────────────────────────────────

function McpAnalyticsDemand() {
  const { data, loading, error, reload } = useApiResource(
    `/api/mcp-analytics/demand?limit=50`,
  );
  if (loading && !data) return <LoadingSpinner label="Loading demand" />;
  if (error) {
    return (
      <EmptyState
        icon="alert"
        title="Couldn't load demand"
        body={String(error?.message || error)}
        action={<Button onClick={reload}>Retry</Button>} />
    );
  }
  const clusters = data?.clusters || [];
  const unclustered = data?.unclustered_counts || {};
  const pending = (unclustered.search_miss || 0) + (unclustered.failed_intent || 0);

  return (
    <div className="mcp-analytics-demand">
      <div className="mcp-analytics-demand-summary">
        <div>
          <strong>{clusters.length}</strong> demand clusters
        </div>
        <div>
          <strong>{pending}</strong> signals pending clustering
          {' '}<span className="mcp-analytics-dim">({unclustered.search_miss || 0} search misses · {unclustered.failed_intent || 0} failed intents)</span>
        </div>
      </div>
      {clusters.length === 0 ? (
        <EmptyState
          icon="plug"
          title="No demand clusters yet"
          body="The demand-clustering cron groups search misses and failed intents into themes. New gateways start empty here." />
      ) : (
        <ul className="mcp-analytics-cluster-grid">
          {clusters.map((c) => (
            <li key={c.id} className="mcp-analytics-cluster-card">
              <div className="mcp-analytics-cluster-card-label">{c.label}</div>
              {c.description && <div className="mcp-analytics-cluster-card-desc">{c.description}</div>}
              <div className="mcp-analytics-cluster-card-meta">
                <span><strong>{c.member_count}</strong> signals</span>
                <span>last seen {formatRelative(c.last_seen_at)}</span>
              </div>
            </li>
          ))}
        </ul>
      )}
    </div>
  );
}

// ─── Settings ──────────────────────────────────────────────────────────

function McpAnalyticsSettings() {
  const { data, loading, error, reload } = useApiResource(`/api/mcp-analytics/servers`);
  const auth = useAuth();
  const isOwnerOrAdmin = ['owner', 'admin'].includes(String(auth?.me?.role || '').toLowerCase());
  const [revealedSecret, setRevealedSecret] = useStateMa(null);
  const [busyServerId, setBusyServerId] = useStateMa(null);
  const toast = useToast();

  async function provision(serverId) {
    if (!window.confirm('Generate a new ingest token for this MCP server? Any previous token is replaced — the gateway must be reconfigured with the new secret.')) return;
    setBusyServerId(serverId);
    try {
      // apiFetch returns parsed JSON on success and throws on non-OK.
      const json = await apiFetch('/api/mcp-analytics/ingest-token', {
        method: 'POST',
        body: JSON.stringify({ mcp_server_id: serverId, action: 'provision' }),
      });
      setRevealedSecret({ serverId, secret: json.secret });
      reload();
    } catch (e) {
      toast?.show?.({ tone: 'bad', message: `Provision failed: ${e.message || e}` });
    } finally {
      setBusyServerId(null);
    }
  }

  async function revoke(serverId) {
    if (!window.confirm('Revoke the ingest token? The gateway will stop being able to push events until you re-provision.')) return;
    setBusyServerId(serverId);
    try {
      await apiFetch('/api/mcp-analytics/ingest-token', {
        method: 'POST',
        body: JSON.stringify({ mcp_server_id: serverId, action: 'revoke' }),
      });
      toast?.show?.({ tone: 'ok', message: 'Token revoked.' });
      reload();
    } catch (e) {
      toast?.show?.({ tone: 'bad', message: `Revoke failed: ${e.message || e}` });
    } finally {
      setBusyServerId(null);
    }
  }

  if (loading && !data) return <LoadingSpinner label="Loading settings" />;
  if (error) {
    return (
      <EmptyState
        icon="alert"
        title="Couldn't load settings"
        body={String(error?.message || error)}
        action={<Button onClick={reload}>Retry</Button>} />
    );
  }
  const servers = data?.servers || [];

  return (
    <div className="mcp-analytics-settings">
      <section className="mcp-analytics-settings-section">
        <h3>Ingest tokens</h3>
        <p className="mcp-analytics-settings-help">
          Each Code Mode MCP gateway pushes telemetry signed with a per-server
          HMAC secret. Provision the token here, then set three env vars on the
          gateway:
        </p>
        <pre className="mcp-analytics-pre mcp-analytics-env-snippet">{`ANALYTICS_INGEST_URL=https://${window.location.host}/api/mcp-analytics/ingest
ANALYTICS_INGEST_SECRET=<revealed below — one-time>
ANALYTICS_MCP_SERVER_ID=<server id from the table>`}</pre>
        {servers.length === 0 ? (
          <EmptyState
            icon="plug"
            title="No MCP servers in this organization"
            body="Connect a server in Sources → My MCPs & CLIs before you can provision an analytics ingest token for it." />
        ) : (
          <table className="mcp-analytics-table mcp-analytics-settings-table">
            <thead>
              <tr>
                <th>MCP server</th>
                <th>ID</th>
                <th>Token</th>
                <th>Last used</th>
                <th></th>
              </tr>
            </thead>
            <tbody>
              {servers.map((s) => {
                const provisioned = Boolean(s.token_created_at);
                const revoked = Boolean(s.token_revoked_at);
                return (
                  <tr key={s.id}>
                    <td>{s.display_name || <em className="mcp-analytics-dim">(unnamed)</em>}</td>
                    <td><span className="mcp-analytics-mono">{s.id}</span></td>
                    <td>
                      {!provisioned && <Pill tone="warn">not provisioned</Pill>}
                      {provisioned && !revoked && <Pill tone="ok">active</Pill>}
                      {provisioned && revoked && <Pill tone="bad">revoked</Pill>}
                    </td>
                    <td>{s.token_last_used_at ? formatRelative(s.token_last_used_at) : <span className="mcp-analytics-dim">never</span>}</td>
                    <td>
                      {isOwnerOrAdmin ? (
                        <div className="mcp-analytics-row-actions">
                          <Button
                            disabled={busyServerId === s.id}
                            onClick={() => provision(s.id)}>
                            {provisioned ? 'Rotate' : 'Provision'}
                          </Button>
                          {provisioned && !revoked && (
                            <Button
                              variant="ghost"
                              disabled={busyServerId === s.id}
                              onClick={() => revoke(s.id)}>
                              Revoke
                            </Button>
                          )}
                        </div>
                      ) : <span className="mcp-analytics-dim">owner/admin only</span>}
                    </td>
                  </tr>
                );
              })}
            </tbody>
          </table>
        )}
        {revealedSecret && (
          <div className="mcp-analytics-secret-reveal">
            <div className="mcp-analytics-secret-header">
              <Icon name="key" size={13} />
              <strong>New ingest secret — copy now, we cannot show it again.</strong>
            </div>
            <pre className="mcp-analytics-pre mcp-analytics-pre-secret">{revealedSecret.secret}</pre>
            <Button variant="ghost" onClick={() => setRevealedSecret(null)}>Done</Button>
          </div>
        )}
      </section>
    </div>
  );
}

window.McpAnalyticsPage = McpAnalyticsPage;
