/* global React, Button, Icon, Pill, EmptyState, LoadingSpinner, useAuth, useToast, useApiResource, apiFetch, formatRelative, canUseTenantAction, tenantActionDisabledReason, getPlanLimitState, useEscapeToClose, CategoryBadge, CategoryFilterRow, useCategoryFilterState, McpConnectModal, useMcpConnectFormState, useMcpProvisioningPoll, validateMcpConnectFormInputs, buildMcpConnectPayload, runMcpOAuthFlow, startOAuthConnect, McpServerDetail, ProductMcpCreateModal, ProductMcpDetailView */

// /mcps — the unified MCP registry. One list for every server Armature
// knows about: hosted product MCPs (code mode), the org's own remote
// servers, CLI targets, and SDK-analytics-only registrations. Replaces
// /product-mcps (which now redirects here); /sources stays as-is for now.
//
// One entity, three capabilities:
//   Hosting   — Armature generates and runs it (product MCP)
//   Analytics — session telemetry via the ingest key + SDK
//   Testing   — workflows/benchmarks can call it (probe + tool discovery)
// Each card shows the capabilities it has; dashed chips are the upsell
// path to the ones it doesn't.

const { useState: useStateMx, useMemo: useMemoMx, useEffect: useEffectMx } = React;

function mxTargetKind(row) {
  return row?.target_kind || row?.targetKind || 'mcp';
}

function mxIsHostedNpmRow(row) {
  return mxTargetKind(row) === 'mcp' && (row?.transport_type || row?.transportType) === 'stdio_hosted';
}

const MX_PROVISIONING_LABELS = {
  pending: 'queued',
  provisioning: 'provisioning',
  discovering: 'discovering',
  ready: 'ready',
  failed: 'failed',
};

// Mirrors the status derivation on McpServerCard (pages-mcp-servers.jsx):
// hosted/CLI targets report provisioning_status; remote targets report the
// latest connection test.
function mxTestingStatus(row) {
  const kind = mxTargetKind(row);
  const provisioned = kind === 'cli' || mxIsHostedNpmRow(row);
  const status = provisioned
    ? (row.provisioning_status || row.last_connection_status || row.latest_connection_test?.status)
    : (row.last_connection_status || row.latest_connection_test?.status);
  if (provisioned) {
    const tone = status === 'ready' ? 'ok' : status === 'failed' ? 'bad' : status ? 'warn' : 'neutral';
    return { tone, label: MX_PROVISIONING_LABELS[status] || status || 'unknown' };
  }
  if (status === 'success' || status === 'ok') return { tone: 'ok', label: 'reachable' };
  if (status === 'failed' || status === 'error') return { tone: 'bad', label: status };
  if (status) return { tone: 'warn', label: status };
  return { tone: 'neutral', label: 'never tested' };
}

const MX_PRODUCT_STATUS_TONE = {
  live: 'ok',
  needs_setup: 'warn',
  paused: 'neutral',
  error: 'bad',
};

function MxCapChip({ tone = 'neutral', dashed = false, icon = null, children, onClick = null, title = '' }) {
  const className = `mcps-cap-chip mcps-cap-${tone}${dashed ? ' mcps-cap-dashed' : ''}${onClick ? ' mcps-cap-clickable' : ''}`;
  const body = (
    <>
      {icon ? <Icon name={icon} size={11} /> : !dashed && <span className={`mcps-cap-dot mcps-cap-dot-${tone}`} />}
      <span>{children}</span>
    </>
  );
  if (!onClick) return <span className={className} title={title}>{body}</span>;
  return (
    <button
      type="button"
      className={className}
      title={title}
      onClick={(event) => { event.stopPropagation(); onClick(); }}>
      {body}
    </button>
  );
}

function MxOriginPill({ kind }) {
  if (kind === 'product') return <Pill tone="info">Hosted</Pill>;
  if (kind === 'cli') return <Pill>CLI</Pill>;
  if (kind === 'analytics_ingest') return <Pill>SDK</Pill>;
  return <Pill>Your server</Pill>;
}

// ---------------------------------------------------------------------------
// Cards
// ---------------------------------------------------------------------------

