import { useState, useMemo, useCallback, useRef, useEffect } from 'react';
import { useParams, useNavigate } from 'react-router';
import { ExternalLink, RefreshCw, Pencil, LayoutGrid, List, ChevronRight, ChevronDown, ArrowUp, ArrowDown, Search, Cpu, HeartPulse, Activity } from 'lucide-react';
import {
  StatCard, StatusDot, Badge, MonoText,
  GroupCard, DataTable, EventFeed,
  LogViewer, ButtonGroup, SectionHeader, Toggle, Select, Button, useToast,
  Alert, ConfirmDialog,
} from '@cameleer/design-system';
import type { Column, FeedEvent, LogEntry, ButtonGroupItem } from '@cameleer/design-system';
import styles from './AgentHealth.module.css';
import sectionStyles from '../../styles/section-card.module.css';
import logStyles from '../../styles/log-panel.module.css';
import { useAgents } from '../../api/queries/agents';
import { useInfiniteApplicationLogs } from '../../api/queries/logs';
import { useInfiniteAgentEvents } from '../../api/queries/agents';
import { InfiniteScrollArea } from '../../components/InfiniteScrollArea';
import { useApplicationConfig, useUpdateApplicationConfig } from '../../api/queries/commands';
import { useCatalog, useDismissApp } from '../../api/queries/catalog';
import { useIsAdmin } from '../../auth/auth-store';
import { useEnvironmentStore } from '../../api/environment-store';
import type { ConfigUpdateResponse } from '../../api/queries/commands';
import type { AgentInstance } from '../../api/types';
import { timeAgo } from '../../utils/format-utils';
import { formatUptime, mapLogLevel, eventSeverity, eventIcon } from '../../utils/agent-utils';
import { describeApiError } from '../../api/errors';

// ── Helpers ──────────────────────────────────────────────────────────────────

function formatErrorRate(rate?: number): string {
  if (rate == null) return '\u2014';
  return `${(rate * 100).toFixed(1)}%`;
}

type NormStatus = 'live' | 'stale' | 'dead';

function normalizeStatus(status: string): NormStatus {
  return status.toLowerCase() as NormStatus;
}

function statusColor(s: NormStatus): 'success' | 'warning' | 'error' {
  if (s === 'live') return 'success';
  if (s === 'stale') return 'warning';
  return 'error';
}

// ── Data grouping ────────────────────────────────────────────────────────────

interface AppGroup {
  appId: string;
  instances: AgentInstance[];
  liveCount: number;
  staleCount: number;
  deadCount: number;
  totalTps: number;
  totalActiveRoutes: number;
  totalRoutes: number;
  maxCpu: number;
}

function groupByApp(agentList: AgentInstance[]): AppGroup[] {
  const map = new Map<string, AgentInstance[]>();
  for (const a of agentList) {
    const app = a.applicationId;
    const list = map.get(app) ?? [];
    list.push(a);
    map.set(app, list);
  }
  return Array.from(map.entries()).map(([appId, instances]) => ({
    appId,
    instances,
    liveCount: instances.filter((i) => normalizeStatus(i.status) === 'live').length,
    staleCount: instances.filter((i) => normalizeStatus(i.status) === 'stale').length,
    deadCount: instances.filter((i) => normalizeStatus(i.status) === 'dead').length,
    totalTps: instances.reduce((s, i) => s + (i.tps ?? 0), 0),
    totalActiveRoutes: instances.reduce((s, i) => s + (i.activeRoutes ?? 0), 0),
    totalRoutes: instances.reduce((s, i) => s + (i.totalRoutes ?? 0), 0),
    maxCpu: Math.max(...instances.map((i) => (i as AgentInstance & { cpuUsage?: number }).cpuUsage ?? -1)),
  }));
}

function appHealth(group: AppGroup): 'success' | 'warning' | 'error' {
  if (group.deadCount > 0) return 'error';
  if (group.staleCount > 0) return 'warning';
  return 'success';
}

function latestHeartbeat(group: AppGroup): string | undefined {
  let latest: string | undefined;
  for (const inst of group.instances) {
    if (inst.lastHeartbeat && (!latest || inst.lastHeartbeat > latest)) {
      latest = inst.lastHeartbeat;
    }
  }
  return latest;
}

// ── Detail sub-components ────────────────────────────────────────────────────

const LOG_LEVEL_ITEMS: ButtonGroupItem[] = [
  { value: 'error', label: 'Error', color: 'var(--error)' },
  { value: 'warn', label: 'Warn', color: 'var(--warning)' },
  { value: 'info', label: 'Info', color: 'var(--success)' },
  { value: 'debug', label: 'Debug', color: 'var(--running)' },
  { value: 'trace', label: 'Trace', color: 'var(--text-muted)' },
];

const LOG_SOURCE_ITEMS: ButtonGroupItem[] = [
  { value: 'app', label: 'App' },
  { value: 'agent', label: 'Agent' },
  { value: 'container', label: 'Container' },
];

