/* global React, Button, ConfirmDialog, Icon, Pill, StatusDot, Drawer, EmptyState, Select, useApiResource, apiFetch, formatDateTime, formatDuration, formatRelative, shortId, ToolMonitorWizard, ToolWizardLoading, ToolArgField, buildInitialToolState, validateAllSelectedTools, buildArgsFromState, fieldKindFor, defaultValueForKind, validateField, coerceValueForSubmit, useToast, useAuth, requirePaidAction, canUseTenantAction, tenantActionDisabledReason, getPlanLimitState, AlertRulesButton, AlertRulesForTargetModal */
const { useState: useStateTM, useMemo: useMemoTM, useEffect: useEffectTM, useRef: useRefTM } = React;
const TOOL_MONITORS_TABLE_COLUMN_COUNT = 8;
const HOSTED_MCP_PENDING_STATUSES = new Set(['pending', 'provisioning', 'discovering']);

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

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

function catalogToolsForWizard(toolCatalog) {
  if (!Array.isArray(toolCatalog)) return [];
  return toolCatalog
    .filter((tool) => tool?.is_active !== false)
    .map((tool) => ({
      name: tool.tool_name || tool.name,
      description: tool.description || null,
      inputSchema: tool.input_schema || tool.inputSchema || { type: 'object', properties: {} },
    }))
    .filter((tool) => tool.name);
}

function hostedCatalogUnavailableMessage(server) {
  const status = server?.provisioning_status || server?.provisioningStatus;
  if (HOSTED_MCP_PENDING_STATUSES.has(status)) {
    return 'Hosted setup or discovery is still running. The catalog will appear automatically when it finishes.';
  }
  if (status === 'failed') {
    return server?.provisioning_error || 'Hosted setup failed. Retry provisioning from the MCP server details.';
  }
  return 'No tools are cataloged for this hosted MCP server yet. Open the server details and refresh the catalog.';
}

function isCliTargetTM(server) {
  return (server?.target_kind || server?.targetKind || 'mcp') === 'cli';
}

function cliProvisioningStatusTM(server) {
  return server?.provisioning_status || server?.provisioningStatus || 'ready';
}

function canMonitorServerTM(server) {
  if (!isCliTargetTM(server)) return true;
  return ['ready', 'needs_review'].includes(cliProvisioningStatusTM(server));
}

function monitorServerUnavailableReasonTM(server) {
  if (!isCliTargetTM(server)) return '';
  if (canMonitorServerTM(server)) return '';
  const status = cliProvisioningStatusTM(server);
  if (status === 'pending' || status === 'provisioning') return 'installing package';
  if (status === 'discovering') return 'discovering commands';
  if (status === 'failed') return 'provisioning failed';
  return 'not ready';
}

function parseToolMonitorAddParamsTM(queryString = '') {
  const params = new URLSearchParams(queryString || '');
  const openValue = params.get('new') || params.get('add') || '';
  return {
    open: openValue === '1' || openValue === 'true' || params.get('action') === 'new',
    serverId: params.get('server') || params.get('serverId') || params.get('mcpServerId') || '',
  };
}

function cliReviewRowToMonitorToolTM(row) {
  return {
    name: row.tool_name,
    description: row.description || null,
    inputSchema: row.input_schema || { type: 'object', properties: {} },
    cliRisk: cliRiskGroupTM(row?.risk || row?.source_metadata?.risk),
    reviewStatus: row.review_status || 'discovered',
  };
}

function cliRiskGroupTM(risk) {
  if (risk === 'read') return 'read';
  if (risk === 'mutating') return 'mutating';
  return 'unknown';
}

function cliReviewRowsToMonitorToolsTM(rows) {
  if (!Array.isArray(rows)) return [];
  return rows
    .filter((row) => row?.review_status !== 'disabled')
    .map(cliReviewRowToMonitorToolTM)
    .filter((tool) => tool.name);
}

function groupCliMonitorToolsTM(tools) {
  const specs = [
    { key: 'read', label: 'Read-only commands', defaultOpen: true },
    { key: 'mutating', label: 'Side-effecting commands', defaultOpen: false },
    { key: 'unknown', label: 'Unknown-risk commands', defaultOpen: false },
  ];
  return specs
    .map((spec) => ({ ...spec, tools: (tools || []).filter((tool) => cliRiskGroupTM(tool.cliRisk) === spec.key) }))
    .filter((group) => group.tools.length > 0);
}

function buildCliMonitorInitialToolStateTM(tools) {
  const initial = buildInitialToolState(tools);
  for (const tool of tools || []) {
    const risk = cliRiskGroupTM(tool.cliRisk);
    if (!initial[tool.name]) continue;
    initial[tool.name] = {
      ...initial[tool.name],
      checked: risk === 'read',
      bucket: risk === 'read' ? initial[tool.name].bucket : 'skipped',
      cliRisk: risk,
    };
  }
  return initial;
}

function agentFillToolsForAddTM(tools, existingNames, source) {
  const existing = existingNames instanceof Set ? existingNames : new Set(existingNames || []);
  return (tools || [])
    .filter((tool) => !existing.has(tool.name))
    .filter((tool) => source !== 'cli' || cliRiskGroupTM(tool.cliRisk) === 'read');
}

function buildEditDraftFromMonitor(monitor) {
  const schema = monitor.tool_input_schema;
  const props = (schema && typeof schema === 'object' && schema.properties && typeof schema.properties === 'object')
    ? schema.properties
    : null;
  const args = monitor.arguments && typeof monitor.arguments === 'object' && !Array.isArray(monitor.arguments)
    ? monitor.arguments
    : {};
  // Schema is unusable (server didn't probe yet, or tool removed) → fall back
  // to raw JSON editing so the user can still adjust arguments without losing
  // data. Form-mode kicks in once the schema is back.
  if (!props || Object.keys(props).length === 0) {
    return {
      schemaAvailable: false,
      interval: monitor.interval_seconds,
      argsRaw: JSON.stringify(args, null, 2),
    };
  }
  const values = {};
  for (const [key, fieldSchema] of Object.entries(props)) {
    if (Object.prototype.hasOwnProperty.call(args, key)) {
      values[key] = args[key];
    } else {
      values[key] = defaultValueForKind(fieldKindFor(fieldSchema), fieldSchema);
    }
  }
  // Anything in the saved arguments that isn't in the schema gets surfaced as
  // an editable extras row — preserves data when a tool's schema was edited
  // server-side after the monitor was created.
  const extras = [];
  for (const [key, value] of Object.entries(args)) {
    if (Object.prototype.hasOwnProperty.call(props, key)) continue;
    extras.push({
      key,
      value: typeof value === 'string' ? value : (() => {
        try { return JSON.stringify(value); } catch { return String(value); }
      })(),
    });
  }
  return {
    schemaAvailable: true,
    interval: monitor.interval_seconds,
    inputSchema: schema,
    values,
    extras,
    errors: {},
  };
}