function MxProductCard({ row, analyticsInfo, onOpen }) {
  const tone = MX_PRODUCT_STATUS_TONE[row.status] || 'neutral';
  const connOk = row.authenticationStatus === 'configured';
  return (
    <div
      className="mcp-card ui-surface-interactive"
      role="button"
      tabIndex={0}
      onClick={onOpen}
      onKeyDown={(e) => { if (e.key === 'Enter' || e.key === ' ') { e.preventDefault(); onOpen(); } }}>
      <div className="mcp-card-head">
        <div className="mcp-card-icon"><Icon name="globe" size={16} /></div>
        <div className="mcp-card-identity">
          <div className="mcp-card-name">{row.name}</div>
          <div className="mcp-card-url mono">{row.publicMcpUrl || `/${row.sourceSlug}`}</div>
        </div>
      </div>
      <div className="mcp-card-meta">
        <MxOriginPill kind="product" />
        <Pill tone={tone} dot>{row.status === 'needs_setup' ? 'needs setup' : row.status || 'unknown'}</Pill>
      </div>
      <div className="mcps-cap-row">
        <MxCapChip tone={tone}>
          Hosting · {row.status === 'live' ? 'live' : (row.status === 'needs_setup' ? 'needs setup' : row.status || 'unknown')}
        </MxCapChip>
        <MxCapChip tone={analyticsInfo ? 'ok' : 'neutral'}>
          Analytics · {analyticsInfo?.token_last_used_at
            ? `last event ${formatRelative(analyticsInfo.token_last_used_at)}`
            : row.requests24h > 0
              ? `${row.requests24h} requests · 24h`
              : 'no events yet'}
        </MxCapChip>
        {!connOk && (
          <MxCapChip tone="warn" icon="alert">API connection missing</MxCapChip>
        )}
      </div>
      <div className="mcp-card-cta">
        <span>Open</span>
        <Icon name="chevronRight" size={12} />
      </div>
    </div>
  );
}

function MxTargetCard({ row, ingestKey, analyticsInfo, analyticsEnabled, categoriesEnabled, canManageKeys, onOpen, onConnectAnalytics }) {
  const kind = mxTargetKind(row);
  const isSdkOnly = kind === 'analytics_ingest';
  const testing = mxTestingStatus(row);
  const toolCount = row.latest_connection_test?.tool_count;
  const environment = row.environment || '';
  const subtitle = kind === 'cli'
    ? row.connection_config?.cli?.command || row.connection_config?.cli_package_name || 'CLI command'
    : row.base_url || (isSdkOnly ? 'SDK ingest only' : 'No base URL');
  const interactive = !isSdkOnly;
  const open = interactive ? onOpen : undefined;
  const analyticsChip = ingestKey
    ? (
      <MxCapChip
        tone="ok"
        onClick={analyticsEnabled ? onConnectAnalytics : null}
        title="Manage the analytics ingest key">
        Analytics · {ingestKey.lastUsedAt
          ? `last event ${formatRelative(ingestKey.lastUsedAt)}`
          : 'key issued · waiting for events'}
      </MxCapChip>
    )
    : analyticsEnabled && canManageKeys && !isSdkOnly
      ? (
        <MxCapChip dashed icon="plus" onClick={onConnectAnalytics} title="Provision an ingest key and instrument this server">
          Connect SDK analytics
        </MxCapChip>
      )
      : analyticsInfo
        ? (
          <MxCapChip tone="ok">
            Analytics · {analyticsInfo.token_last_used_at ? `last event ${formatRelative(analyticsInfo.token_last_used_at)}` : 'connected'}
          </MxCapChip>
        )
        : null;
  return (
    <div
      className={`mcp-card ${interactive ? 'ui-surface-interactive' : 'ui-surface'}`}
      role={interactive ? 'button' : undefined}
      tabIndex={interactive ? 0 : undefined}
      onClick={open}
      onKeyDown={interactive
        ? (e) => { if (e.key === 'Enter' || e.key === ' ') { e.preventDefault(); onOpen(); } }
        : undefined}>
      <div className="mcp-card-head">
        <div className="mcp-card-icon"><Icon name={kind === 'cli' ? 'terminal' : isSdkOnly ? 'key' : 'mcp'} size={16} /></div>
        <div className="mcp-card-identity">
          <div className="mcp-card-name">{row.name}</div>
          <div className="mcp-card-url mono">{subtitle}</div>
        </div>
      </div>
      <div className="mcp-card-meta">
        <MxOriginPill kind={kind} />
        {environment && environment !== 'production' && <Pill>{environment}</Pill>}
        {categoriesEnabled && row.category?.slug && <CategoryBadge category={row.category} />}
        {!isSdkOnly && <Pill tone={testing.tone} dot>{testing.label}</Pill>}
      </div>
      <div className="mcps-cap-row">
        {!isSdkOnly && (
          <MxCapChip tone={testing.tone} onClick={onOpen} title="Open testing details">
            Testing · {testing.label}{toolCount != null && testing.tone === 'ok' ? ` · ${toolCount} tool${toolCount === 1 ? '' : 's'}` : ''}
          </MxCapChip>
        )}
        {analyticsChip}
      </div>
      {interactive && (
        <div className="mcp-card-cta">
          <span>Open</span>
          <Icon name="chevronRight" size={12} />
        </div>
      )}
    </div>
  );
}

// ---------------------------------------------------------------------------
// Add MCP — intent step
// ---------------------------------------------------------------------------