function CompactAppCard({ group, onExpand, onNavigate }: { group: AppGroup; onExpand: () => void; onNavigate: () => void }) {
  const health = appHealth(group);
  const heartbeat = latestHeartbeat(group);
  const isHealthy = health === 'success';

  const variantClass =
    health === 'success' ? styles.compactCardSuccess
    : health === 'warning' ? styles.compactCardWarning
    : styles.compactCardError;

  return (
    <div
      className={`${styles.compactCard} ${variantClass}`}
      onClick={onExpand}
      role="button"
      tabIndex={0}
      onKeyDown={(e) => { if (e.key === 'Enter' || e.key === ' ') onExpand(); }}
    >
      <div className={styles.compactCardHeader}>
        <span
          className={styles.compactCardName}
          onClick={(e) => { e.stopPropagation(); onNavigate(); }}
          role="link"
        >
          {group.appId}
        </span>
        <ChevronRight size={14} className={styles.compactCardChevron} />
      </div>
      <div className={styles.compactCardMeta}>
        <span className={styles.compactCardLive}>
          {group.liveCount}/{group.instances.length} live
        </span>
        <span className={styles.compactCardTps}>
          <Activity size={10} /> {group.totalTps.toFixed(1)}/s
        </span>
        {group.maxCpu >= 0 && (
          <span className={styles.compactCardCpu}>
            <Cpu size={10} /> {(group.maxCpu * 100).toFixed(0)}%
          </span>
        )}
        <span className={isHealthy ? styles.compactCardHeartbeat : styles.compactCardHeartbeatWarn}>
          <HeartPulse size={10} /> {heartbeat ? timeAgo(heartbeat, true) : '\u2014'}
        </span>
      </div>
    </div>
  );
}

// ── AgentHealth page ─────────────────────────────────────────────────────────