function ToolMonitorsPage({ navigate, queryString = '' }) {
  const auth = useAuth();
  const toast = useToast();
  const monitors = useApiResource('/api/tool-monitors');
  const servers = useApiResource('/api/mcp-servers');
  const alerts = useApiResource('/api/alerts');
  const rows = monitors.data?.rows || [];
  const alertRows = alerts.data?.rows || [];
  const alertsByMonitor = useMemoTM(() => {
    const out = {};
    for (const rule of alertRows) {
      if (!rule.toolMonitorId) continue;
      if (!out[rule.toolMonitorId]) out[rule.toolMonitorId] = [];
      out[rule.toolMonitorId].push(rule);
    }
    return out;
  }, [alertRows]);
  const activeServers = useMemoTM(() => (servers.data?.rows || []).filter((s) => s.is_active), [servers.data?.rows]);
  const serverOptions = useMemoTM(() => activeServers.map((server) => ({
    server,
    canMonitor: canMonitorServerTM(server),
    reason: monitorServerUnavailableReasonTM(server),
  })), [activeServers]);
  const selectableServerOptions = serverOptions.filter((option) => option.canMonitor);
  const sourcePickerOptions = useMemoTM(() => selectableServerOptions.map((option) => ({
    value: option.server.id,
    label: option.server.name,
    icon: isCliTargetTM(option.server) ? 'terminal' : 'mcp',
  })), [selectableServerOptions]);
  const addParams = useMemoTM(() => parseToolMonitorAddParamsTM(queryString), [queryString]);
  const isInitialLoading = monitors.loading && !monitors.data;
  const loadError = monitors.error;
  const toolMonitorCreateLimitState = getPlanLimitState(auth, 'toolMonitors', 1);
  const canManageToolMonitors = canUseTenantAction(auth, 'editor');
  const toolMonitorActionDisabledReason = tenantActionDisabledReason(auth, 'editor');
  const [filter, setFilter] = useStateTM('all');
  const [search, setSearch] = useStateTM('');
  const [editingId, setEditingId] = useStateTM(null);
  const [editDraft, setEditDraft] = useStateTM(null);
  const [editError, setEditError] = useStateTM('');
  const [editSaving, setEditSaving] = useStateTM(false);
  const [busyAction, setBusyAction] = useStateTM(null);
  const [rowError, setRowError] = useStateTM({});
  const [pageMessage, setPageMessage] = useStateTM('');
  // Add-monitor wizard state
  const [addStep, setAddStep] = useStateTM('idle'); // 'idle' | 'pick' | 'probing' | 'wizard'
  const [addServerId, setAddServerId] = useStateTM('');
  const [addProbe, setAddProbe] = useStateTM(null); // { ok, tools, errorMessage }
  const [addSelectedTools, setAddSelectedTools] = useStateTM({});
  const [addAgentState, setAddAgentState] = useStateTM(/** @type {{status: string, error?: string}} */ ({ status: 'idle' }));
  const [addSavingMonitors, setAddSavingMonitors] = useStateTM(false);
  const [addCliStartActive, setAddCliStartActive] = useStateTM(false);
  const handledAddLinkRef = useRefTM('');
  const addSelectedServer = activeServers.find((server) => server.id === addServerId) || null;
  const addSelectedServerIsHosted = isHostedMcpServerRow(addSelectedServer);
  const addPrimaryActionLabel = addSelectedServerIsHosted
    ? 'Load tools'
    : isCliTargetTM(addSelectedServer)
      ? 'Load choices'
      : 'Probe & continue';
  // Per-row runs history state. Shape: { [monitorId]: { loading, error, rows } }
  const [expandedId, setExpandedId] = useStateTM(null);
  const [runsByMonitor, setRunsByMonitor] = useStateTM({});
  // Alert-rules-for-target modal state. `manageAlertsMonitor` holds the
  // monitor whose alert rules are being managed; null means closed. Track the
  // monitor itself rather than just the id so we can keep showing the name in
  // the modal header even after the row is removed from `rows`.
  const [manageAlertsMonitor, setManageAlertsMonitor] = useStateTM(null);

  function openManageAlertsFor(monitor) {
    setManageAlertsMonitor(monitor);
  }

  async function loadRunsFor(monitor) {
    setRunsByMonitor((prev) => ({
      ...prev,
      [monitor.id]: { ...(prev[monitor.id] || {}), loading: true, error: null },
    }));
    try {
      const data = await apiFetch(`/api/tool-monitors/${monitor.id}/runs?limit=20`);
      setRunsByMonitor((prev) => ({ ...prev, [monitor.id]: { loading: false, error: null, rows: data.rows || [] } }));
    } catch (err) {
      setRunsByMonitor((prev) => ({ ...prev, [monitor.id]: { loading: false, error: err.message, rows: null } }));
    }
  }

  async function toggleExpand(monitor) {
    if (expandedId === monitor.id) { setExpandedId(null); return; }
    setExpandedId(monitor.id);
    if (runsByMonitor[monitor.id]?.rows) return;
    await loadRunsFor(monitor);
  }

  function startAddFlow(preselectServerId = '') {
    if (!requirePaidAction(auth, navigate, { requiredRole: 'editor' })) return false;
    if (!toolMonitorCreateLimitState.allowed) {
      toast.show({ tone: 'bad', title: 'Plan limit reached', description: toolMonitorCreateLimitState.message });
      return false;
    }
    setPageMessage('');
    setAddProbe(null);
    setAddSelectedTools({});
    setAddCliStartActive(false);
    const requested = preselectServerId
      ? selectableServerOptions.find((option) => option.server.id === preselectServerId)
      : null;
    const preselect = requested?.server.id || (!preselectServerId && selectableServerOptions.length === 1 ? selectableServerOptions[0].server.id : '');
    if (preselectServerId && !requested) {
      setPageMessage('That source is not ready for monitors yet.');
    }
    setAddServerId(preselect);
    setAddStep('pick');
    return true;
  }

  function cancelAddFlow() {
    setAddStep('idle');
    setAddProbe(null);
    setAddSelectedTools({});
    setAddAgentState({ status: 'idle' });
    setAddCliStartActive(false);
    setAddServerId('');
  }

  useEffectTM(() => {
    if (!addParams.open) return;
    if (servers.loading && !servers.data) return;
    const key = `${addParams.serverId || ''}:${queryString || ''}`;
    if (handledAddLinkRef.current === key) return;
    handledAddLinkRef.current = key;
    const opened = startAddFlow(addParams.serverId);
    if (opened && typeof navigate === 'function') navigate('/tool-monitors', { replace: true });
  }, [addParams.open, addParams.serverId, queryString, servers.loading, servers.data, selectableServerOptions]);

  async function runAddAgentFill(serverId, tools) {
    if (!tools || tools.length === 0) return;
    setAddAgentState({ status: 'loading' });
    try {
      const data = await apiFetch(`/api/mcp-servers/${serverId}/agent-fill`, {
        method: 'POST',
        body: JSON.stringify({ tools }),
      });
      setAddSelectedTools((cur) => window.mergeAgentFills(cur, tools, data?.fills || {}));
      setAddAgentState({ status: 'ready' });
    } catch (err) {
      setAddAgentState({ status: 'error', error: err.message });
    }
  }

  function openAddWizardWithTools(serverId, tools, metadata = {}) {
    setAddProbe({ ok: true, tools, ...metadata });
    const existing = new Set(rows.filter((m) => m.mcp_server_id === serverId).map((m) => m.tool_name));
    const initial = metadata.source === 'cli'
      ? buildCliMonitorInitialToolStateTM(tools)
      : buildInitialToolState(tools);
    for (const name of Object.keys(initial)) {
      if (existing.has(name)) {
        initial[name] = { ...initial[name], checked: false, existing: true, bucket: 'existing' };
      }
    }
    setAddSelectedTools(initial);
    setAddStep('wizard');
    const fresh = agentFillToolsForAddTM(tools, existing, metadata.source);
    runAddAgentFill(serverId, fresh);
  }

  async function loadHostedMcpCatalogForAdd(serverId, server) {
    setAddStep('probing');
    setAddProbe(null);
    setAddSelectedTools({});
    setAddAgentState({ status: 'idle' });
    setAddCliStartActive(false);
    try {
      const detail = await apiFetch(`/api/mcp-servers/${serverId}`);
      const detailServer = detail?.mcpServer || server;
      const tools = catalogToolsForWizard(detail?.toolCatalog);
      if (tools.length > 0) {
        openAddWizardWithTools(serverId, tools, { cached: true });
        return;
      }
      setAddProbe({
        ok: false,
        tools: [],
        errorCode: 'hosted_catalog_empty',
        errorMessage: hostedCatalogUnavailableMessage(detailServer),
      });
      setAddStep('wizard');
    } catch (err) {
      setAddProbe({ ok: false, tools: [], errorCode: err.code, errorMessage: err.message });
      setAddStep('wizard');
    }
  }

  async function loadCliCommandsForAdd(serverId, server) {
    if (!canMonitorServerTM(server)) {
      setAddProbe({ ok: false, tools: [], errorMessage: `This CLI target is ${monitorServerUnavailableReasonTM(server)}.` });
      setAddStep('wizard');
      return;
    }
    const data = await apiFetch(`/api/mcp-servers/${serverId}/cli-command-reviews`);
    const commands = cliReviewRowsToMonitorToolsTM(data.rows || []);
    if (commands.length === 0) {
      setAddProbe({
        ok: false,
        tools: [],
        errorMessage: 'No CLI commands are available to monitor.',
      });
      setAddStep('wizard');
      return;
    }
    openAddWizardWithTools(serverId, commands, { source: 'cli', sectionMode: 'cliRisk' });
  }

  useEffectTM(() => {
    if (addStep === 'idle') return undefined;
    function onKey(event) {
      if (event.key === 'Escape' && !addSavingMonitors) cancelAddFlow();
    }
    document.addEventListener('keydown', onKey);
    return () => document.removeEventListener('keydown', onKey);
  }, [addStep, addSavingMonitors]);

  async function runAddProbe(serverId) {
    if (!requirePaidAction(auth, navigate, { requiredRole: 'editor' })) return;
    if (isHostedMcpServerRow(addSelectedServer)) {
      return loadHostedMcpCatalogForAdd(serverId, addSelectedServer);
    }
    setAddStep('probing');
    setAddProbe(null);
    setAddSelectedTools({});
    setAddAgentState({ status: 'idle' });
    try {
      const selectedServer = activeServers.find((server) => server.id === serverId);
      if (isCliTargetTM(selectedServer)) {
        await loadCliCommandsForAdd(serverId, selectedServer);
        return;
      }
      const probe = await apiFetch(`/api/mcp-servers/${serverId}/probe`, { method: 'POST' });
      setAddProbe(probe);
      if (probe?.ok && Array.isArray(probe.tools)) {
        openAddWizardWithTools(serverId, probe.tools);
      } else {
        setAddStep('wizard');
      }
    } catch (probeError) {
      setAddProbe({ ok: false, tools: [], errorCode: probeError.code, errorMessage: probeError.message });
      setAddStep('wizard');
    }
  }

  const passingCount = rows.filter((m) => m.last_status === 'pass').length;
  const failingCount = rows.filter((m) => m.last_status === 'fail' || m.last_status === 'error').length;
  const pausedCount = rows.filter((m) => !m.is_active).length;
  const totalCount = rows.length;

  const filtered = useMemoTM(() => {
    const term = search.trim().toLowerCase();
    return rows.filter((m) => {
      if (term && !m.tool_name.toLowerCase().includes(term) && !(m.server_name || '').toLowerCase().includes(term)) return false;
      if (filter === 'failing') {
        return m.last_status === 'fail' || m.last_status === 'error';
      }
      if (filter === 'passing') {
        return m.last_status === 'pass';
      }
      if (filter === 'paused') {
        return !m.is_active;
      }
      return true;
    });
  }, [rows, filter, search]);

  function setRowErrFor(id, msg) {
    setRowError((prev) => {
      const next = { ...prev };
      if (msg) next[id] = msg;
      else delete next[id];
      return next;
    });
  }

  async function runNow(m) {
    if (!requirePaidAction(auth, navigate, { requiredRole: 'editor' })) return;
    setBusyAction(m.id);
    setRowErrFor(m.id, '');
    try {
      await apiFetch(`/api/mcp-servers/${m.mcp_server_id}/tool-monitors/${m.id}/run-now`, { method: 'POST' });
      await monitors.reload();
      if (expandedId === m.id) await loadRunsFor(m);
    } catch (e) {
      setRowErrFor(m.id, e.message);
    } finally {
      setBusyAction(null);
    }
  }

  async function togglePause(m) {
    if (!requirePaidAction(auth, navigate, { requiredRole: 'editor' })) return;
    setBusyAction(m.id);
    setRowErrFor(m.id, '');
    try {
      await apiFetch(`/api/mcp-servers/${m.mcp_server_id}/tool-monitors/${m.id}`, {
        method: 'PATCH',
        body: JSON.stringify({ isActive: !m.is_active }),
      });
      await monitors.reload();
    } catch (e) {
      if (!m.is_active && e.code === 'cli_monitor_requires_test_or_override') {
        setConfirmStartMonitor(m);
        return;
      }
      setRowErrFor(m.id, e.message);
    } finally {
      setBusyAction(null);
    }
  }

  const [confirmDeleteMonitor, setConfirmDeleteMonitor] = useStateTM(null);
  const [confirmStartMonitor, setConfirmStartMonitor] = useStateTM(null);
  const [runDetail, setRunDetail] = useStateTM(/** @type {{ run: any, monitor: any } | null} */ (null));

  useEffectTM(() => {
    if (!runDetail) return undefined;
    function onKey(event) { if (event.key === 'Escape') setRunDetail(null); }
    document.addEventListener('keydown', onKey);
    return () => document.removeEventListener('keydown', onKey);
  }, [runDetail]);
  async function performDeleteMonitor(m) {
    if (!requirePaidAction(auth, navigate, { requiredRole: 'editor' })) return;
    setBusyAction(m.id);
    setRowErrFor(m.id, '');
    try {
      await apiFetch(`/api/mcp-servers/${m.mcp_server_id}/tool-monitors/${m.id}`, { method: 'DELETE' });
      await monitors.reload();
      toast.show({ tone: 'ok', title: 'Monitor deleted' });
    } catch (e) {
      setRowErrFor(m.id, e.message);
      toast.show({ tone: 'bad', title: 'Delete failed', description: e.message });
    } finally {
      setBusyAction(null);
      setConfirmDeleteMonitor(null);
    }
  }

  async function performOverrideStartMonitor(m) {
    if (!m || !requirePaidAction(auth, navigate, { requiredRole: 'editor' })) return;
    setBusyAction(m.id);
    setRowErrFor(m.id, '');
    try {
      await apiFetch(`/api/mcp-servers/${m.mcp_server_id}/tool-monitors/${m.id}`, {
        method: 'PATCH',
        body: JSON.stringify({ isActive: true, activationOverride: true }),
      });
      setConfirmStartMonitor(null);
      await monitors.reload();
    } catch (e) {
      setRowErrFor(m.id, e.message);
    } finally {
      setBusyAction(null);
    }
  }

  function openEdit(m) {
    if (!canManageToolMonitors) return;
    if (editingId === m.id) { closeEdit(); return; }
    setEditingId(m.id);
    setEditError('');
    setEditDraft(buildEditDraftFromMonitor(m));
  }

  function closeEdit() {
    setEditingId(null);
    setEditDraft(null);
    setEditError('');
  }

  async function saveEdit(m) {
    if (!requirePaidAction(auth, navigate, { requiredRole: 'editor' })) return;
    if (!editDraft) return;
    setEditError('');

    // JSON-fallback path: schema unavailable, user edits raw JSON.
    if (!editDraft.schemaAvailable) {
      let parsedArgs;
      try {
        parsedArgs = editDraft.argsRaw.trim() ? JSON.parse(editDraft.argsRaw) : {};
        if (!parsedArgs || typeof parsedArgs !== 'object' || Array.isArray(parsedArgs)) {
          throw new Error('Arguments must be a JSON object');
        }
      } catch (e) {
        setEditError(`Invalid JSON: ${e.message}`);
        return;
      }
      setEditSaving(true);
      try {
        await apiFetch(`/api/mcp-servers/${m.mcp_server_id}/tool-monitors/${m.id}`, {
          method: 'PATCH',
          body: JSON.stringify({ intervalSeconds: editDraft.interval, arguments: parsedArgs }),
        });
        await monitors.reload();
        closeEdit();
      } catch (e) {
        setEditError(e.message);
      } finally {
        setEditSaving(false);
      }
      return;
    }

    // Structured-form path: validate per-field, then build args.
    const tool = { name: m.tool_name, inputSchema: editDraft.inputSchema };
    const cfg = {
      checked: true,
      values: editDraft.values,
      extras: editDraft.extras,
      errors: {},
      ignoredFields: {},
    };
    const validation = validateAllSelectedTools([tool], { [tool.name]: cfg });
    if (validation.hasErrors) {
      setEditDraft({ ...editDraft, errors: validation.next[tool.name].errors || {} });
      return;
    }
    const argsObj = buildArgsFromState(tool, validation.next[tool.name]);

    setEditSaving(true);
    try {
      await apiFetch(`/api/mcp-servers/${m.mcp_server_id}/tool-monitors/${m.id}`, {
        method: 'PATCH',
        body: JSON.stringify({ intervalSeconds: editDraft.interval, arguments: argsObj }),
      });
      await monitors.reload();
      closeEdit();
    } catch (e) {
      setEditError(e.message);
    } finally {
      setEditSaving(false);
    }
  }

  function setEditValue(key, next) {
    setEditDraft((prev) => prev && ({
      ...prev,
      values: { ...prev.values, [key]: next },
      errors: { ...prev.errors, [key]: null },
    }));
  }

  function updateEditExtra(idx, patch) {
    setEditDraft((prev) => {
      if (!prev) return prev;
      const extras = prev.extras.slice();
      extras[idx] = { ...extras[idx], ...patch };
      const errors = { ...prev.errors };
      delete errors[`__extra_${idx}_key`];
      return { ...prev, extras, errors };
    });
  }

  function removeEditExtra(idx) {
    setEditDraft((prev) => {
      if (!prev) return prev;
      const extras = prev.extras.filter((_, i) => i !== idx);
      const errors = { ...(prev.errors || {}) };
      // Error keys are indexed by extras position (e.g. __extra_2_key). Removing
      // index `idx` shifts every later extra down by one — drop the removed
      // entry and rewrite higher indices so error display stays attached to
      // the correct row.
      for (const key of Object.keys(errors)) {
        const m = key.match(/^__extra_(\d+)_key$/);
        if (!m) continue;
        const i = Number(m[1]);
        if (i === idx) {
          delete errors[key];
        } else if (i > idx) {
          errors[`__extra_${i - 1}_key`] = errors[key];
          delete errors[key];
        }
      }
      return { ...prev, extras, errors };
    });
  }

  function addEditExtra() {
    setEditDraft((prev) => prev && ({ ...prev, extras: [...prev.extras, { key: '', value: '' }] }));
  }

  return (
    <div className="page-inner tool-monitors-page">
      <div className="ui-page-head">
        <h1 className="ui-page-title">Monitors</h1>
        <div className="ui-page-actions">
          <Button size="sm" variant="ghost" onClick={() => monitors.reload()}>
            <Icon name="refresh" size={13} />Refresh
          </Button>
        </div>
      </div>

      {pageMessage && <div className="auth-message alerts-message">{pageMessage}</div>}

      {loadError && (
        <div className="card tool-monitors-error" style={{ marginBottom: 12 }}>
          <div className="row" style={{ gap: 10, padding: '12px 14px', alignItems: 'flex-start' }}>
            <Icon name="alert" size={16} />
            <div className="col" style={{ gap: 2, flex: 1 }}>
              <div style={{ fontWeight: 600, fontSize: 13 }}>Couldn’t load tool monitors</div>
              <div className="text-xs muted">{loadError.message || 'The /api/tool-monitors endpoint returned an error.'}</div>
            </div>
            <button className="btn btn-sm" onClick={() => monitors.reload()}>
              <Icon name="refresh" size={12} />Retry
            </button>
          </div>
        </div>
      )}

      <ToolMonitorsHeroStats
        total={totalCount}
        passing={passingCount}
        failing={failingCount}
        paused={pausedCount}
        loading={isInitialLoading}
      />

      <div className="tool-monitors-filterbar">
        <div className="seg" role="tablist" aria-label="Filter monitors">
          <button type="button" className={filter === 'all' ? 'active' : ''} onClick={() => setFilter('all')}>
            All <span className="muted text-xs">{totalCount}</span>
          </button>
          <button type="button" className={filter === 'passing' ? 'active' : ''} onClick={() => setFilter('passing')}>
            Passing <span className="muted text-xs">{passingCount}</span>
          </button>
          <button type="button" className={filter === 'failing' ? 'active' : ''} onClick={() => setFilter('failing')}>
            Failing <span className="muted text-xs">{failingCount}</span>
          </button>
          <button type="button" className={filter === 'paused' ? 'active' : ''} onClick={() => setFilter('paused')}>
            Paused <span className="muted text-xs">{pausedCount}</span>
          </button>
        </div>
        <div className="search-input tool-monitors-search">
          <Icon name="search" size={14} />
          <input
            placeholder="Search by tool or server"
            value={search}
            onChange={(e) => setSearch(e.target.value)}
          />
        </div>
      </div>

      {addStep !== 'idle' && (
        <div className="dialog-backdrop" onMouseDown={(e) => { if (e.target === e.currentTarget && !addSavingMonitors) cancelAddFlow(); }}>
        <div className="modal-panel tool-wizard-modal" role="dialog" aria-modal="true" aria-label="Add tool monitor">
        <div className="modal-header">
          <div>
            <div className="modal-title">Add tool monitor</div>
            <div className="modal-subtitle">Pick a source, load available tools or approved commands, choose what to monitor.</div>
          </div>
          <button className="icon-btn" type="button" aria-label="Close" onClick={cancelAddFlow} disabled={addSavingMonitors}>
            <Icon name="x" size={15} />
          </button>
        </div>
        <div className="modal-body tool-wizard-modal-body">
          {addStep === 'pick' && (
            <div className="col" style={{ gap: 10 }}>
              {activeServers.length === 0 ? (
                <div className="text-xs muted">No active MCP servers. Add one first.</div>
              ) : (
                <div className="row" style={{ gap: 8, flexWrap: 'wrap', alignItems: 'center' }}>
                  <Select
                    className="tool-monitors-source-select"
                    value={addServerId}
                    options={sourcePickerOptions}
                    onChange={setAddServerId}
                    portal
                    placeholder="Select a source..."
                    ariaLabel="Monitor source"
                    searchable={sourcePickerOptions.length > 6}
                    disabled={sourcePickerOptions.length === 0}
                  />
                  <button
                    className="btn btn-primary btn-sm"
                    disabled={!canManageToolMonitors || !addServerId}
                    onClick={() => runAddProbe(addServerId)}>
                    <Icon name="search" size={12} />{addPrimaryActionLabel}
                  </button>
                  <button className="btn btn-sm" onClick={cancelAddFlow}>Cancel</button>
                </div>
              )}
              {activeServers.length > 0 && selectableServerOptions.length === 0 && (
                <div className="auth-message">
                  <Icon name="clock" size={12} /> No sources are ready for monitors yet.
                </div>
              )}
              {serverOptions.some((option) => !option.canMonitor) && (
                <div className="text-xs muted">
                  CLI targets become selectable after package installation, command discovery, and command review.
                </div>
              )}
            </div>
          )}

          {addStep === 'probing' && <ToolWizardLoading phase="probe" />}

          {addStep === 'wizard' && addProbe && !addProbe.ok && (
            <div className="tool-wizard-error">
              <div className="row" style={{ gap: 8, alignItems: 'flex-start' }}>
                <Icon name="alert" size={16} />
                <div className="col" style={{ gap: 4, flex: 1 }}>
                  <div style={{ fontWeight: 600 }}>
                    {addProbe.errorCode?.startsWith('hosted_') ? 'Hosted tool catalog is not ready' : 'Could not load monitor choices'}
                  </div>
                  <div className="text-xs">{addProbe.errorMessage || 'Unknown error'}</div>
                </div>
                <button
                  className="btn btn-sm"
                  disabled={!canManageToolMonitors}
                  title={canManageToolMonitors ? undefined : toolMonitorActionDisabledReason}
                  onClick={() => runAddProbe(addServerId)}>
                  <Icon name="refresh" size={12} />Retry
                </button>
                <button className="btn btn-sm btn-ghost" onClick={cancelAddFlow}>Cancel</button>
              </div>
            </div>
          )}

          {addStep === 'wizard' && addProbe?.ok && (
            <>
              {addProbe.source === 'cli' && (
                <div className="tool-monitor-cli-activation-note">
                  <div className="text-sm">Read-only commands start active. Side-effecting and unknown-risk commands start paused.</div>
                  <label className="check-row text-xs">
                    <input
                      type="checkbox"
                      checked={addCliStartActive}
                      onChange={(event) => setAddCliStartActive(event.target.checked)}
                    />
                    Start without a test run
                  </label>
                </div>
              )}
              <ToolMonitorWizard
                tools={addProbe.tools}
                selectedTools={addSelectedTools}
                setSelectedTools={setAddSelectedTools}
                saving={addSavingMonitors}
                sectionMode={addProbe.sectionMode}
                onSave={async () => {
                  if (!requirePaidAction(auth, navigate, { requiredRole: 'editor' })) return;
                  const validation = validateAllSelectedTools(addProbe.tools, addSelectedTools);
                  if (validation.hasErrors) {
                    setAddSelectedTools(validation.next);
                    return;
                  }
                  const picked = addProbe.tools.filter((t) => {
                    const c = validation.next[t.name];
                    return c?.checked && !c?.existing;
                  });
                  const limitState = getPlanLimitState(auth, 'toolMonitors', picked.length);
                  if (!limitState.allowed) {
                    toast.show({ tone: 'bad', title: 'Plan limit reached', description: limitState.message });
                    return;
                  }
                  setAddSavingMonitors(true);
                  let savedCount = 0;
                  let failedAny = false;
                  const nextState = { ...validation.next };
                  try {
                    for (const tool of picked) {
                      const cfg = nextState[tool.name];
                      const argsObj = buildArgsFromState(tool, cfg);
                      try {
                        await apiFetch(`/api/mcp-servers/${addServerId}/tool-monitors`, {
                          method: 'POST',
                          body: JSON.stringify({
                            toolName: tool.name,
                            intervalSeconds: cfg.interval,
                            arguments: argsObj,
                            inputSchema: tool.inputSchema,
                            ...(addProbe.source === 'cli'
                              ? { startActive: addCliStartActive === true }
                              : { startActive: true }),
                          }),
                        });
                        nextState[tool.name] = { ...cfg, saved: true, saveError: null };
                        savedCount += 1;
                      } catch (perToolError) {
                        failedAny = true;
                        nextState[tool.name] = { ...cfg, saveError: perToolError.message };
                      }
                    }
                    setAddSelectedTools(nextState);
                    if (!failedAny) {
                      cancelAddFlow();
                      setPageMessage(`Saved ${savedCount} tool monitor${savedCount === 1 ? '' : 's'}.`);
                      await monitors.reload();
                    } else {
                      setPageMessage(`Saved ${savedCount} of ${picked.length} monitors. Resolve errors below and retry.`);
                      await monitors.reload();
                    }
                  } finally {
                    setAddSavingMonitors(false);
                  }
                }}
                onCancel={cancelAddFlow}
                agentState={addAgentState}
                onAgentRetry={() => {
                  const existing = new Set(rows.filter((m) => m.mcp_server_id === addServerId).map((m) => m.tool_name));
                  const fresh = agentFillToolsForAddTM(addProbe?.tools || [], existing, addProbe?.source);
                  runAddAgentFill(addServerId, fresh);
                }}
              />
            </>
          )}
        </div>
        </div>
        </div>
      )}

      {isInitialLoading ? (
        <div className="card">
          <ul className="tool-monitors-list">
            {Array.from({ length: 4 }).map((_, i) => (
              <li key={i} className="tool-monitors-row loading-row">
                <span className="skel skel-badge"></span>
                <span className="skel skel-long"></span>
                <span className="skel skel-mid"></span>
                <span className="skel skel-short"></span>
              </li>
            ))}
          </ul>
        </div>
      ) : !loadError && totalCount === 0 ? (
        <div className="card">
          <EmptyState
            icon="zap"
            title="No tool monitors yet"
            body="Add an MCP server and pick the tools you want pinged on a schedule."
            action={
              activeServers.length === 0 ? (
                <button className="btn btn-primary btn-sm" onClick={() => navigate('/sources/mcp-servers')}>
                  <Icon name="mcp" size={12} />Add an MCP server to get started
                </button>
              ) : (
                <button
                  className="btn btn-primary btn-sm"
                  disabled={!canManageToolMonitors || addStep !== 'idle' || !toolMonitorCreateLimitState.allowed}
                  onClick={() => startAddFlow()}
                  title={!canManageToolMonitors ? toolMonitorActionDisabledReason : !toolMonitorCreateLimitState.allowed ? toolMonitorCreateLimitState.message : undefined}>
                  <Icon name="plus" size={12} />Add a tool monitor
                </button>
              )
            }
          />
        </div>
      ) : (
        <div className="card">
          <div className="tool-monitors-list-toolbar">
            <Button
              variant="primary"
              size="sm"
              disabled={!canManageToolMonitors || addStep !== 'idle' || activeServers.length === 0 || !toolMonitorCreateLimitState.allowed}
              onClick={() => startAddFlow()}
              title={!canManageToolMonitors ? toolMonitorActionDisabledReason : !toolMonitorCreateLimitState.allowed ? toolMonitorCreateLimitState.message : activeServers.length === 0 ? 'Add a server first' : 'Add a tool monitor'}>
              <Icon name="plus" size={12} />New monitor
            </Button>
          </div>
          {filtered.length === 0 ? (
            <EmptyState
              icon="search"
              title={loadError && totalCount === 0 ? "Couldn't load monitors" : 'No monitors match your filters'}
              body={loadError && totalCount === 0 ? 'Retry above, or add a new monitor.' : 'Try clearing the search or switching to All.'}
            />
          ) : (
          <div className="runs-table-wrap tool-monitors-table-wrap">
            <table className="runs-table tool-monitors-table">
              <thead>
                <tr>
                  <th className="col-status">Status</th>
                  <th>Monitor</th>
                  <th>Server</th>
                  <th>Cadence</th>
                  <th>Last run</th>
                  <th>Next run</th>
                  <th>Alerts</th>
                  <th className="col-actions">Actions</th>
                </tr>
              </thead>
              <tbody>
                {filtered.map((m) => {
                  const errMsg = rowError[m.id];
                  const isEditing = editingId === m.id;
                  const lastErr = m.last_error_message || '';
                  const status = m.last_status || 'pending';
                  const isBusy = busyAction === m.id;
                  const stuck = isMonitorStuck(m);
                  const stuckTooltip = stuck ? monitorStuckTooltip(m) : '';
                  const targetKind = getTargetKind(m);
                  const serverName = m.server_name || (targetKind === 'cli' ? 'CLI target' : 'MCP server');
                  return (
                    <React.Fragment key={m.id}>
                      <tr className={`runs-row tool-monitors-table-row ${isEditing ? 'is-editing' : ''}`}>
                        <td className="col-status">
                          <span className="runs-status-cell">
                            <span className={`badge ${monitorStatusBadgeClassTM(status)} tool-monitors-status`}>
                              <span className="dot"></span>{formatMonitorRunStatus(status)}
                            </span>
                            {stuck && (
                              <span
                                className="badge badge-pending ui-tooltip tool-monitors-stuck"
                                role="status"
                                data-tooltip={stuckTooltip}
                                tabIndex={0}
                                aria-label={stuckTooltip}>
                                <Icon name="alert" size={10} />Stuck
                              </span>
                            )}
                          </span>
                        </td>
                        <td className="tool-monitors-monitor-cell ui-tooltip" data-tooltip={m.tool_name} tabIndex={0}>
                          <span className="tool-monitors-toolname mono" title={m.tool_name}>{m.tool_name}</span>
                        </td>
                        <td className="tool-monitors-server-cell ui-tooltip" data-tooltip={serverName} tabIndex={0}>
                          <span className="badge badge-neutral tool-monitors-server-tag">
                            <Icon name={targetKind === 'cli' ? 'terminal' : 'mcp'} size={10} />
                            <span className="tool-monitors-server-name" title={serverName}>{serverName}</span>
                          </span>
                        </td>
                        <td className="muted">
                          every {formatMonitorInterval(m.interval_seconds)}
                          {!m.is_active && <span> · paused</span>}
                        </td>
                        <td className="muted">
                          {m.last_run_at ? formatRelative(m.last_run_at) : 'never'}
                          {typeof m.last_latency_ms === 'number' && (
                            <span> · {formatDuration(m.last_latency_ms)}</span>
                          )}
                        </td>
                        <td className="muted">{m.next_run_at ? formatRelative(m.next_run_at) : '—'}</td>
                        <td>
                          <AlertRulesButton
                            rules={alertsByMonitor[m.id] || []}
                            loading={alerts.loading && !alerts.data}
                            onOpen={() => openManageAlertsFor(m)}
                          />
                        </td>
                        <td className="col-actions">
                          <div className="tool-monitors-actions">
                            <button
                              className={`btn btn-sm ${expandedId === m.id ? 'btn-primary' : ''}`}
                              disabled={isBusy}
                              onClick={() => toggleExpand(m)}
                              title="Show recent runs"
                              aria-label="Show recent runs"
                              aria-expanded={expandedId === m.id}>
                              <Icon name="clock" size={11} />History
                            </button>
                            <button
                              className="btn btn-sm"
                              disabled={!canManageToolMonitors || isBusy}
                              onClick={() => runNow(m)}
                              title={canManageToolMonitors ? 'Run now' : toolMonitorActionDisabledReason}
                              aria-label="Run now">
                              <Icon name="play" size={11} />Run
                            </button>
                            <button
                              className="btn btn-sm"
                              disabled={!canManageToolMonitors || isBusy}
                              onClick={() => togglePause(m)}
                              title={canManageToolMonitors ? (m.is_active ? 'Pause' : 'Resume') : toolMonitorActionDisabledReason}
                              aria-label={m.is_active ? 'Pause monitor' : 'Resume monitor'}>
                              <Icon name={m.is_active ? 'pause' : 'play'} size={11} />{m.is_active ? 'Pause' : 'Resume'}
                            </button>
                            <button
                              className={`btn btn-sm ${isEditing ? 'btn-primary' : ''}`}
                              disabled={!canManageToolMonitors || isBusy}
                              onClick={() => openEdit(m)}
                              title={canManageToolMonitors ? 'Edit' : toolMonitorActionDisabledReason}
                              aria-label="Edit monitor">
                              <Icon name="edit" size={11} />Edit
                            </button>
                            <button
                              className="btn btn-sm btn-icon"
                              disabled={!canManageToolMonitors || isBusy}
                              onClick={() => setConfirmDeleteMonitor(m)}
                              title={canManageToolMonitors ? 'Delete monitor' : toolMonitorActionDisabledReason}
                              aria-label="Delete monitor">
                              <Icon name="trash" size={12} />
                            </button>
                          </div>
                        </td>
                      </tr>
                      {((lastErr && status !== 'pass') || errMsg) && (
                        <tr className="tool-monitors-table-detail">
                          <td colSpan={TOOL_MONITORS_TABLE_COLUMN_COUNT}>
                            {lastErr && status !== 'pass' && (
                              <div className="tool-monitors-row-error" title={lastErr}>
                                <Icon name="alert" size={11} />
                                <span className="text-xs">{lastErr.slice(0, 140)}{lastErr.length > 140 ? '…' : ''}</span>
                              </div>
                            )}
                            {errMsg && (
                              <div className="tool-monitors-row-error" title={errMsg}>
                                <Icon name="alert" size={11} />
                                <span className="text-xs">{errMsg}</span>
                              </div>
                            )}
                          </td>
                        </tr>
                      )}
                      {expandedId === m.id && (
                        <tr className="tool-monitors-table-detail">
                          <td colSpan={TOOL_MONITORS_TABLE_COLUMN_COUNT}>
                            <div className="tool-monitors-runs">
                              <div className="row" style={{ justifyContent: 'space-between', alignItems: 'center', marginBottom: 6 }}>
                                <span className="field-label">Recent runs (last 20)</span>
                                <button
                                  className="btn btn-sm"
                                  onClick={() => loadRunsFor(m)}
                                  disabled={runsByMonitor[m.id]?.loading}>
                                  <Icon name="refresh" size={11} />Refresh
                                </button>
                              </div>
                              {runsByMonitor[m.id]?.loading && (
                                <div className="text-xs muted">Loading runs…</div>
                              )}
                              {runsByMonitor[m.id]?.error && (
                                <div className="auth-message error text-xs">{runsByMonitor[m.id].error}</div>
                              )}
                              {!runsByMonitor[m.id]?.loading
                                && Array.isArray(runsByMonitor[m.id]?.rows)
                                && runsByMonitor[m.id].rows.length === 0 && (
                                <div className="text-xs muted">No runs yet.</div>
                              )}
                              {!runsByMonitor[m.id]?.loading
                                && Array.isArray(runsByMonitor[m.id]?.rows)
                                && runsByMonitor[m.id].rows.length > 0 && (
                                <div className="runs-table-wrap tool-monitors-runs-table-wrap">
                                  <table className="runs-table tool-monitors-runs-table">
                                    <thead>
                                      <tr>
                                        <th className="col-status">Status</th>
                                        <th className="col-id">Run</th>
                                        <th>Started</th>
                                        <th className="num">Latency</th>
                                        <th>Error</th>
                                      </tr>
                                    </thead>
                                    <tbody>
                                      {runsByMonitor[m.id].rows.map((run) => {
                                        const errorMessage = run.error_message || '';
                                        return (
                                          <tr
                                            key={run.id}
                                            className="runs-row tool-monitors-runs-row"
                                            tabIndex={0}
                                            onClick={() => setRunDetail({ run, monitor: m })}
                                            onKeyDown={(e) => {
                                              if (e.key === 'Enter' || e.key === ' ') { e.preventDefault(); setRunDetail({ run, monitor: m }); }
                                            }}
                                            aria-label={`View details for run ${formatRelative(run.started_at)}`}
                                            title="View run details">
                                            <td className="col-status">
                                              <span className="runs-status-cell">
                                                <span className={`badge ${monitorStatusBadgeClassTM(run.status)}`}>
                                                  <span className="dot"></span>{formatMonitorRunStatus(run.status)}
                                                </span>
                                              </span>
                                            </td>
                                            <td className="col-id mono">{shortId(run.id)}</td>
                                            <td className="muted" title={run.started_at}>
                                              {formatRelative(run.started_at)}
                                            </td>
                                            <td className="num">
                                              {typeof run.latency_ms === 'number' ? formatDuration(run.latency_ms) : '—'}
                                            </td>
                                            <td className="tool-monitors-run-error muted" title={errorMessage}>
                                              {errorMessage
                                                ? `${errorMessage.slice(0, 96)}${errorMessage.length > 96 ? '…' : ''}`
                                                : '—'}
                                            </td>
                                          </tr>
                                        );
                                      })}
                                    </tbody>
                                  </table>
                                </div>
                              )}
                            </div>
                          </td>
                        </tr>
                      )}
                      {isEditing && editDraft && (
                        <tr className="tool-monitors-table-detail">
                          <td colSpan={TOOL_MONITORS_TABLE_COLUMN_COUNT}>
                            <div className="tool-monitors-edit tool-monitor-row-body">
                              <div className="row" style={{ gap: 10, alignItems: 'center', flexWrap: 'wrap' }}>
                                <span className="field-label" style={{ minWidth: 80 }}>Cadence</span>
                                <Select
                                  value={editDraft.interval}
                                  onChange={(value) => setEditDraft({ ...editDraft, interval: Number(value) })}
                                  className="tool-monitors-edit-cadence-select"
                                  options={[
                                    { value: 300, label: 'Every 5 minutes' },
                                    { value: 900, label: 'Every 15 minutes' },
                                    { value: 3600, label: 'Every hour' },
                                  ]} />
                              </div>

                              {editDraft.schemaAvailable ? (
                                <>
                                  {Object.entries(editDraft.inputSchema?.properties || {}).map(([key, fieldSchema]) => {
                                    const required = Array.isArray(editDraft.inputSchema?.required)
                                      && editDraft.inputSchema.required.includes(key);
                                    return (
                                      <ToolArgField
                                        key={key}
                                        fieldKey={key}
                                        schema={fieldSchema}
                                        value={editDraft.values?.[key]}
                                        error={editDraft.errors?.[key]}
                                        isRequired={required}
                                        onChange={(v) => setEditValue(key, v)}
                                      />
                                    );
                                  })}

                                  {editDraft.extras.length > 0 && (
                                    <div className="tool-monitor-extras">
                                      {editDraft.extras.map((row, idx) => (
                                        <div key={idx} className="row" style={{ gap: 6, alignItems: 'flex-start' }}>
                                          <div className="col" style={{ flex: '0 0 200px', gap: 2 }}>
                                            <input
                                              className="input mono text-xs"
                                              placeholder="key"
                                              value={row.key}
                                              onChange={(e) => updateEditExtra(idx, { key: e.target.value })}
                                            />
                                            {editDraft.errors?.[`__extra_${idx}_key`] && (
                                              <div className="text-xs error-text">{editDraft.errors[`__extra_${idx}_key`]}</div>
                                            )}
                                          </div>
                                          <input
                                            className="input mono text-xs"
                                            placeholder="value (string, or JSON literal)"
                                            value={row.value}
                                            onChange={(e) => updateEditExtra(idx, { value: e.target.value })}
                                            style={{ flex: 1 }}
                                          />
                                          <button className="btn btn-sm btn-ghost" onClick={() => removeEditExtra(idx)} aria-label="Remove field">
                                            <Icon name="x" size={12} />
                                          </button>
                                        </div>
                                      ))}
                                    </div>
                                  )}
                                  <button className="btn btn-sm btn-ghost tool-monitor-add-extra" onClick={addEditExtra}>
                                    <Icon name="plus" size={12} />Add another argument
                                  </button>
                                </>
                              ) : (
                                <div className="col" style={{ gap: 4, marginTop: 8 }}>
                                  <span className="field-label">Arguments (JSON)</span>
                                  <div className="text-xs muted" style={{ marginBottom: 4 }}>
                                    Schema unavailable for this tool — re-probe the server to get a structured form.
                                  </div>
                                  <textarea
                                    rows={5}
                                    className="textarea mono text-xs"
                                    value={editDraft.argsRaw}
                                    onChange={(e) => setEditDraft({ ...editDraft, argsRaw: e.target.value })}
                                  />
                                </div>
                              )}

                              {editError && <div className="auth-message error text-xs" style={{ marginTop: 6 }}>{editError}</div>}
                              <div className="row" style={{ gap: 8, marginTop: 10 }}>
                                <button
                                  className="btn btn-primary btn-sm"
                                  disabled={!canManageToolMonitors || editSaving}
                                  title={canManageToolMonitors ? undefined : toolMonitorActionDisabledReason}
                                  onClick={() => saveEdit(m)}>
                                  {editSaving ? 'Saving…' : 'Save changes'}
                                </button>
                                <button className="btn btn-sm" disabled={editSaving} onClick={closeEdit}>Cancel</button>
                              </div>
                            </div>
                          </td>
                        </tr>
                      )}
                    </React.Fragment>
                  );
                })}
              </tbody>
            </table>
          </div>
          )}
        </div>
      )}

      <ConfirmDialog
        open={Boolean(confirmDeleteMonitor)}
        tone="danger"
        title={confirmDeleteMonitor ? `Delete monitor for ${confirmDeleteMonitor.tool_name}?` : ''}
        description="The monitor stops running. Past run history is preserved on the server."
        confirmLabel="Delete monitor"
        confirmBusyLabel="Deleting"
        busy={Boolean(confirmDeleteMonitor) && busyAction === confirmDeleteMonitor.id}
        onCancel={() => setConfirmDeleteMonitor(null)}
        onConfirm={() => confirmDeleteMonitor && performDeleteMonitor(confirmDeleteMonitor)} />

      <ConfirmDialog
        open={Boolean(confirmStartMonitor)}
        title={confirmStartMonitor ? `Start ${confirmStartMonitor.tool_name} without a passing test run?` : ''}
        description="This CLI command may run on schedule before Armature has seen it pass manually."
        confirmLabel="Start active"
        confirmBusyLabel="Starting"
        tone="danger"
        busy={Boolean(confirmStartMonitor) && busyAction === confirmStartMonitor.id}
        onCancel={() => setConfirmStartMonitor(null)}
        onConfirm={() => performOverrideStartMonitor(confirmStartMonitor)}
      />

      {runDetail && (
        <RunDetailModal
          run={runDetail.run}
          monitor={runDetail.monitor}
          onClose={() => setRunDetail(null)}
        />
      )}

      <AlertRulesForTargetModal
        open={Boolean(manageAlertsMonitor)}
        onClose={() => setManageAlertsMonitor(null)}
        targetKind="tool-monitor"
        targetId={manageAlertsMonitor?.id || ''}
        targetName={manageAlertsMonitor?.tool_name || 'tool monitor'}
        alertsResource={alerts}
        navigate={navigate} />
    </div>
  );
}