const MX_INTENTS = [
  {
    key: 'hosted',
    icon: 'sparkles',
    title: "It doesn't exist yet — generate and host it",
    body: 'From your OpenAPI docs or GitHub repo. Armature runs the endpoint; analytics is built in.',
  },
  {
    key: 'server',
    icon: 'plug',
    title: 'I already run an MCP server',
    body: 'Connect a remote URL or a hosted npm package, then enable testing and SDK analytics.',
  },
  {
    key: 'cli',
    icon: 'terminal',
    title: "It's a CLI",
    body: 'An npm package or binary. Armature runs it sandboxed for testing and benchmarks.',
  },
];

function MxIntentModal({ onClose, onPick }) {
  useEscapeToClose({ enabled: true, disabled: false, onClose });
  return (
    <div
      className="dialog-backdrop"
      onMouseDown={(event) => { if (event.target === event.currentTarget) onClose(); }}>
      <div className="modal-panel mcps-intent-panel" role="dialog" aria-modal="true" aria-labelledby="mcps-intent-title">
        <div className="modal-header">
          <div>
            <div className="modal-title" id="mcps-intent-title">Add an MCP</div>
            <div className="modal-subtitle">Where does it live today?</div>
          </div>
          <button className="icon-btn" type="button" aria-label="Close" onClick={onClose}>
            <Icon name="x" size={15} />
          </button>
        </div>
        <div className="modal-body mcps-intent-body">
          {MX_INTENTS.map((intent) => (
            <button
              key={intent.key}
              type="button"
              className="mcps-intent-option"
              onClick={() => onPick(intent.key)}>
              <span className="mcps-intent-icon"><Icon name={intent.icon} size={18} /></span>
              <span>
                <span className="mcps-intent-title">{intent.title}</span>
                <span className="mcps-intent-body-text">{intent.body}</span>
              </span>
              <Icon name="chevronRight" size={14} />
            </button>
          ))}
        </div>
      </div>
    </div>
  );
}

// ---------------------------------------------------------------------------
// Add MCP — connect an existing server / CLI. Same shared form stack as
// /sources and /benchmark (useMcpConnectFormState + McpConnectModal), with
// the submit path POSTing to /api/mcp-servers and landing on /mcps.
// ---------------------------------------------------------------------------