export default function AgentHealth() {
  const { appId } = useParams();
  const navigate = useNavigate();
  const { toast } = useToast();
  const selectedEnv = useEnvironmentStore((s) => s.environment);
  const { data: agents } = useAgents(undefined, appId);
  const { data: appConfig } = useApplicationConfig(appId, selectedEnv);
  const updateConfig = useUpdateApplicationConfig();

  const isAdmin = useIsAdmin();
  const selectedEnvForCatalog = useEnvironmentStore((s) => s.environment);
  const { data: catalogApps } = useCatalog(selectedEnvForCatalog);
  const dismissApp = useDismissApp();
  const catalogEntry = catalogApps?.find((a) => a.slug === appId);

  const [confirmDismissOpen, setConfirmDismissOpen] = useState(false);
  const [viewMode, setViewMode] = useState<'compact' | 'expanded'>(() => {
    const saved = localStorage.getItem('cameleer:runtime:viewMode');
    return saved === 'expanded' ? 'expanded' : 'compact';
  });
  const [expandedApps, setExpandedApps] = useState<Set<string>>(new Set());

  const toggleViewMode = useCallback((mode: 'compact' | 'expanded') => {
    setViewMode(mode);
    setExpandedApps(new Set());
    localStorage.setItem('cameleer:runtime:viewMode', mode);
  }, []);

  const toggleAppExpanded = useCallback((appId: string) => {
    setExpandedApps((prev) => {
      const next = new Set(prev);
      if (next.has(appId)) next.delete(appId);
      else next.add(appId);
      return next;
    });
  }, []);

  const [animatingApps, setAnimatingApps] = useState<Map<string, 'expanding' | 'collapsing'>>(new Map());
  const animationTimers = useRef<Map<string, ReturnType<typeof setTimeout>>>(new Map());

  const animateToggle = useCallback((appIdToToggle: string) => {
    // Clear any existing timer for this app
    const existing = animationTimers.current.get(appIdToToggle);
    if (existing) clearTimeout(existing);

    const isCurrentlyExpanded = expandedApps.has(appIdToToggle);

    if (isCurrentlyExpanded) {
      // Collapsing: start animation, then remove from expandedApps after transition
      setAnimatingApps((prev) => new Map(prev).set(appIdToToggle, 'collapsing'));
      const timer = setTimeout(() => {
        setExpandedApps((prev) => {
          const next = new Set(prev);
          next.delete(appIdToToggle);
          return next;
        });
        setAnimatingApps((prev) => {
          const next = new Map(prev);
          next.delete(appIdToToggle);
          return next;
        });
        animationTimers.current.delete(appIdToToggle);
      }, 200);
      animationTimers.current.set(appIdToToggle, timer);
    } else {
      // Expanding: add to expandedApps immediately, animate in
      setExpandedApps((prev) => new Set(prev).add(appIdToToggle));
      setAnimatingApps((prev) => new Map(prev).set(appIdToToggle, 'expanding'));
      // Use requestAnimationFrame to ensure the collapsed state renders first
      requestAnimationFrame(() => {
        requestAnimationFrame(() => {
          setAnimatingApps((prev) => {
            const next = new Map(prev);
            next.delete(appIdToToggle);
            return next;
          });
        });
      });
    }
  }, [expandedApps]);

  // Cleanup timers on unmount
  useEffect(() => {
    return () => {
      animationTimers.current.forEach((timer) => clearTimeout(timer));
    };
  }, []);

  const [configEditing, setConfigEditing] = useState(false);
  const [configDraft, setConfigDraft] = useState<Record<string, string | boolean>>({});

  const startConfigEdit = useCallback(() => {
    if (!appConfig) return;
    setConfigDraft({
      applicationLogLevel: appConfig.applicationLogLevel ?? 'INFO',
      agentLogLevel: appConfig.agentLogLevel ?? 'INFO',
      engineLevel: appConfig.engineLevel ?? 'REGULAR',
      payloadCaptureMode: appConfig.payloadCaptureMode ?? 'BOTH',
      metricsEnabled: appConfig.metricsEnabled,
    });
    setConfigEditing(true);
  }, [appConfig]);

  const saveConfigEdit = useCallback(() => {
    if (!appConfig) return;
    const updated = { ...appConfig, ...configDraft };
    updateConfig.mutate({ config: updated, environment: selectedEnv }, {
      onSuccess: (saved: ConfigUpdateResponse) => {
        setConfigEditing(false);
        setConfigDraft({});
        if (saved.pushResult.success) {
          toast({ title: 'Config updated', description: `${appId} (v${saved.config.version}) — pushed to ${saved.pushResult.total}/${saved.pushResult.total} agents`, variant: 'success' });
        } else {
          const failed = [...saved.pushResult.responses.filter(r => r.status !== 'SUCCESS').map(r => r.agentId), ...saved.pushResult.timedOut];
          toast({ title: 'Config updated — partial push failure', description: `${saved.pushResult.responded}/${saved.pushResult.total} responded. Failed: ${failed.join(', ')}`, variant: 'warning', duration: 86_400_000 });
        }
      },
      onError: () => {
        toast({ title: 'Config update failed', variant: 'error', duration: 86_400_000 });
      },
    });
  }, [appConfig, configDraft, updateConfig, toast, appId]);
  const [eventSortAsc, setEventSortAsc] = useState(false);
  const eventStream = useInfiniteAgentEvents({ appId, isAtTop: true });

  const [appFilter, setAppFilter] = useState('');
  type AppSortKey = 'status' | 'name' | 'tps' | 'cpu' | 'heartbeat';
  const [appSortKey, setAppSortKey] = useState<AppSortKey>('name');
  const [appSortAsc, setAppSortAsc] = useState(true);

  const cycleSort = useCallback((key: AppSortKey) => {
    if (appSortKey === key) {
      setAppSortAsc((prev) => !prev);
    } else {
      setAppSortKey(key);
      setAppSortAsc(key === 'name'); // name defaults asc, others desc
    }
  }, [appSortKey]);

  const [logSearch, setLogSearch] = useState('');
  const [logLevels, setLogLevels] = useState<Set<string>>(new Set());
  const [logSources, setLogSources] = useState<Set<string>>(new Set());
  const [logSortAsc, setLogSortAsc] = useState(false);
  const [isLogAtTop, setIsLogAtTop] = useState(true);
  const logScrollRef = useRef<HTMLDivElement | null>(null);

  const logStream = useInfiniteApplicationLogs({
    application: appId,
    sources: [...logSources],
    levels: [...logLevels],
    sort: logSortAsc ? 'asc' : 'desc',
    isAtTop: isLogAtTop,
  });
  const logEntries = useMemo<LogEntry[]>(() => {
    return logStream.items.map((l) => ({
      timestamp: l.timestamp ?? '',
      level: mapLogLevel(l.level),
      message: l.message ?? '',
      source: l.source ?? undefined,
    }));
  }, [logStream.items]);
  const logSearchLower = logSearch.toLowerCase();
  const filteredLogs = logSearchLower
    ? logEntries.filter((l) => l.message.toLowerCase().includes(logSearchLower))
    : logEntries;

  const agentList = agents ?? [];

  const allGroups = useMemo(() => groupByApp(agentList), [agentList]);

  const sortedGroups = useMemo(() => {
    const healthPriority = (g: AppGroup) => g.deadCount > 0 ? 0 : g.staleCount > 0 ? 1 : 2;
    const nameCmp = (a: AppGroup, b: AppGroup) => a.appId.localeCompare(b.appId);

    const sorted = [...allGroups].sort((a, b) => {
      let cmp = 0;
      switch (appSortKey) {
        case 'status': cmp = healthPriority(a) - healthPriority(b); break;
        case 'name': cmp = a.appId.localeCompare(b.appId); break;
        case 'tps': cmp = a.totalTps - b.totalTps; break;
        case 'cpu': cmp = a.maxCpu - b.maxCpu; break;
        case 'heartbeat': {
          const ha = latestHeartbeat(a) ?? '';
          const hb = latestHeartbeat(b) ?? '';
          cmp = ha < hb ? -1 : ha > hb ? 1 : 0;
          break;
        }
      }
      if (!appSortAsc) cmp = -cmp;
      return cmp !== 0 ? cmp : nameCmp(a, b);
    });
    return sorted;
  }, [allGroups, appSortKey, appSortAsc]);

  const appFilterLower = appFilter.toLowerCase();
  const groups = appFilterLower ? sortedGroups.filter((g) => g.appId.toLowerCase().includes(appFilterLower)) : sortedGroups;

  // Aggregate stats
  const totalInstances = agentList.length;
  const liveCount = agentList.filter((a) => normalizeStatus(a.status) === 'live').length;
  const staleCount = agentList.filter((a) => normalizeStatus(a.status) === 'stale').length;
  const deadCount = agentList.filter((a) => normalizeStatus(a.status) === 'dead').length;
  const totalTps = agentList.reduce((s, a) => s + (a.tps ?? 0), 0);
  const totalActiveRoutes = agentList.reduce((s, a) => s + (a.activeRoutes ?? 0), 0);
  const totalRoutes = agentList.reduce((s, a) => s + (a.totalRoutes ?? 0), 0);

  // Map events to FeedEvent
  const feedEvents: FeedEvent[] = useMemo(() => {
    const mapped = eventStream.items.map((e) => ({
      id: `${e.timestamp}:${e.instanceId}:${e.eventType}`,
      severity: eventSeverity(e.eventType),
      icon: eventIcon(e.eventType),
      message: `${e.instanceId}: ${e.eventType}${e.detail ? ' \u2014 ' + e.detail : ''}`,
      timestamp: new Date(e.timestamp),
    }));
    return eventSortAsc ? mapped.toReversed() : mapped;
  }, [eventStream.items, eventSortAsc]);

  // Column definitions for the instance DataTable
  const instanceColumns: Column<AgentInstance>[] = useMemo(
    () => [
      {
        key: 'status',
        header: '',
        width: '12px',
        render: (_val, row) => <StatusDot variant={normalizeStatus(row.status)} />,
      },
      {
        key: '_inspect',
        header: '',
        width: '36px',
        render: (_val, row) => (
          <button
            className={styles.inspectLink}
            title="Open instance page"
            onClick={(e) => {
              e.stopPropagation();
              navigate(`/agents/${row.applicationId}/${row.instanceId}`);
            }}
          >
            <ExternalLink size={14} />
          </button>
        ),
      },
      {
        key: 'name',
        header: 'Instance',
        render: (_val, row) => (
          <MonoText size="sm" className={styles.instanceName}>{row.displayName ?? row.instanceId}</MonoText>
        ),
      },
      {
        key: 'state',
        header: 'State',
        render: (_val, row) => {
          const ns = normalizeStatus(row.status);
          return <Badge label={row.status} color={statusColor(ns)} variant="filled" />;
        },
      },
      {
        key: 'uptime',
        header: 'Uptime',
        render: (_val, row) => (
          <MonoText size="xs" className={styles.instanceMeta}>{formatUptime(row.uptimeSeconds)}</MonoText>
        ),
      },
      {
        key: 'tps',
        header: 'TPS',
        render: (_val, row) => (
          <MonoText size="xs" className={styles.instanceMeta}>
            {row.tps != null ? `${row.tps.toFixed(1)}/s` : '\u2014'}
          </MonoText>
        ),
      },
      {
        key: 'cpuUsage',
        header: 'CPU',
        render: (_val, row) => {
          const cpu = (row as AgentInstance & { cpuUsage?: number }).cpuUsage;
          return (
            <MonoText size="xs" className={cpu != null && cpu > 0.8 ? styles.instanceError : styles.instanceMeta}>
              {cpu != null && cpu >= 0 ? `${(cpu * 100).toFixed(0)}%` : '\u2014'}
            </MonoText>
          );
        },
      },
      {
        key: 'lastHeartbeat',
        header: 'Heartbeat',
        render: (_val, row) => {
          const ns = normalizeStatus(row.status);
          return (
            <MonoText
              size="xs"
              className={
                ns === 'dead'
                  ? styles.instanceHeartbeatDead
                  : ns === 'stale'
                    ? styles.instanceHeartbeatStale
                    : styles.instanceMeta
              }
            >
              {timeAgo(row.lastHeartbeat)}
            </MonoText>
          );
        },
      },
    ],
    [],
  );

  function handleInstanceClick(inst: AgentInstance) {
    navigate(`/runtime/${inst.applicationId}/${inst.instanceId}`);
  }

  const isFullWidth = !!appId;

  return (
    <div className={styles.content}>
      {/* No agents warning + dismiss — shown when app-scoped, no agents, admin */}
      {appId && agentList.length === 0 && isAdmin && (
        <>
          <Alert variant="warning" title="No agents connected" className={styles.dismissAlert}>
            <span>
              {catalogEntry?.managed ? 'Managed app' : 'Discovered app'} &mdash; {(catalogEntry?.exchangeCount ?? 0).toLocaleString()} exchanges recorded.
              {' '}You can dismiss this application to remove it and all associated data.
            </span>
            <div style={{ marginTop: 8 }}>
              <Button variant="danger" size="sm" disabled={dismissApp.isPending}
                onClick={() => setConfirmDismissOpen(true)}>
                {dismissApp.isPending ? 'Dismissing\u2026' : 'Dismiss Application'}
              </Button>
            </div>
          </Alert>
          <ConfirmDialog
            open={confirmDismissOpen}
            onClose={() => setConfirmDismissOpen(false)}
            onConfirm={() => {
              setConfirmDismissOpen(false);
              dismissApp.mutate(appId, {
                onSuccess: () => {
                  toast({ title: 'Application dismissed', description: `${appId} and all associated data have been deleted`, variant: 'success' });
                  navigate('/runtime');
                },
                onError: (err) => {
                  toast({ title: 'Dismiss failed', description: describeApiError(err), variant: 'error', duration: 86_400_000 });
                },
              });
            }}
            title="Dismiss Application"
            message={`This will permanently delete "${appId}" and all associated data (${(catalogEntry?.exchangeCount ?? 0).toLocaleString()} exchanges, logs, diagrams). This action cannot be undone.`}
            confirmText={appId}
            confirmLabel="Dismiss"
            variant="danger"
            loading={dismissApp.isPending}
          />
        </>
      )}

      {/* Stat strip */}
      <div className={styles.statStrip}>
        <StatCard
          label="Total Agents"
          value={String(totalInstances)}
          accent={deadCount > 0 ? 'warning' : 'amber'}
          detail={
            <span className={styles.breakdown}>
              <span className={styles.bpLive}><StatusDot variant="live" /> {liveCount} live</span>
              <span className={styles.bpStale}><StatusDot variant="stale" /> {staleCount} stale</span>
              <span className={styles.bpDead}><StatusDot variant="dead" /> {deadCount} dead</span>
            </span>
          }
        />
        <StatCard
          label="Applications"
          value={String(allGroups.length)}
          accent="running"
          detail={
            <span className={styles.breakdown}>
              <span className={styles.bpLive}>
                <StatusDot variant="live" /> {allGroups.filter((g) => g.deadCount === 0 && g.staleCount === 0).length} healthy
              </span>
              <span className={styles.bpStale}>
                <StatusDot variant="stale" /> {allGroups.filter((g) => g.staleCount > 0 && g.deadCount === 0).length} degraded
              </span>
              <span className={styles.bpDead}>
                <StatusDot variant="dead" /> {allGroups.filter((g) => g.deadCount > 0).length} critical
              </span>
            </span>
          }
        />
        <StatCard
          label="Active Routes"
          value={
            <span
              className={
                styles[
                  totalActiveRoutes === 0
                    ? 'routesError'
                    : totalActiveRoutes < totalRoutes
                      ? 'routesWarning'
                      : 'routesSuccess'
                ]
              }
            >
              {totalActiveRoutes}/{totalRoutes}
            </span>
          }
          accent={totalActiveRoutes === 0 ? 'error' : totalActiveRoutes < totalRoutes ? 'warning' : 'success'}
          detail={totalActiveRoutes < totalRoutes ? `${totalRoutes - totalActiveRoutes} suspended` : 'all routes active'}
        />
        <StatCard
          label="Total TPS"
          value={totalTps.toFixed(1)}
          accent="amber"
          detail="msg/s"
        />
        <StatCard
          label="Dead"
          value={String(deadCount)}
          accent={deadCount > 0 ? 'error' : 'success'}
          detail={deadCount > 0 ? 'requires attention' : 'all healthy'}
        />
      </div>

      {/* Application config bar */}
      {appId && appConfig && (
        <div className={`${sectionStyles.section} ${styles.configBar}`}>
          {configEditing ? (
            <>
              <div className={styles.configField}>
                <span className={styles.configLabel}>App Log Level</span>
                <Select value={String(configDraft.applicationLogLevel ?? 'INFO')}
                  onChange={(e) => setConfigDraft(d => ({ ...d, applicationLogLevel: e.target.value }))}
                  options={[
                    { value: 'ERROR', label: 'ERROR' },
                    { value: 'WARN', label: 'WARN' },
                    { value: 'INFO', label: 'INFO' },
                    { value: 'DEBUG', label: 'DEBUG' },
                    { value: 'TRACE', label: 'TRACE' },
                  ]}
                />
              </div>
              <div className={styles.configField}>
                <span className={styles.configLabel}>Agent Log Level</span>
                <Select value={String(configDraft.agentLogLevel ?? 'INFO')}
                  onChange={(e) => setConfigDraft(d => ({ ...d, agentLogLevel: e.target.value }))}
                  options={[
                    { value: 'ERROR', label: 'ERROR' },
                    { value: 'WARN', label: 'WARN' },
                    { value: 'INFO', label: 'INFO' },
                    { value: 'DEBUG', label: 'DEBUG' },
                    { value: 'TRACE', label: 'TRACE' },
                  ]}
                />
              </div>
              <div className={styles.configField}>
                <span className={styles.configLabel}>Engine Level</span>
                <Select value={String(configDraft.engineLevel ?? 'REGULAR')}
                  onChange={(e) => setConfigDraft(d => ({ ...d, engineLevel: e.target.value }))}
                  options={[
                    { value: 'NONE', label: 'None' },
                    { value: 'MINIMAL', label: 'Minimal' },
                    { value: 'REGULAR', label: 'Regular' },
                    { value: 'COMPLETE', label: 'Complete' },
                  ]}
                />
              </div>
              <div className={styles.configField}>
                <span className={styles.configLabel}>Payload Capture</span>
                <Select value={String(configDraft.payloadCaptureMode ?? 'BOTH')}
                  onChange={(e) => setConfigDraft(d => ({ ...d, payloadCaptureMode: e.target.value }))}
                  options={[
                    { value: 'NONE', label: 'None' },
                    { value: 'INPUT', label: 'Input' },
                    { value: 'OUTPUT', label: 'Output' },
                    { value: 'BOTH', label: 'Both' },
                  ]}
                />
              </div>
              <div className={styles.configField}>
                <span className={styles.configLabel}>Metrics</span>
                <Toggle checked={Boolean(configDraft.metricsEnabled)}
                  onChange={(e) => setConfigDraft(d => ({ ...d, metricsEnabled: (e.target as HTMLInputElement).checked }))} />
              </div>
              <div className={styles.configActions}>
                <Button variant="primary" size="sm" onClick={saveConfigEdit} disabled={updateConfig.isPending}>Save</Button>
                <Button variant="secondary" size="sm" onClick={() => { setConfigEditing(false); setConfigDraft({}); }}>Cancel</Button>
              </div>
            </>
          ) : (
            <>
              <div className={styles.configField}>
                <span className={styles.configLabel}>App Log Level</span>
                <Badge label={appConfig.applicationLogLevel ?? 'INFO'} color={
                  (appConfig.applicationLogLevel ?? 'INFO') === 'ERROR' ? 'error'
                  : (appConfig.applicationLogLevel ?? 'INFO') === 'WARN' ? 'warning'
                  : (appConfig.applicationLogLevel ?? 'INFO') === 'DEBUG' ? 'running'
                  : (appConfig.applicationLogLevel ?? 'INFO') === 'TRACE' ? 'auto' : 'success'
                } variant="filled" />
              </div>
              <div className={styles.configField}>
                <span className={styles.configLabel}>Agent Log Level</span>
                <Badge label={appConfig.agentLogLevel ?? 'INFO'} color={
                  (appConfig.agentLogLevel ?? 'INFO') === 'ERROR' ? 'error'
                  : (appConfig.agentLogLevel ?? 'INFO') === 'WARN' ? 'warning'
                  : (appConfig.agentLogLevel ?? 'INFO') === 'DEBUG' ? 'running'
                  : (appConfig.agentLogLevel ?? 'INFO') === 'TRACE' ? 'auto' : 'success'
                } variant="filled" />
              </div>
              <div className={styles.configField}>
                <span className={styles.configLabel}>Engine Level</span>
                <Badge label={appConfig.engineLevel ?? 'REGULAR'} color={
                  (appConfig.engineLevel ?? 'REGULAR') === 'NONE' ? 'error'
                  : (appConfig.engineLevel ?? 'REGULAR') === 'MINIMAL' ? 'warning'
                  : (appConfig.engineLevel ?? 'REGULAR') === 'COMPLETE' ? 'running' : 'success'
                } variant="filled" />
              </div>
              <div className={styles.configField}>
                <span className={styles.configLabel}>Payload Capture</span>
                <Badge label={appConfig.payloadCaptureMode ?? 'BOTH'} color={
                  (appConfig.payloadCaptureMode ?? 'BOTH') === 'BOTH' ? 'running'
                  : (appConfig.payloadCaptureMode ?? 'BOTH') === 'NONE' ? 'auto' : 'warning'
                } variant="filled" />
              </div>
              <div className={styles.configField}>
                <span className={styles.configLabel}>Metrics</span>
                <Badge label={appConfig.metricsEnabled ? 'On' : 'Off'} color={appConfig.metricsEnabled ? 'success' : 'error'} variant="filled" />
              </div>
              <Button variant="ghost" size="sm" title="Edit config" onClick={startConfigEdit}><Pencil size={14} /></Button>
            </>
          )}
        </div>
      )}


      {/* View toolbar — hidden on app detail page */}
      {!isFullWidth && (
        <div className={styles.viewToolbar}>
          <div className={styles.viewToggle}>
            <button
              className={`${styles.viewToggleBtn} ${viewMode === 'compact' ? styles.viewToggleBtnActive : ''}`}
              onClick={() => toggleViewMode('compact')}
              title="Compact view"
            >
              <LayoutGrid size={14} />
            </button>
            <button
              className={`${styles.viewToggleBtn} ${viewMode === 'expanded' ? styles.viewToggleBtnActive : ''}`}
              onClick={() => toggleViewMode('expanded')}
              title="Expanded view"
            >
              <List size={14} />
            </button>
          </div>
          <div className={styles.appFilterWrap}>
            <Search size={12} className={styles.appFilterIcon} />
            <input
              type="text"
              className={styles.appFilterInput}
              placeholder="Filter..."
              value={appFilter}
              onChange={(e) => setAppFilter(e.target.value)}
              aria-label="Filter applications"
            />
            {appFilter && (
              <button
                type="button"
                className={styles.appFilterClear}
                onClick={() => setAppFilter('')}
                aria-label="Clear filter"
              >
                &times;
              </button>
            )}
          </div>
          <div className={styles.sortGroup}>
            {([['status', 'Status'], ['name', 'Name'], ['tps', 'TPS'], ['cpu', 'CPU'], ['heartbeat', 'Heartbeat']] as const).map(([key, label]) => (
              <button
                key={key}
                className={`${styles.sortBtn} ${appSortKey === key ? styles.sortBtnActive : ''}`}
                onClick={() => cycleSort(key)}
                title={`Sort by ${label}`}
              >
                {label}
                {appSortKey === key && (
                  appSortAsc ? <ArrowUp size={10} /> : <ArrowDown size={10} />
                )}
              </button>
            ))}
          </div>
        </div>
      )}

      {/* Group cards grid */}
      {viewMode === 'expanded' || isFullWidth ? (
        <div className={isFullWidth ? styles.groupGridSingle : styles.groupGrid}>
          {groups.map((group) => (
            <GroupCard
              key={group.appId}
              title={group.appId}
              accent={appHealth(group)}
              headerRight={
                <Badge
                  label={`${group.liveCount}/${group.instances.length} LIVE`}
                  color={appHealth(group)}
                  variant="filled"
                />
              }
              meta={
                <div className={styles.groupMeta}>
                  <span><Activity size={12} /> <strong>{group.totalTps.toFixed(1)}</strong>/s</span>
                  <span><strong>{group.totalActiveRoutes}</strong>/{group.totalRoutes} routes</span>
                  {group.maxCpu >= 0 && <span><Cpu size={12} /> <strong>{(group.maxCpu * 100).toFixed(0)}%</strong></span>}
                </div>
              }
              footer={
                group.deadCount > 0 ? (
                  <div className={styles.alertBanner}>
                    <span className={styles.alertIcon}>&#9888;</span>
                    <span>
                      Single point of failure &mdash;{' '}
                      {group.deadCount === group.instances.length
                        ? 'no redundancy'
                        : `${group.deadCount} dead instance${group.deadCount > 1 ? 's' : ''}`}
                    </span>
                  </div>
                ) : undefined
              }
            >
              <DataTable<AgentInstance & { id: string }>
                columns={instanceColumns as Column<AgentInstance & { id: string }>[]}
                data={group.instances.map(i => ({ ...i, id: i.instanceId }))}
                onRowClick={handleInstanceClick}
                pageSize={50}
                flush
              />
            </GroupCard>
          ))}
        </div>
      ) : (
        <div className={styles.compactGrid}>
          {groups.map((group) => (
            <div key={group.appId} className={styles.compactGridCell}>
              <CompactAppCard
                group={group}
                onExpand={() => animateToggle(group.appId)}
                onNavigate={() => navigate(`/runtime/${group.appId}`)}
              />
              {expandedApps.has(group.appId) && (
                <>
                <div className={styles.overlayBackdrop} onClick={() => animateToggle(group.appId)} />
                <div
                  ref={(el) => {
                    if (!el) return;
                    // Constrain overlay within viewport
                    const rect = el.getBoundingClientRect();
                    const vw = document.documentElement.clientWidth;
                    if (rect.right > vw - 16) {
                      el.style.left = 'auto';
                      el.style.right = '0';
                    }
                    if (rect.bottom > document.documentElement.clientHeight) {
                      const overflow = rect.bottom - document.documentElement.clientHeight + 16;
                      el.style.maxHeight = `${rect.height - overflow}px`;
                      el.style.overflowY = 'auto';
                    }
                  }}
                  className={`${styles.compactGridExpanded} ${styles.expandWrapper} ${
                    animatingApps.get(group.appId) === 'expanding'
                      ? styles.expandWrapperCollapsed
                      : animatingApps.get(group.appId) === 'collapsing'
                        ? styles.expandWrapperCollapsed
                        : styles.expandWrapperExpanded
                  }`}
                >
                  <GroupCard
                    title={group.appId}
                    accent={appHealth(group)}
                    headerRight={
                      <div style={{ display: 'flex', alignItems: 'center', gap: 8 }}>
                        <Badge
                          label={`${group.liveCount}/${group.instances.length} LIVE`}
                          color={appHealth(group)}
                          variant="filled"
                        />
                        <button
                          className={styles.collapseBtn}
                          onClick={(e) => { e.stopPropagation(); animateToggle(group.appId); }}
                          title="Collapse"
                        >
                          <ChevronDown size={14} />
                        </button>
                      </div>
                    }
                    meta={
                      <div className={styles.groupMeta}>
                        <span><Activity size={12} /> <strong>{group.totalTps.toFixed(1)}</strong>/s</span>
                        <span><strong>{group.totalActiveRoutes}</strong>/{group.totalRoutes} routes</span>
                        {group.maxCpu >= 0 && <span><Cpu size={12} /> <strong>{(group.maxCpu * 100).toFixed(0)}%</strong></span>}
                      </div>
                    }
                    footer={
                      group.deadCount > 0 ? (
                        <div className={styles.alertBanner}>
                          <span className={styles.alertIcon}>&#9888;</span>
                          <span>
                            Single point of failure &mdash;{' '}
                            {group.deadCount === group.instances.length
                              ? 'no redundancy'
                              : `${group.deadCount} dead instance${group.deadCount > 1 ? 's' : ''}`}
                          </span>
                        </div>
                      ) : undefined
                    }
                  >
                    <DataTable<AgentInstance & { id: string }>
                      columns={instanceColumns as Column<AgentInstance & { id: string }>[]}
                      data={group.instances.map(i => ({ ...i, id: i.instanceId }))}
                      onRowClick={handleInstanceClick}
                      pageSize={50}
                      flush
                    />
                  </GroupCard>
                </div>
                </>
              )}
            </div>
          ))}
        </div>
      )}

      {/* Log + Timeline side by side */}
      <div className={styles.bottomRow}>
        <div className={logStyles.logCard}>
          <div className={logStyles.logHeader}>
            <SectionHeader>Application Log</SectionHeader>
            <div className={logStyles.headerActions}>
              <span className={styles.sectionMeta}>
                {filteredLogs.length === logStream.items.length
                  ? `${filteredLogs.length} entries`
                  : `${filteredLogs.length} of ${logStream.items.length} entries`}
              </span>
              <Button variant="ghost" size="sm" onClick={() => setLogSortAsc((v) => !v)} title={logSortAsc ? 'Oldest first' : 'Newest first'}>
                {logSortAsc ? '\u2191' : '\u2193'}
              </Button>
              <Button variant="ghost" size="sm" onClick={() => {
                logStream.refresh();
                logScrollRef.current?.scrollTo({ top: 0 });
              }} title="Refresh">
                <RefreshCw size={14} />
              </Button>
            </div>
          </div>
          <div className={logStyles.logToolbar}>
            <div className={logStyles.logSearchWrap}>
              <input
                type="text"
                className={logStyles.logSearchInput}
                placeholder="Search logs…"
                value={logSearch}
                onChange={(e) => setLogSearch(e.target.value)}
                aria-label="Search logs"
              />
              {logSearch && (
                <button
                  type="button"
                  className={logStyles.logSearchClear}
                  onClick={() => setLogSearch('')}
                  aria-label="Clear search"
                >
                  &times;
                </button>
              )}
            </div>
            <ButtonGroup
              items={LOG_SOURCE_ITEMS}
              value={logSources}
              onChange={setLogSources}
            />
            {logSources.size > 0 && (
              <Button variant="ghost" size="sm" onClick={() => setLogSources(new Set())}>
                Clear
              </Button>
            )}
            <ButtonGroup items={LOG_LEVEL_ITEMS} value={logLevels} onChange={setLogLevels} />
            {logLevels.size > 0 && (
              <Button variant="ghost" size="sm" onClick={() => setLogLevels(new Set())}>
                Clear
              </Button>
            )}
          </div>
          <InfiniteScrollArea
            scrollRef={logScrollRef}
            onEndReached={logStream.fetchNextPage}
            onTopVisibilityChange={setIsLogAtTop}
            isFetchingNextPage={logStream.isFetchingNextPage}
            hasNextPage={logStream.hasNextPage}
            isLoading={logStream.isLoading}
            hasItems={logStream.items.length > 0}
            maxHeight={360}
          >
            {filteredLogs.length > 0 ? (
              <LogViewer entries={filteredLogs} className={logStyles.flatScroll} />
            ) : (
              <div className={logStyles.logEmpty}>
                {logSearch || logLevels.size > 0 || logSources.size > 0
                  ? 'No matching log entries'
                  : logStream.isLoading ? 'Loading logs\u2026' : 'No log entries available'}
              </div>
            )}
          </InfiniteScrollArea>
        </div>

        <div className={logStyles.logCard}>
          <div className={logStyles.logHeader}>
            <SectionHeader>Timeline</SectionHeader>
            <div className={logStyles.headerActions}>
              <span className={styles.sectionMeta}>{eventStream.items.length} events</span>
              <Button variant="ghost" size="sm" onClick={() => setEventSortAsc((v) => !v)} title={eventSortAsc ? 'Oldest first' : 'Newest first'}>
                {eventSortAsc ? '\u2191' : '\u2193'}
              </Button>
              <Button variant="ghost" size="sm" onClick={() => eventStream.refresh()} title="Refresh">
                <RefreshCw size={14} />
              </Button>
            </div>
          </div>
          <div className={logStyles.eventFeedContainer}>
            {feedEvents.length > 0 ? (
              <EventFeed events={feedEvents} />
            ) : (
              <div className={logStyles.logEmpty}>
                {eventStream.isLoading ? 'Loading events\u2026' : 'No events in the selected time range.'}
              </div>
            )}
          </div>
          {eventStream.hasNextPage && (
            <button
              className={logStyles.loadOlderBtn}
              onClick={eventStream.fetchNextPage}
              disabled={eventStream.isFetchingNextPage}
            >
              {eventStream.isFetchingNextPage ? 'Loading\u2026' : 'Load older events'}
            </button>
          )}
        </div>
      </div>

    </div>
  );
}