function RunDetailModal({ run, monitor, onClose }) {
  const toast = useToast();
  const summary = run.result_summary;
  const formattedSummary = formatRunResult(summary);
  const [copied, setCopied] = React.useState(false);
  const closeButtonRef = React.useRef(null);
  const onCloseRef = React.useRef(onClose);
  React.useLayoutEffect(() => { onCloseRef.current = onClose; }, [onClose]);

  React.useEffect(() => {
    const previousActive = document.activeElement;
    function onKey(e) {
      if (e.key === 'Escape') { e.stopPropagation(); onCloseRef.current?.(); }
    }
    document.addEventListener('keydown', onKey);
    const focusFrame = window.requestAnimationFrame(() => closeButtonRef.current?.focus());
    return () => {
      document.removeEventListener('keydown', onKey);
      window.cancelAnimationFrame(focusFrame);
      if (previousActive instanceof HTMLElement && previousActive.isConnected) previousActive.focus();
    };
  }, []);

  async function copyResponse() {
    if (!formattedSummary) return;
    try {
      await navigator.clipboard.writeText(formattedSummary);
      setCopied(true);
      window.setTimeout(() => setCopied(false), 1500);
    } catch (err) {
      toast.show({ tone: 'bad', title: 'Copy failed', description: err.message });
    }
  }

  return (
    <div className="dialog-backdrop" onMouseDown={(e) => { if (e.target === e.currentTarget) onClose(); }}>
      <div className="modal-panel run-detail-modal" role="dialog" aria-modal="true" aria-label="Run details">
        <div className="modal-header">
          <div>
            <div className="modal-title">Run details</div>
            <div className="modal-subtitle mono">{monitor?.tool_name}</div>
          </div>
          <button ref={closeButtonRef} className="icon-btn" type="button" aria-label="Close" onClick={onClose}>
            <Icon name="x" size={15} />
          </button>
        </div>
        <div className="modal-body">
          <div className="tm-run-detail-meta">
            <div className="tm-run-detail-meta-row">
              <span className="field-label">Status</span>
              <span className={`badge ${monitorStatusBadgeClassTM(run.status)}`}>{run.status}</span>
            </div>
            <div className="tm-run-detail-meta-row">
              <span className="field-label">Started</span>
              <span className="text-xs">{formatDateTime(run.started_at)} <span className="muted">({formatRelative(run.started_at)})</span></span>
            </div>
            {run.finished_at && (
              <div className="tm-run-detail-meta-row">
                <span className="field-label">Finished</span>
                <span className="text-xs">{formatDateTime(run.finished_at)}</span>
              </div>
            )}
            <div className="tm-run-detail-meta-row">
              <span className="field-label">Latency</span>
              <span className="text-xs">{typeof run.latency_ms === 'number' ? `${run.latency_ms}ms` : '—'}</span>
            </div>
            {(run.error_code || run.error_message) && (
              <div className="tm-run-detail-meta-row">
                <span className="field-label">Error</span>
                <div className="col" style={{ gap: 2, flex: 1 }}>
                  {run.error_code && <div className="text-xs mono">{run.error_code}</div>}
                  {run.error_message && <div className="text-xs error-text">{run.error_message}</div>}
                </div>
              </div>
            )}
          </div>
          <div className="col" style={{ gap: 6, marginTop: 12 }}>
            <div className="row" style={{ justifyContent: 'space-between', alignItems: 'center' }}>
              <span className="field-label">Response</span>
              {summary && (
                <button className="btn btn-sm" type="button" onClick={copyResponse} title="Copy full response">
                  <Icon name={copied ? 'check' : 'copy'} size={11} />{copied ? 'Copied' : 'Copy full response'}
                </button>
              )}
            </div>
            {summary ? (
              <pre className="run-detail-response">{formattedSummary}</pre>
            ) : (
              <div className="text-xs muted">No response captured for this run.</div>
            )}
          </div>
        </div>
      </div>
    </div>
  );
}