function MxConnectFlow({ targetKind, onClose, refresh, onOpenTarget }) {
  const toast = useToast();
  const formState = useMcpConnectFormState({ targetKind });
  const [serverSaving, setServerSaving] = useStateMx(false);
  const [oauthInFlight, setOauthInFlight] = useStateMx(null);
  const [cliSetupState, setCliSetupState] = useStateMx(null);
  const [pendingCliTargetId, setPendingCliTargetId] = useStateMx(null);
  const [pendingCliPollCount, setPendingCliPollCount] = useStateMx(0);
  const [message, setMessage] = useStateMx('');

  useMcpProvisioningPoll({
    cliSetupState, setCliSetupState,
    pendingCliTargetId, setPendingCliTargetId,
    pendingCliPollCount, setPendingCliPollCount,
    reload: refresh,
  });

  function cancelOauthInFlight() {
    const popup = oauthInFlight?.popup;
    if (popup && !popup.closed) {
      try { popup.close(); } catch (_error) {}
    }
  }

  // Probe the new target so its tool surface materializes on the card and
  // in the workflow picker without dragging the user into a wizard.
  async function quietProbe(serverId) {
    if (!serverId) return;
    try {
      await apiFetch(`/api/mcp-servers/${serverId}/probe`, { method: 'POST' });
      refresh();
    } catch (probeError) {
      toast.show({
        tone: 'warn',
        title: 'Could not discover tools',
        description: probeError.message || 'The MCP was added but its tool surface could not be discovered yet.',
      });
    }
  }

  async function runOauthForServer(serverId, { providerSlug, requestedScopes, authConfig }) {
    const oauthStart = await startOAuthConnect({
      mcpServerId: serverId,
      providerSlug,
      requestedScopes: requestedScopes || [],
      authConfig: authConfig || {},
      returnTo: '/mcps',
    });
    refresh();
    setOauthInFlight({
      popup: null,
      providerLabel: providerSlug || 'the provider',
      redirectUriMode: oauthStart?.redirectUriMode === 'loopback' ? 'loopback' : 'web',
    });
    let popupResult;
    try {
      popupResult = await runMcpOAuthFlow(oauthStart, {
        onPopupOpened: (popup) => setOauthInFlight((current) => current ? { ...current, popup } : current),
      });
    } finally {
      setOauthInFlight(null);
    }
    if (popupResult.outcome !== 'navigated') refresh();
    return popupResult;
  }

  async function addServer() {
    if (serverSaving) return;
    setServerSaving(true);
    setMessage('');
    try {
      const validationError = validateMcpConnectFormInputs(formState);
      if (validationError) throw new Error(validationError);
      const built = buildMcpConnectPayload(formState);
      const created = await apiFetch('/api/mcp-servers', {
        method: 'POST',
        body: JSON.stringify(built.payload),
      });
      const newServerId = created?.mcpServer?.id;

      if (newServerId && (built.kind === 'cli' || built.kind === 'hosted')) {
        setPendingCliTargetId(newServerId);
        setPendingCliPollCount(0);
        setCliSetupState({
          serverId: newServerId,
          serverName: formState.serverName.trim(),
          setupKind: built.kind,
          provisioningRunId: created?.provisioningRun?.id || null,
          status: created?.mcpServer?.provisioning_status || 'pending',
          failedAtStatus: null,
          errorCode: null,
          updatedAt: new Date().toISOString(),
        });
        toast.show({
          tone: 'ok',
          title: 'MCP added',
          description: 'Provisioning has started. It will appear as ready once setup finishes.',
        });
        refresh();
        if (built.payload.pendingOAuth) {
          const outcome = await runOauthForServer(newServerId, {
            providerSlug: built.payload.pendingOAuth.providerSlug,
            requestedScopes: built.payload.pendingOAuth.authConfig?.requested_scopes || [],
            authConfig: built.payload.pendingOAuth.authConfig || {},
          });
          if (outcome?.outcome === 'failed') {
            toast.show({ tone: 'bad', title: 'OAuth failed', description: outcome.message || 'OAuth authorization did not complete.' });
          }
        }
        return;
      }

      if (newServerId && built.kind === 'remote' && built.oauth) {
        const popupResult = await runOauthForServer(newServerId, {
          providerSlug: built.oauth.providerSlug,
          requestedScopes: built.oauth.requestedScopes,
          authConfig: {},
        });
        if (popupResult.outcome === 'navigated') return;
        if (popupResult.outcome === 'connected') {
          toast.show({ tone: 'ok', title: 'MCP connected', description: 'OAuth completed. Discovering tools…' });
          onClose();
          await quietProbe(newServerId);
          onOpenTarget(newServerId);
        } else if (popupResult.outcome === 'failed') {
          toast.show({ tone: 'bad', title: 'OAuth failed', description: popupResult.message || 'OAuth authorization did not complete.' });
        } else if (popupResult.outcome === 'pending') {
          toast.show({ tone: 'warn', title: 'OAuth completed; provisioning is still in progress', description: 'Tools will appear once provisioning finishes.' });
          onClose();
        } else if (popupResult.outcome === 'closed') {
          setMessage('Sign-in was canceled. Submit again to retry.');
        }
        return;
      }

      toast.show({ tone: 'ok', title: 'MCP added', description: 'Discovering tools…' });
      refresh();
      onClose();
      if (newServerId && built.kind === 'remote') {
        await quietProbe(newServerId);
        onOpenTarget(newServerId);
      }
    } catch (error) {
      toast.show({ tone: 'bad', title: 'Connect failed', description: error.message });
    } finally {
      setServerSaving(false);
    }
  }

  function handleCancel() {
    if (serverSaving) return;
    formState.reset();
    setMessage('');
    setCliSetupState(null);
    setPendingCliTargetId(null);
    setPendingCliPollCount(0);
    onClose();
  }

  return (
    <McpConnectModal
      open
      message={message}
      formState={formState}
      saving={serverSaving}
      oauthInFlight={oauthInFlight}
      onCancelOauth={cancelOauthInFlight}
      onSubmit={addServer}
      onCancel={handleCancel}
      cliSetupState={cliSetupState}
      onOpenSetupTarget={() => {
        if (!cliSetupState?.serverId) return;
        const serverId = cliSetupState.serverId;
        formState.reset();
        setCliSetupState(null);
        setPendingCliTargetId(null);
        onClose();
        onOpenTarget(serverId);
      }} />
  );
}

// ---------------------------------------------------------------------------
// Connect SDK analytics — provision / rotate / revoke the ingest key for an
// existing server, with the reveal-once token + env-var instructions.
// ---------------------------------------------------------------------------

