import { useMemo } from 'react'
import { useParams, Link } from 'react-router-dom'
import styles from './AgentInstance.module.css'
// Layout
import { AppShell } from '../../design-system/layout/AppShell/AppShell'
import { Sidebar } from '../../design-system/layout/Sidebar/Sidebar'
import { TopBar } from '../../design-system/layout/TopBar/TopBar'
// Composites
import { LineChart } from '../../design-system/composites/LineChart/LineChart'
import { AreaChart } from '../../design-system/composites/AreaChart/AreaChart'
import { EventFeed } from '../../design-system/composites/EventFeed/EventFeed'
import { Tabs } from '../../design-system/composites/Tabs/Tabs'
import { LogViewer } from '../../design-system/composites/LogViewer/LogViewer'
import type { LogEntry } from '../../design-system/composites/LogViewer/LogViewer'
// Primitives
import { StatusDot } from '../../design-system/primitives/StatusDot/StatusDot'
import { MonoText } from '../../design-system/primitives/MonoText/MonoText'
import { Badge } from '../../design-system/primitives/Badge/Badge'
import { StatCard } from '../../design-system/primitives/StatCard/StatCard'
import { SectionHeader } from '../../design-system/primitives/SectionHeader/SectionHeader'
// Global filters
import { useGlobalFilters } from '../../design-system/providers/GlobalFilterProvider'
// Data
import { agents } from '../../mocks/agents'
import { SIDEBAR_APPS } from '../../mocks/sidebar'
import { agentEvents } from '../../mocks/agentEvents'
import { useState } from 'react'
// ── Mock trend data ──────────────────────────────────────────────────────────
function buildTimeSeries(baseValue: number, variance: number, points = 30) {
const now = Date.now()
const interval = (6 * 60 * 60 * 1000) / points
return Array.from({ length: points }, (_, i) => ({
x: new Date(now - (points - i) * interval),
y: Math.max(0, baseValue + (Math.random() - 0.5) * variance * 2),
}))
}
function buildMemoryHistory(currentPct: number) {
return [
{ label: 'Heap Used', data: buildTimeSeries(currentPct * 0.7, 10) },
{ label: 'Heap Total', data: buildTimeSeries(currentPct * 0.9, 5) },
]
}
// ── Mock log entries ─────────────────────────────────────────────────────────
function buildLogEntries(agentName: string): LogEntry[] {
const now = Date.now()
const MIN = 60_000
return [
{ timestamp: new Date(now - 1 * MIN).toISOString(), level: 'info', message: `[o.a.c.impl.DefaultCamelContext] Route order-validation started and consuming from: direct:validate` },
{ timestamp: new Date(now - 2 * MIN).toISOString(), level: 'info', message: `[o.a.c.impl.DefaultCamelContext] Total 3 routes, of which 3 are started` },
{ timestamp: new Date(now - 5 * MIN).toISOString(), level: 'warn', message: `[o.a.c.processor.errorhandler] Failed delivery for exchangeId: ID-${agentName}-1710847200000-0-1. Exhausted after 3 attempts.` },
{ timestamp: new Date(now - 8 * MIN).toISOString(), level: 'info', message: `[o.a.c.health.HealthCheckHelper] Health check [routes] is UP` },
{ timestamp: new Date(now - 12 * MIN).toISOString(), level: 'info', message: `[o.a.c.health.HealthCheckHelper] Health check [consumers] is UP` },
{ timestamp: new Date(now - 15 * MIN).toISOString(), level: 'debug', message: `[o.a.c.component.kafka] KafkaConsumer[order-events] poll returned 42 records in 18ms` },
{ timestamp: new Date(now - 18 * MIN).toISOString(), level: 'info', message: `[o.a.c.impl.engine.InternalRouteStartup] Route order-enrichment started and consuming from: kafka:order-events` },
{ timestamp: new Date(now - 25 * MIN).toISOString(), level: 'warn', message: `[o.a.c.component.http] HTTP endpoint https://payment-api.internal/verify returned 503 — will retry` },
{ timestamp: new Date(now - 30 * MIN).toISOString(), level: 'info', message: `[o.a.c.impl.DefaultCamelContext] Apache Camel ${agentName} (CamelContext) is starting` },
{ timestamp: new Date(now - 32 * MIN).toISOString(), level: 'info', message: `[org.springframework.boot] Started ${agentName} in 4.231 seconds (process running for 4.892)` },
]
}
// ── Mock JVM / process info ──────────────────────────────────────────────────
function buildProcessInfo(agent: typeof agents[0]) {
return {
jvmVersion: 'OpenJDK 21.0.2+13',
camelVersion: '4.4.0',
springBootVersion: '3.2.4',
pid: Math.floor(1000 + Math.random() * 90000),
startTime: new Date(Date.now() - parseDuration(agent.uptime)).toISOString(),
heapMax: '512 MB',
heapUsed: `${Math.round(512 * agent.memoryUsagePct / 100)} MB`,
nonHeapUsed: `${Math.round(80 + Math.random() * 40)} MB`,
threadCount: Math.floor(20 + Math.random() * 30),
peakThreads: Math.floor(45 + Math.random() * 20),
gcCollections: Math.floor(Math.random() * 500),
gcPauseTotal: `${(Math.random() * 2).toFixed(2)}s`,
classesLoaded: Math.floor(8000 + Math.random() * 4000),
openFileDescriptors: Math.floor(50 + Math.random() * 200),
maxFileDescriptors: 65536,
}
}
function parseDuration(s: string): number {
let ms = 0
const dMatch = s.match(/(\d+)d/)
const hMatch = s.match(/(\d+)h/)
const mMatch = s.match(/(\d+)m/)
if (dMatch) ms += parseInt(dMatch[1]) * 86400000
if (hMatch) ms += parseInt(hMatch[1]) * 3600000
if (mMatch) ms += parseInt(mMatch[1]) * 60000
return ms || 60000
}
// ── Component ────────────────────────────────────────────────────────────────
const LOG_TABS = [
{ label: 'All', value: 'all' },
{ label: 'Warnings', value: 'warn' },
{ label: 'Errors', value: 'error' },
]
export function AgentInstance() {
const { appId, instanceId } = useParams<{ appId: string; instanceId: string }>()
const { isInTimeRange } = useGlobalFilters()
const [logFilter, setLogFilter] = useState('all')
const agent = agents.find((a) => a.appId === appId && a.id === instanceId)
const instanceEvents = useMemo(() => {
if (!agent) return []
return agentEvents
.filter((e) => e.searchText?.toLowerCase().includes(agent.name.toLowerCase()))
.filter((e) => isInTimeRange(e.timestamp))
}, [agent, isInTimeRange])
if (!agent) {
return (