function formatRunResult(summary) {
  if (summary == null) return '';
  if (typeof summary !== 'string') {
    try { return JSON.stringify(summary, null, 2); } catch { return String(summary); }
  }
  // Pretty-print when the server stored a JSON string; leave plain text alone.
  const trimmed = summary.trim();
  if (trimmed.startsWith('{') || trimmed.startsWith('[')) {
    try { return JSON.stringify(JSON.parse(trimmed), null, 2); } catch { /* fall through */ }
  }
  return summary;
}

function ToolMonitorsHeroStats({ total, passing, failing, paused, loading }) {
  const allHealthy = total > 0 && failing === 0 && paused === 0;
  const heroTone = loading
    ? 'loading'
    : total === 0
      ? 'empty'
      : failing > 0
        ? 'alert'
        : allHealthy
          ? 'ok'
          : 'mixed';
  const heroLabel = loading
    ? 'Loading…'
    : total === 0
      ? 'No monitors yet'
      : failing > 0
        ? `${failing} monitor${failing === 1 ? '' : 's'} failing`
        : paused > 0 && passing === 0
          ? 'All monitors paused'
          : 'All monitors healthy';
  const heroIcon = loading
    ? 'refresh'
    : failing > 0
      ? 'alert'
      : total === 0
        ? 'zap'
        : 'check';
  return (
    <div className={`tm-hero tm-hero-${heroTone}`}>
      <div className="tm-hero-headline">
        <span className="tm-hero-icon"><Icon name={heroIcon} size={18} /></span>
        <div>
          <div className="tm-hero-title">{heroLabel}</div>
          <div className="tm-hero-sub">
            {loading ? <span className="skel skel-mid"></span> : `${total} monitor${total === 1 ? '' : 's'} configured`}
          </div>
        </div>
      </div>
      <div className="tm-hero-stats">
        <HeroStat label="Passing" value={loading ? null : passing} tone="ok" />
        <HeroStat label="Failing" value={loading ? null : failing} tone={failing > 0 ? 'alert' : 'muted'} />
        <HeroStat label="Paused" value={loading ? null : paused} tone="muted" />
      </div>
    </div>
  );
}