function MxAnalyticsDialog({ server, ingestKey, onClose, onChanged }) {
  const toast = useToast();
  const [busy, setBusy] = useStateMx(false);
  const [revealedToken, setRevealedToken] = useStateMx('');
  const [confirmingRevoke, setConfirmingRevoke] = useStateMx(false);
  useEscapeToClose({ enabled: true, disabled: busy, onClose });

  async function provision() {
    setBusy(true);
    try {
      const result = await apiFetch('/api/settings/analytics-ingest-keys', {
        method: 'POST',
        body: JSON.stringify({ mcpServerId: server.id }),
      });
      setRevealedToken(result.token || '');
      onChanged();
    } catch (error) {
      toast.show({ tone: 'bad', title: 'Could not create ingest key', description: error.message });
    } finally {
      setBusy(false);
    }
  }

  async function rotate() {
    if (!ingestKey) return;
    setBusy(true);
    try {
      const result = await apiFetch(`/api/settings/analytics-ingest-keys/${ingestKey.id}`, {
        method: 'POST',
        body: JSON.stringify({ action: 'rotate' }),
      });
      setRevealedToken(result.token || '');
      onChanged();
    } catch (error) {
      toast.show({ tone: 'bad', title: 'Could not rotate ingest key', description: error.message });
    } finally {
      setBusy(false);
    }
  }

  async function revoke() {
    if (!ingestKey) return;
    setBusy(true);
    try {
      await apiFetch(`/api/settings/analytics-ingest-keys/${ingestKey.id}`, { method: 'DELETE' });
      toast.show({ tone: 'ok', title: 'Ingest key revoked' });
      onChanged();
      onClose();
    } catch (error) {
      toast.show({ tone: 'bad', title: 'Could not revoke ingest key', description: error.message });
    } finally {
      setBusy(false);
      setConfirmingRevoke(false);
    }
  }

  async function copyToken() {
    try {
      await navigator.clipboard.writeText(revealedToken);
      toast.show({ tone: 'ok', title: 'Token copied' });
    } catch (_error) {
      toast.show({ tone: 'bad', title: 'Could not copy — select it manually' });
    }
  }

  return (
    <div
      className="dialog-backdrop"
      onMouseDown={(event) => { if (event.target === event.currentTarget && !busy) onClose(); }}>
      <div className="modal-panel mcps-analytics-panel" role="dialog" aria-modal="true" aria-labelledby="mcps-analytics-title">
        <div className="modal-header">
          <div>
            <div className="modal-title" id="mcps-analytics-title">Session analytics</div>
            <div className="modal-subtitle">{server.name}</div>
          </div>
          <button className="icon-btn" type="button" aria-label="Close" disabled={busy} onClick={onClose}>
            <Icon name="x" size={15} />
          </button>
        </div>
        <div className="modal-body">
          {revealedToken ? (
            <div className="mcps-token-reveal">
              <div className="field-label">Ingestion token</div>
              <div className="field-help">
                Shown once. Set it as <span className="mono">ANALYTICS_INGEST_API_KEY</span> in this
                server's deployment and instrument it with <span className="mono">@armature-tech/mcp-analytics</span> —
                sessions will appear under Session Analytics.
              </div>
              <div className="settings-token-row">
                <div className="code api-key-token">{revealedToken}</div>
                <Button size="sm" variant="ghost" onClick={copyToken}><Icon name="copy" size={13} />Copy</Button>
              </div>
            </div>
          ) : ingestKey ? (
            <div className="mcps-key-status">
              <div className="field-row">
                <div>
                  <div className="field-label">Active key</div>
                  <div className="field-help mono">{ingestKey.tokenPrefix}...</div>
                </div>
                <div className="field-value muted">
                  Created {formatRelative(ingestKey.createdAt)}
                  {' · '}Calls {ingestKey.callCount || 0}
                  {' · '}Last used {ingestKey.lastUsedAt ? formatRelative(ingestKey.lastUsedAt) : 'never'}
                </div>
              </div>
              <div className="field-help">
                Rotating mints a new secret for this server and invalidates the old one immediately.
              </div>
            </div>
          ) : (
            <div className="field-help">
              Provision a bearer token for this server, set it as
              {' '}<span className="mono">ANALYTICS_INGEST_API_KEY</span> in your deployment, and
              instrument the server with <span className="mono">@armature-tech/mcp-analytics</span>.
              Tool calls then show up as sessions under Session Analytics.
            </div>
          )}
        </div>
        <div className="modal-actions">
          {!revealedToken && ingestKey && (
            confirmingRevoke ? (
              <>
                <span className="muted">Revoke this key? Ingest stops immediately.</span>
                <Button variant="danger" loading={busy} loadingLabel="Revoking..." onClick={revoke}>Revoke key</Button>
                <Button variant="ghost" disabled={busy} onClick={() => setConfirmingRevoke(false)}>Keep it</Button>
              </>
            ) : (
              <>
                <Button variant="ghost" disabled={busy} onClick={() => setConfirmingRevoke(true)}>Revoke</Button>
                <Button variant="primary" loading={busy} loadingLabel="Rotating..." onClick={rotate}>
                  <Icon name="key" size={13} />Rotate key
                </Button>
              </>
            )
          )}
          {!revealedToken && !ingestKey && (
            <Button variant="primary" loading={busy} loadingLabel="Creating key..." onClick={provision}>
              <Icon name="key" size={13} />Create ingestion key
            </Button>
          )}
          {revealedToken && (
            <Button variant="primary" onClick={onClose}>Done</Button>
          )}
        </div>
      </div>
    </div>
  );
}