function HeroStat({ label, value, tone }) {
  return (
    <div className={`tm-stat tm-stat-${tone}`}>
      <div className="tm-stat-value">{value === null ? <span className="skel skel-short"></span> : value}</div>
      <div className="tm-stat-label">{label}</div>
    </div>
  );
}

function monitorStatusBadgeClassTM(status) {
  if (status === 'pass') return 'badge-success';
  if (status === 'fail') return 'badge-fail';
  if (status === 'error') return 'badge-fail';
  return 'badge-pending';
}

function formatMonitorRunStatus(status) {
  if (status === 'pass') return 'Pass';
  if (status === 'fail') return 'Fail';
  if (status === 'error') return 'Error';
  if (status === 'pending') return 'Pending';
  return status || 'Pending';
}

function formatMonitorInterval(seconds) {
  if (seconds === 300) return '5 minutes';
  if (seconds === 900) return '15 minutes';
  if (seconds === 3600) return '1 hour';
  return `${seconds}s`;
}

function monitorStuckTooltip(m) {
  return `Monitor scheduler is behind: this monitor is overdue by more than twice its cadence. Scheduled run was due ${formatRelative(m.next_run_at)}. This does not mean the tool failed; it means the runner has not picked up the scheduled run.`;
}

// A monitor is "stuck" when its scheduled run is overdue by more than 2x its
// interval — forgiving enough not to false-alarm on a single missed tick, loud
// enough to catch the case where the monitor scheduler/runner isn't running.
function isMonitorStuck(m) {
  if (!m.is_active || !m.next_run_at || !m.interval_seconds) return false;
  const dueMs = new Date(m.next_run_at).getTime();
  if (!Number.isFinite(dueMs)) return false;
  const overdueSec = (Date.now() - dueMs) / 1000;
  return overdueSec > 2 * m.interval_seconds;
}

window.ToolMonitorsPage = ToolMonitorsPage;
window.parseToolMonitorAddParamsTM = parseToolMonitorAddParamsTM;
window.canMonitorServerTM = canMonitorServerTM;
window.monitorServerUnavailableReasonTM = monitorServerUnavailableReasonTM;
window.cliRiskGroupTM = cliRiskGroupTM;
window.cliReviewRowsToMonitorToolsTM = cliReviewRowsToMonitorToolsTM;
window.groupCliMonitorToolsTM = groupCliMonitorToolsTM;
window.buildCliMonitorInitialToolStateTM = buildCliMonitorInitialToolStateTM;
window.agentFillToolsForAddTM = agentFillToolsForAddTM;