// ---------------------------------------------------------------------------
// Page
// ---------------------------------------------------------------------------

const MX_KIND_FILTERS = [
  { key: 'all', label: 'All' },
  { key: 'product', label: 'Hosted' },
  { key: 'mcp', label: 'MCP servers' },
  { key: 'cli', label: 'CLIs' },
  { key: 'analytics_ingest', label: 'SDK only' },
];

function McpsPage({ navigate, queryString }) {
  const auth = useAuth();
  const productMcpEnabled = auth?.me?.featureFlags?.productMcp === true;
  const analyticsEnabled = auth?.me?.featureFlags?.mcpAnalytics === true;
  const categoriesEnabled = auth?.me?.featureFlags?.mcpCategories === true;
  const canManage = canUseTenantAction(auth, 'editor');
  const canManageKeys = canUseTenantAction(auth, 'admin');
  const manageDisabledReason = tenantActionDisabledReason(auth, 'editor');

  const params = useMemoMx(() => new URLSearchParams(queryString || ''), [queryString]);
  const selectedProductId = params.get('id');
  const detailServerId = params.get('mcp');

  // Same-tab OAuth callbacks return to /mcps?server=<id> (AppBody's
  // buildMcpOAuthReturnPath sets `server`, mirroring /sources). Remap to
  // this page's ?mcp= deep link so the target's detail reopens, and
  // replace the URL so refreshes don't re-trigger the remap.
  const oauthReturnServerId = params.get('server');
  useEffectMx(() => {
    if (!oauthReturnServerId) return;
    navigate(`/mcps?mcp=${encodeURIComponent(oauthReturnServerId)}`, { replace: true });
  }, [oauthReturnServerId, navigate]);

  // GitHub App install round-trips land back here (the /product-mcps
  // redirect preserves the query string), so the create wizard must
  // reopen exactly like it did on the old page.
  const githubSetup = {
    githubInstallationId: params.get('githubInstallationId') || '',
    githubInstallationGrant: params.get('githubInstallationGrant') || '',
    setupAction: params.get('githubSetupAction') || '',
    setupError: params.get('githubSetupError') || '',
  };
  const shouldOpenProductCreate = !selectedProductId && (
    params.get('create') === '1'
    || Boolean(githubSetup.githubInstallationId)
    || Boolean(githubSetup.setupError)
  );

  const productList = useApiResource(productMcpEnabled ? '/api/product-mcps' : null, [productMcpEnabled]);
  const servers = useApiResource('/api/mcp-servers');
  const ingestKeys = useApiResource(analyticsEnabled ? '/api/settings/analytics-ingest-keys' : null, [analyticsEnabled]);
  const analyticsServers = useApiResource(analyticsEnabled ? '/api/mcp-analytics/servers' : null, [analyticsEnabled]);
  const categoriesResource = useApiResource(categoriesEnabled ? '/api/mcp-categories' : null, [categoriesEnabled]);
  const categories = categoriesResource.data?.rows || [];

  const [intentOpen, setIntentOpen] = useStateMx(false);
  const [creatingProduct, setCreatingProduct] = useStateMx(shouldOpenProductCreate);
  const [connectKind, setConnectKind] = useStateMx(null);
  const [analyticsTarget, setAnalyticsTarget] = useStateMx(null);
  const [setupNotice, setSetupNotice] = useStateMx(null);
  const [kindFilter, setKindFilter] = useStateMx('all');
  const [envFilter, setEnvFilter] = useStateMx('all');

  const { selectedSlugs, toggleSlug, clearAll } = useCategoryFilterState({
    queryString,
    navigate,
    pathname: '/mcps',
  });

  const orgSlug = auth?.me?.organization?.slug || 'your-org';
  const baseDomain = `mcp.${orgSlug}.armature.tech`;

  const serverRows = servers.data?.rows || [];
  const productRows = productList.data?.rows || [];

  const ingestKeyByServerId = useMemoMx(() => {
    const out = {};
    for (const key of ingestKeys.data?.rows || []) {
      if (key.mcpServerId) out[key.mcpServerId] = key;
    }
    return out;
  }, [ingestKeys.data]);

  const analyticsByServerId = useMemoMx(() => {
    const out = {};
    for (const row of analyticsServers.data?.servers || []) out[row.id] = row;
    return out;
  }, [analyticsServers.data]);

  const environments = useMemoMx(() => {
    const values = new Set();
    for (const row of serverRows) {
      if (row.environment) values.add(row.environment);
    }
    return Array.from(values).sort();
  }, [serverRows]);

  const entries = useMemoMx(() => {
    const product = productRows.map((row) => ({ kind: 'product', id: row.id, name: row.name || '', row }));
    const targets = serverRows.map((row) => ({ kind: mxTargetKind(row), id: row.id, name: row.name || '', row }));
    return [...product, ...targets].sort((a, b) => {
      if ((a.kind === 'product') !== (b.kind === 'product')) return a.kind === 'product' ? -1 : 1;
      return a.name.localeCompare(b.name);
    });
  }, [productRows, serverRows]);

  const kindCounts = useMemoMx(() => {
    const out = { all: entries.length };
    for (const entry of entries) out[entry.kind] = (out[entry.kind] || 0) + 1;
    return out;
  }, [entries]);

  // Category filters only apply to target rows — product MCPs can't be
  // categorized, so an active category filter hides them (consistent with
  // /sources, which never lists them). The filtered empty state below
  // offers a "Clear filters" reset so kind+category combinations that
  // match nothing (e.g. Hosted + a category) aren't a dead end.
  const filteredEntries = useMemoMx(() => entries.filter((entry) => {
    if (kindFilter !== 'all' && entry.kind !== kindFilter) return false;
    if (envFilter !== 'all' && entry.kind !== 'product' && (entry.row.environment || 'production') !== envFilter) return false;
    if (categoriesEnabled && selectedSlugs.size > 0) {
      if (entry.kind === 'product') return false;
      if (!entry.row.category?.slug || !selectedSlugs.has(entry.row.category.slug)) return false;
    }
    return true;
  }), [entries, kindFilter, envFilter, categoriesEnabled, selectedSlugs]);

  const categoryCounts = useMemoMx(() => {
    const out = {};
    for (const entry of entries) {
      const slug = entry.kind !== 'product' && entry.row.category?.slug;
      if (!slug) continue;
      out[slug] = (out[slug] || 0) + 1;
    }
    return out;
  }, [entries]);
  // "All" chip count in the category row counts categorizable rows only,
  // matching what an active category selection can ever show.
  const categorizableCount = useMemoMx(
    () => entries.filter((entry) => entry.kind !== 'product').length,
    [entries],
  );

  const hasActiveFilters = kindFilter !== 'all' || envFilter !== 'all' || (categoriesEnabled && selectedSlugs.size > 0);
  const clearAllFilters = () => {
    setKindFilter('all');
    setEnvFilter('all');
    if (categoriesEnabled && selectedSlugs.size > 0) clearAll();
  };

  const limitState = getPlanLimitState(auth, 'mcpServers', 1);
  const isInitialLoading = (servers.loading && !servers.data)
    || (productMcpEnabled && productList.loading && !productList.data);
  const addDisabled = isInitialLoading || !canManage || !limitState.allowed;
  const addDisabledReason = !canManage ? manageDisabledReason : !limitState.allowed ? limitState.message : '';

  const refresh = () => {
    servers.reload();
    if (productMcpEnabled) productList.reload();
    if (analyticsEnabled) {
      ingestKeys.reload();
      analyticsServers.reload();
    }
  };

  const openTarget = (serverId) => navigate(`/mcps?mcp=${encodeURIComponent(serverId)}`);
  const closeTarget = () => navigate('/mcps');
  const toast = useToast();

  const closeProductCreate = () => {
    setCreatingProduct(false);
    if (params.get('create') || githubSetup.githubInstallationId || githubSetup.setupError) navigate('/mcps');
  };

  if (selectedProductId) {
    return (
      <div className="page-inner">
        <ProductMcpDetailView
          id={selectedProductId}
          onBack={() => navigate('/mcps')}
          onDeleted={async () => {
            setSetupNotice(null);
            refresh();
            navigate('/mcps');
          }}
          setupNotice={setupNotice?.id === selectedProductId ? setupNotice : null} />
      </div>
    );
  }

  const hasAnyRows = entries.length > 0;

  return (
    <div className="page-inner">
      <div className="page-header">
        <div>
          <h1 className="page-title">MCPs</h1>
          <div className="page-subtitle">Every MCP Armature knows about — hosted, observed, or tested.</div>
        </div>
        <div className="page-actions">
          <Button
            variant="primary"
            disabled={addDisabled}
            title={addDisabledReason || undefined}
            onClick={() => setIntentOpen(true)}>
            <Icon name="plus" size={13} />Add MCP
          </Button>
        </div>
      </div>

      {hasAnyRows && (
        <div className="mcps-filter-bar">
          <div className="mcps-kind-filter" role="group" aria-label="Filter by kind">
            {MX_KIND_FILTERS.filter((f) => f.key === 'all' || (kindCounts[f.key] || 0) > 0).map((f) => (
              <button
                key={f.key}
                type="button"
                className={`mcp-category-chip ${kindFilter === f.key ? 'active' : ''}`}
                aria-pressed={kindFilter === f.key}
                onClick={() => setKindFilter(f.key)}>
                <span>{f.label}</span>
                <span className="mcp-category-chip-count">{kindCounts[f.key] || 0}</span>
              </button>
            ))}
            {environments.length > 1 && (
              <>
                <span className="mcps-filter-sep" aria-hidden="true" />
                {['all', ...environments].map((env) => (
                  <button
                    key={env}
                    type="button"
                    className={`mcp-category-chip ${envFilter === env ? 'active' : ''}`}
                    aria-pressed={envFilter === env}
                    onClick={() => setEnvFilter(env)}>
                    <span>{env === 'all' ? 'All envs' : env}</span>
                  </button>
                ))}
              </>
            )}
          </div>
          {categoriesEnabled && (
            <CategoryFilterRow
              categories={categories}
              selectedSlugs={selectedSlugs}
              onToggle={toggleSlug}
              onClear={clearAll}
              countsBySlug={categoryCounts}
              totalCount={categorizableCount} />
          )}
        </div>
      )}

      {isInitialLoading ? (
        <div className="code inline-loading" style={{ margin: '40px auto' }}>
          <LoadingSpinner size="sm" label="Loading MCPs" decorative /> Loading MCPs…
        </div>
      ) : servers.error ? (
        <EmptyState icon="alert" title="Couldn't load MCPs" body={servers.error.message || 'Try again in a moment.'} />
      ) : !hasAnyRows ? (
        <EmptyState
          icon="mcp"
          title="No MCPs yet"
          body="Generate and host an MCP from your API docs, connect a server you already run, or add a CLI to test."
          action={(
            <Button variant="primary" disabled={addDisabled} title={addDisabledReason || undefined} onClick={() => setIntentOpen(true)}>
              <Icon name="plus" size={13} />Add MCP
            </Button>
          )} />
      ) : filteredEntries.length === 0 ? (
        <EmptyState
          icon="search"
          title="No MCPs match these filters"
          body={categoriesEnabled && selectedSlugs.size > 0 && kindFilter === 'product'
            ? 'Hosted MCPs have no category, so they never match a category filter.'
            : 'Clear a filter to see the rest of the registry.'}
          action={hasActiveFilters ? (
            <Button onClick={clearAllFilters}>Clear filters</Button>
          ) : null} />
      ) : (
        <div className="mcps-grid">
          {filteredEntries.map((entry) => entry.kind === 'product' ? (
            <MxProductCard
              key={`product-${entry.id}`}
              row={entry.row}
              analyticsInfo={analyticsByServerId[entry.id] || null}
              onOpen={() => navigate(`/mcps?id=${encodeURIComponent(entry.id)}`)} />
          ) : (
            <MxTargetCard
              key={entry.id}
              row={entry.row}
              ingestKey={ingestKeyByServerId[entry.id] || null}
              analyticsInfo={analyticsByServerId[entry.id] || null}
              analyticsEnabled={analyticsEnabled}
              categoriesEnabled={categoriesEnabled}
              canManageKeys={canManageKeys}
              onOpen={() => openTarget(entry.id)}
              onConnectAnalytics={() => setAnalyticsTarget(entry.row)} />
          ))}
        </div>
      )}

      <McpServerDetail
        serverId={detailServerId}
        navigate={navigate}
        onRefreshed={refresh}
        onClose={closeTarget}
        onDeleted={(name) => {
          closeTarget();
          toast.show({ tone: 'ok', title: `Deleted ${name}` });
          refresh();
        }}
        onEdited={() => {
          toast.show({ tone: 'ok', title: 'MCP updated' });
          refresh();
        }}
        onCreateWorkflow={() => navigate(`/workflows?new=1&server=${encodeURIComponent(detailServerId)}`)}
        onViewMonitors={() => navigate('/tool-monitors')} />

      {intentOpen && (
        <MxIntentModal
          onClose={() => setIntentOpen(false)}
          onPick={(intentKey) => {
            setIntentOpen(false);
            if (intentKey === 'hosted') setCreatingProduct(true);
            else setConnectKind(intentKey === 'cli' ? 'cli' : 'mcp');
          }} />
      )}

      {creatingProduct && (
        <ProductMcpCreateModal
          baseDomain={baseDomain}
          githubSetup={githubSetup}
          onClose={closeProductCreate}
          onCreated={(id, notice) => {
            setCreatingProduct(false);
            setSetupNotice(notice ? { id, ...notice } : null);
            refresh();
            navigate(`/mcps?id=${encodeURIComponent(id)}`);
          }} />
      )}

      {connectKind && (
        <MxConnectFlow
          targetKind={connectKind}
          onClose={() => setConnectKind(null)}
          refresh={refresh}
          onOpenTarget={(serverId) => {
            setConnectKind(null);
            openTarget(serverId);
          }} />
      )}

      {analyticsTarget && (
        <MxAnalyticsDialog
          server={analyticsTarget}
          ingestKey={ingestKeyByServerId[analyticsTarget.id] || null}
          onClose={() => setAnalyticsTarget(null)}
          onChanged={refresh} />
      )}
    </div>
  );
}

window.McpsPage = McpsPage;
