<template>
  <div class="pb-6 mobile-scroll-pad">
    <!-- Header -->
    <div class="hidden md:block mb-8">
      <div class="flex items-center justify-between">
        <div>
          <h1 class="text-3xl font-bold text-white mb-2">Fleet Dashboard</h1>
          <p class="text-white/70">Beta Telemetry — monitoring {{ nodes.length }} node{{ nodes.length !== 1 ? 's' : '' }}</p>
        </div>
        <div class="flex gap-2 items-center">
          <span v-if="autoRefresh" class="text-xs text-white/40">Auto-refresh 60s</span>
          <button class="glass-button text-sm px-4 py-2" @click="toggleAutoRefresh">
            {{ autoRefresh ? 'Pause' : 'Resume' }}
          </button>
          <button class="glass-button text-sm px-4 py-2" @click="refreshAll">
            Refresh
          </button>
          <button class="glass-button text-sm px-4 py-2" @click="exportFleetData">
            Export JSON
          </button>
        </div>
      </div>
    </div>

    <!-- Mobile Header -->
    <div class="md:hidden mb-6">
      <h1 class="text-2xl font-bold text-white mb-1">Fleet Dashboard</h1>
      <p class="text-white/60 text-sm mb-3">Monitoring {{ nodes.length }} node{{ nodes.length !== 1 ? 's' : '' }}</p>
      <div class="flex gap-2">
        <button class="glass-button text-xs px-3 py-2 flex-1" @click="refreshAll">Refresh</button>
        <button class="glass-button text-xs px-3 py-2 flex-1" @click="exportFleetData">Export</button>
      </div>
    </div>

    <!-- Loading State -->
    <div v-if="loading" class="flex items-center justify-center py-20">
      <div class="text-white/50 text-sm">Loading fleet data...</div>
    </div>

    <!-- Error State -->
    <div v-else-if="errorMessage" class="glass-card p-6 mb-6">
      <div class="alert-error rounded-lg mb-4">{{ errorMessage }}</div>
      <button class="glass-button text-sm px-4 py-2" @click="refreshAll">Retry</button>
    </div>

    <!-- Dashboard Content -->
    <template v-else>
      <!-- Section 1: Fleet Overview Cards -->
      <div class="grid grid-cols-2 lg:grid-cols-5 gap-4 mb-6">
        <div class="monitoring-stat-card">
          <p class="text-xs text-white/50 uppercase tracking-wide">Total Nodes</p>
          <p class="text-2xl font-bold text-white">{{ nodes.length }}</p>
          <p class="text-xs text-white/40">
            <span class="fleet-dot-online"></span> {{ onlineCount }} online
            <span class="ml-1 fleet-dot-offline"></span> {{ offlineCount }} offline
          </p>
        </div>
        <div class="monitoring-stat-card">
          <p class="text-xs text-white/50 uppercase tracking-wide">Fleet Health</p>
          <p class="text-2xl font-bold text-white">{{ fleetHealthPct }}%</p>
          <p class="text-xs text-white/40">{{ healthyCount }}/{{ nodes.length }} no alerts</p>
        </div>
        <div class="monitoring-stat-card">
          <p class="text-xs text-white/50 uppercase tracking-wide">Avg CPU</p>
          <p class="text-2xl font-bold text-white" :class="healthTextClass(avgCpu)">{{ avgCpu.toFixed(1) }}%</p>
          <p class="text-xs text-white/40">across fleet</p>
        </div>
        <div class="monitoring-stat-card">
          <p class="text-xs text-white/50 uppercase tracking-wide">Avg RAM</p>
          <p class="text-2xl font-bold text-white" :class="healthTextClass(avgMem)">{{ avgMem.toFixed(1) }}%</p>
          <p class="text-xs text-white/40">across fleet</p>
        </div>
        <div class="monitoring-stat-card">
          <p class="text-xs text-white/50 uppercase tracking-wide">Avg Disk</p>
          <p class="text-2xl font-bold text-white" :class="healthTextClass(avgDisk)">{{ avgDisk.toFixed(1) }}%</p>
          <p class="text-xs text-white/40">across fleet</p>
        </div>
      </div>

      <!-- Section 2: Node Grid -->
      <div class="glass-card p-5 mb-6">
        <div class="flex items-center justify-between mb-4">
          <h3 class="text-sm font-medium text-white/80">Nodes</h3>
          <div class="flex gap-2">
            <button
              v-for="opt in sortOptions"
              :key="opt.value"
              class="fleet-sort-btn"
              :class="{ 'fleet-sort-btn-active': sortBy === opt.value }"
              @click="sortBy = opt.value"
            >
              {{ opt.label }}
            </button>
          </div>
        </div>

        <!-- Empty State -->
        <div v-if="!nodes.length" class="text-white/40 text-sm py-8 text-center">
          No nodes reporting. Ensure telemetry is enabled on beta nodes.
        </div>

        <div v-else class="grid grid-cols-1 md:grid-cols-2 xl:grid-cols-3 gap-4">
          <div
            v-for="node in sortedNodes"
            :key="node.node_id"
            class="fleet-node-card"
            :class="{ 'fleet-node-card-selected': selectedNodeId === node.node_id }"
            @click="selectNode(node.node_id)"
          >
            <div class="flex items-center justify-between mb-3">
              <div class="flex items-center gap-2">
                <span
                  class="fleet-status-dot"
                  :class="isOnline(node.reported_at) ? 'fleet-dot-online' : 'fleet-dot-offline'"
                ></span>
                <span class="text-sm font-mono text-white">{{ node.node_id.slice(0, 8) }}</span>
              </div>
              <span class="fleet-version-badge">v{{ node.version }}</span>
            </div>

            <div class="space-y-2 mb-3">
              <div class="fleet-metric-row">
                <span class="text-xs text-white/50">CPU</span>
                <div class="fleet-bar-track">
                  <div
                    class="fleet-bar-fill"
                    :class="healthBarClass(node.cpu_pct)"
                    :style="{ width: Math.min(node.cpu_pct, 100) + '%' }"
                  ></div>
                </div>
                <span class="text-xs text-white/60 w-10 text-right">{{ node.cpu_pct.toFixed(0) }}%</span>
              </div>
              <div class="fleet-metric-row">
                <span class="text-xs text-white/50">RAM</span>
                <div class="fleet-bar-track">
                  <div
                    class="fleet-bar-fill"
                    :class="healthBarClass(node.mem_pct)"
                    :style="{ width: Math.min(node.mem_pct, 100) + '%' }"
                  ></div>
                </div>
                <span class="text-xs text-white/60 w-10 text-right">{{ node.mem_pct.toFixed(0) }}%</span>
              </div>
              <div class="fleet-metric-row">
                <span class="text-xs text-white/50">Disk</span>
                <div class="fleet-bar-track">
                  <div
                    class="fleet-bar-fill"
                    :class="healthBarClass(node.disk_pct)"
                    :style="{ width: Math.min(node.disk_pct, 100) + '%' }"
                  ></div>
                </div>
                <span class="text-xs text-white/60 w-10 text-right">{{ node.disk_pct.toFixed(0) }}%</span>
              </div>
            </div>

            <div class="flex items-center justify-between text-xs text-white/40">
              <span>{{ node.running_count }}/{{ node.container_count }} containers</span>
              <span>{{ node.federation_peers }} peers</span>
            </div>
            <div class="flex items-center justify-between text-xs text-white/40 mt-1">
              <span>Up {{ formatUptime(node.uptime_secs) }}</span>
              <span>{{ timeAgo(node.reported_at) }}</span>
            </div>
          </div>
        </div>
      </div>

      <!-- Section 3: Fleet Alerts Timeline -->
      <div class="glass-card p-5 mb-6">
        <h3 class="text-sm font-medium text-white/80 mb-4">Fleet Alerts</h3>

        <div v-if="alertsLoading" class="text-white/40 text-sm py-4 text-center">
          Loading alerts...
        </div>
        <div v-else-if="!fleetAlerts.length" class="text-white/40 text-sm py-4 text-center">
          No alerts across the fleet.
        </div>
        <div v-else class="space-y-2 max-h-80 overflow-y-auto">
          <div
            v-for="(alert, idx) in fleetAlerts.slice(0, 50)"
            :key="idx"
            class="flex items-start gap-3 p-3 bg-white/5 rounded-lg"
          >
            <span
              class="inline-block w-2 h-2 rounded-full mt-1.5 flex-shrink-0"
              :class="alertSeverityDot(alert.rule)"
            ></span>
            <div class="flex-1 min-w-0">
              <div class="flex items-center gap-2 mb-0.5">
                <span class="fleet-node-badge">{{ alert.node_id.slice(0, 8) }}</span>
                <span class="text-xs text-white/40">{{ alertTypeLabel(alert.rule) }}</span>
              </div>
              <p class="text-sm text-white/80">{{ alert.message }}</p>
              <p class="text-xs text-white/30 mt-0.5">{{ formatTimestamp(alert.timestamp) }}</p>
            </div>
          </div>
        </div>
      </div>

      <!-- Section 4: Node Detail (expanded view) -->
      <div v-if="selectedNodeId && selectedNode" class="glass-card p-5 mb-6">
        <div class="flex items-center justify-between mb-4">
          <h3 class="text-sm font-medium text-white/80">
            Node Detail — <span class="font-mono">{{ selectedNodeId.slice(0, 8) }}</span>
          </h3>
          <button class="glass-button text-xs px-3 py-1" @click="selectedNodeId = null">Close</button>
        </div>

        <!-- Node Info Summary -->
        <div class="grid grid-cols-2 lg:grid-cols-4 gap-4 mb-6">
          <div class="monitoring-stat-card">
            <p class="text-xs text-white/50 uppercase tracking-wide">Version</p>
            <p class="text-lg font-bold text-white">v{{ selectedNode.version }}</p>
          </div>
          <div class="monitoring-stat-card">
            <p class="text-xs text-white/50 uppercase tracking-wide">Uptime</p>
            <p class="text-lg font-bold text-white">{{ formatUptime(selectedNode.uptime_secs) }}</p>
          </div>
          <div class="monitoring-stat-card">
            <p class="text-xs text-white/50 uppercase tracking-wide">CPU Cores</p>
            <p class="text-lg font-bold text-white">{{ selectedNode.cpu_cores }}</p>
          </div>
          <div class="monitoring-stat-card">
            <p class="text-xs text-white/50 uppercase tracking-wide">Federation Peers</p>
            <p class="text-lg font-bold text-white">{{ selectedNode.federation_peers }}</p>
          </div>
        </div>

        <!-- History Charts -->
        <div v-if="nodeHistoryLoading" class="text-white/40 text-sm py-4 text-center mb-4">
          Loading history...
        </div>
        <div v-else-if="nodeHistory.length" class="grid grid-cols-1 lg:grid-cols-3 gap-6 mb-6">
          <div class="glass-card p-4">
            <h4 class="text-xs font-medium text-white/60 mb-2">CPU History</h4>
            <LineChart
              :datasets="nodeHistoryCpuDatasets"
              :labels="nodeHistoryLabels"
              :width="chartWidth"
              :height="160"
              :y-max="100"
            />
          </div>
          <div class="glass-card p-4">
            <h4 class="text-xs font-medium text-white/60 mb-2">RAM History</h4>
            <LineChart
              :datasets="nodeHistoryMemDatasets"
              :labels="nodeHistoryLabels"
              :width="chartWidth"
              :height="160"
              :y-max="100"
            />
          </div>
          <div class="glass-card p-4">
            <h4 class="text-xs font-medium text-white/60 mb-2">Disk History</h4>
            <LineChart
              :datasets="nodeHistoryDiskDatasets"
              :labels="nodeHistoryLabels"
              :width="chartWidth"
              :height="160"
              :y-max="100"
            />
          </div>
        </div>

        <!-- Container List -->
        <div class="mb-4">
          <h4 class="text-xs font-medium text-white/60 mb-2 uppercase tracking-wide">Containers</h4>
          <div v-if="!selectedNode.containers.length" class="text-white/40 text-sm py-2">
            No containers reported.
          </div>
          <div v-else class="space-y-1">
            <div
              v-for="c in selectedNode.containers"
              :key="c.id"
              class="flex items-center gap-3 p-2 bg-white/5 rounded-lg"
            >
              <span
                class="inline-block w-2 h-2 rounded-full flex-shrink-0"
                :class="c.state === 'running' ? 'bg-green-400' : 'bg-red-400'"
              ></span>
              <span class="text-sm text-white flex-1 truncate">{{ c.id }}</span>
              <span class="text-xs text-white/40">{{ c.state }}</span>
              <span class="text-xs text-white/30">{{ c.version }}</span>
            </div>
          </div>
        </div>

        <!-- Node Alerts -->
        <div>
          <h4 class="text-xs font-medium text-white/60 mb-2 uppercase tracking-wide">Recent Alerts</h4>
          <div v-if="!selectedNode.recent_alerts.length" class="text-white/40 text-sm py-2">
            No recent alerts for this node.
          </div>
          <div v-else class="space-y-1">
            <div
              v-for="(alert, idx) in selectedNode.recent_alerts"
              :key="idx"
              class="flex items-start gap-3 p-2 bg-white/5 rounded-lg"
            >
              <span
                class="inline-block w-2 h-2 rounded-full mt-1.5 flex-shrink-0"
                :class="alertSeverityDot(alert.rule)"
              ></span>
              <div class="flex-1 min-w-0">
                <p class="text-sm text-white/80">{{ alert.message }}</p>
                <p class="text-xs text-white/30">{{ formatTimestamp(alert.timestamp) }}</p>
              </div>
            </div>
          </div>
        </div>
      </div>

      <!-- Section 5: Container Matrix -->
      <div class="glass-card p-5">
        <h3 class="text-sm font-medium text-white/80 mb-4">Container Matrix</h3>

        <div v-if="!nodes.length" class="text-white/40 text-sm py-4 text-center">
          No nodes to display.
        </div>
        <div v-else class="overflow-x-auto">
          <table class="fleet-matrix-table">
            <thead>
              <tr>
                <th class="fleet-matrix-header-cell">App</th>
                <th
                  v-for="node in sortedNodes"
                  :key="node.node_id"
                  class="fleet-matrix-header-cell font-mono"
                >
                  {{ node.node_id.slice(0, 6) }}
                </th>
              </tr>
            </thead>
            <tbody>
              <tr v-for="app in allAppIds" :key="app">
                <td class="fleet-matrix-cell text-white/70">{{ app }}</td>
                <td
                  v-for="node in sortedNodes"
                  :key="node.node_id"
                  class="fleet-matrix-cell text-center"
                >
                  <span v-if="getContainerState(node, app) === 'running'" class="text-green-400">&#10003;</span>
                  <span v-else-if="getContainerState(node, app) === 'stopped'" class="text-red-400">&#10007;</span>
                  <span v-else class="text-white/20">&mdash;</span>
                </td>
              </tr>
            </tbody>
          </table>
        </div>
      </div>

      <p class="text-xs text-white/30 mt-4 text-center">
        {{ autoRefresh ? 'Auto-refreshing every 60s' : 'Auto-refresh paused' }}
        &middot; Last updated {{ lastRefreshed ? timeAgo(lastRefreshed) : 'never' }}
      </p>
    </template>
  </div>
</template>

<script setup lang="ts">
import { ref, computed, onMounted, onUnmounted, watch } from 'vue'
import { rpcClient } from '@/api/rpc-client'
import LineChart from '@/components/LineChart.vue'
import type { ChartDataset } from '@/components/LineChart.vue'

// --- Types ---

interface FleetNode {
  node_id: string
  version: string
  uptime_secs: number
  cpu_cores: number
  cpu_pct: number
  mem_pct: number
  disk_pct: number
  container_count: number
  running_count: number
  federation_peers: number
  recent_alerts: Array<{ rule: string; message: string; timestamp: string }>
  containers: Array<{ id: string; state: string; version: string }>
  reported_at: string
}

interface FleetAlert {
  node_id: string
  rule: string
  message: string
  timestamp: string
}

interface NodeHistoryEntry {
  timestamp: string
  cpu_pct: number
  mem_pct: number
  disk_pct: number
}

type SortOption = 'status' | 'last-seen' | 'name'

// --- State ---

const loading = ref(true)
const errorMessage = ref('')
const nodes = ref<FleetNode[]>([])
const fleetAlerts = ref<FleetAlert[]>([])
const alertsLoading = ref(false)
const selectedNodeId = ref<string | null>(null)
const nodeHistory = ref<NodeHistoryEntry[]>([])
const nodeHistoryLoading = ref(false)
const autoRefresh = ref(true)
const lastRefreshed = ref('')
const sortBy = ref<SortOption>('status')
const chartWidth = ref(300)
let pollTimer: ReturnType<typeof setInterval> | null = null

const sortOptions: Array<{ label: string; value: SortOption }> = [
  { label: 'Status', value: 'status' },
  { label: 'Last Seen', value: 'last-seen' },
  { label: 'Name', value: 'name' },
]

// --- Computed ---

const onlineCount = computed(() => nodes.value.filter(n => isOnline(n.reported_at)).length)
const offlineCount = computed(() => nodes.value.length - onlineCount.value)
const healthyCount = computed(() => nodes.value.filter(n => n.recent_alerts.length === 0).length)

const fleetHealthPct = computed(() => {
  if (!nodes.value.length) return 0
  return Math.round((healthyCount.value / nodes.value.length) * 100)
})

const avgCpu = computed(() => {
  if (!nodes.value.length) return 0
  return nodes.value.reduce((sum, n) => sum + n.cpu_pct, 0) / nodes.value.length
})

const avgMem = computed(() => {
  if (!nodes.value.length) return 0
  return nodes.value.reduce((sum, n) => sum + n.mem_pct, 0) / nodes.value.length
})

const avgDisk = computed(() => {
  if (!nodes.value.length) return 0
  return nodes.value.reduce((sum, n) => sum + n.disk_pct, 0) / nodes.value.length
})

const selectedNode = computed(() => {
  if (!selectedNodeId.value) return null
  return nodes.value.find(n => n.node_id === selectedNodeId.value) ?? null
})

const sortedNodes = computed(() => {
  const sorted = [...nodes.value]
  switch (sortBy.value) {
    case 'status':
      // Offline first, then by last seen descending
      sorted.sort((a, b) => {
        const aOnline = isOnline(a.reported_at)
        const bOnline = isOnline(b.reported_at)
        if (aOnline !== bOnline) return aOnline ? 1 : -1
        return new Date(b.reported_at).getTime() - new Date(a.reported_at).getTime()
      })
      break
    case 'last-seen':
      sorted.sort((a, b) => new Date(b.reported_at).getTime() - new Date(a.reported_at).getTime())
      break
    case 'name':
      sorted.sort((a, b) => a.node_id.localeCompare(b.node_id))
      break
  }
  return sorted
})

const allAppIds = computed(() => {
  const appSet = new Set<string>()
  for (const node of nodes.value) {
    for (const c of node.containers) {
      appSet.add(c.id)
    }
  }
  return Array.from(appSet).sort()
})

// Node history chart datasets
const nodeHistoryLabels = computed(() => {
  return nodeHistory.value.map(h => {
    const d = new Date(h.timestamp)
    return `${d.getHours().toString().padStart(2, '0')}:${d.getMinutes().toString().padStart(2, '0')}`
  })
})

const nodeHistoryCpuDatasets = computed<ChartDataset[]>(() => [{
  label: 'CPU',
  data: nodeHistory.value.map(h => h.cpu_pct),
  color: '#fb923c',
}])

const nodeHistoryMemDatasets = computed<ChartDataset[]>(() => [{
  label: 'RAM',
  data: nodeHistory.value.map(h => h.mem_pct),
  color: '#3b82f6',
}])

const nodeHistoryDiskDatasets = computed<ChartDataset[]>(() => [{
  label: 'Disk',
  data: nodeHistory.value.map(h => h.disk_pct),
  color: '#a78bfa',
}])

// --- Utility Functions ---

function formatUptime(secs: number): string {
  if (secs < 60) return `${secs}s`
  const days = Math.floor(secs / 86400)
  const hours = Math.floor((secs % 86400) / 3600)
  const mins = Math.floor((secs % 3600) / 60)
  if (days > 0) return `${days}d ${hours}h`
  if (hours > 0) return `${hours}h ${mins}m`
  return `${mins}m`
}

function timeAgo(dateStr: string): string {
  const now = Date.now()
  const then = new Date(dateStr).getTime()
  const diffMs = now - then
  if (diffMs < 0) return 'just now'
  const diffSecs = Math.floor(diffMs / 1000)
  if (diffSecs < 60) return `${diffSecs}s ago`
  const diffMins = Math.floor(diffSecs / 60)
  if (diffMins < 60) return `${diffMins}m ago`
  const diffHours = Math.floor(diffMins / 60)
  if (diffHours < 24) return `${diffHours}h ago`
  const diffDays = Math.floor(diffHours / 24)
  return `${diffDays}d ago`
}

function isOnline(reportedAt: string): boolean {
  const thirtyMinMs = 30 * 60 * 1000
  return Date.now() - new Date(reportedAt).getTime() < thirtyMinMs
}

function healthBarClass(pct: number): string {
  if (pct >= 85) return 'monitoring-bar-danger'
  if (pct >= 60) return 'monitoring-bar-warn'
  return 'monitoring-bar-ok'
}

function healthTextClass(pct: number): string {
  if (pct >= 85) return 'fleet-text-danger'
  if (pct >= 60) return 'fleet-text-warn'
  return ''
}

function alertSeverityDot(rule: string): string {
  const critical = ['container_crash', 'disk_critical', 'node_offline']
  if (critical.includes(rule)) return 'bg-red-400'
  return 'bg-orange-400'
}

function alertTypeLabel(rule: string): string {
  const labels: Record<string, string> = {
    container_crash: 'Container Crash',
    disk_critical: 'Disk Critical',
    disk_warning: 'Disk Warning',
    ram_high: 'High RAM',
    cpu_high: 'High CPU',
    node_offline: 'Node Offline',
    version_mismatch: 'Version Mismatch',
  }
  return labels[rule] ?? rule
}

function formatTimestamp(ts: string): string {
  const d = new Date(ts)
  return d.toLocaleString()
}

function getContainerState(node: FleetNode, appId: string): string | null {
  const container = node.containers.find(c => c.id === appId)
  if (!container) return null
  return container.state
}

// --- Data Fetching ---

async function fetchFleetStatus() {
  try {
    const data = await rpcClient.call<{ nodes: FleetNode[] }>({
      method: 'telemetry.fleet-status',
    })
    if (data?.nodes) {
      nodes.value = data.nodes
      lastRefreshed.value = new Date().toISOString()
    }
  } catch (err) {
    if (loading.value) {
      errorMessage.value = err instanceof Error ? err.message : 'Failed to load fleet data'
    }
  }
}

async function fetchFleetAlerts() {
  alertsLoading.value = true
  try {
    const data = await rpcClient.call<{ alerts: FleetAlert[] }>({
      method: 'telemetry.fleet-alerts',
    })
    if (data?.alerts) {
      fleetAlerts.value = data.alerts
    }
  } catch {
    // Non-critical, retry on next poll
  } finally {
    alertsLoading.value = false
  }
}

async function fetchNodeHistory(nodeId: string) {
  nodeHistoryLoading.value = true
  nodeHistory.value = []
  try {
    const data = await rpcClient.call<{ history: NodeHistoryEntry[] }>({
      method: 'telemetry.fleet-node-history',
      params: { node_id: nodeId },
    })
    if (data?.history) {
      nodeHistory.value = data.history
    }
  } catch {
    // Non-critical
  } finally {
    nodeHistoryLoading.value = false
  }
}

async function refreshAll() {
  loading.value = !nodes.value.length
  errorMessage.value = ''
  await Promise.all([fetchFleetStatus(), fetchFleetAlerts()])
  loading.value = false
}

function selectNode(nodeId: string) {
  if (selectedNodeId.value === nodeId) {
    selectedNodeId.value = null
    nodeHistory.value = []
  } else {
    selectedNodeId.value = nodeId
    fetchNodeHistory(nodeId)
  }
}

function toggleAutoRefresh() {
  autoRefresh.value = !autoRefresh.value
  if (autoRefresh.value) {
    startAutoRefresh()
  } else {
    stopAutoRefresh()
  }
}

function startAutoRefresh() {
  stopAutoRefresh()
  pollTimer = setInterval(async () => {
    await Promise.all([fetchFleetStatus(), fetchFleetAlerts()])
    // Refresh selected node history if one is selected
    if (selectedNodeId.value) {
      await fetchNodeHistory(selectedNodeId.value)
    }
  }, 60000)
}

function stopAutoRefresh() {
  if (pollTimer) {
    clearInterval(pollTimer)
    pollTimer = null
  }
}

function exportFleetData() {
  const exportData = {
    exported_at: new Date().toISOString(),
    nodes: nodes.value,
    alerts: fleetAlerts.value,
  }
  const blob = new Blob([JSON.stringify(exportData, null, 2)], { type: 'application/json' })
  const url = URL.createObjectURL(blob)
  const a = document.createElement('a')
  a.href = url
  a.download = `fleet-telemetry-${new Date().toISOString().slice(0, 10)}.json`
  a.click()
  URL.revokeObjectURL(url)
}

function updateChartWidth() {
  const container = document.querySelector('.glass-card')
  if (container) {
    // For 3-column layout, approximate each chart container width
    const cardWidth = container.clientWidth
    chartWidth.value = Math.max(Math.floor((cardWidth - 80) / 3), 200)
  }
}

// Fetch node history when selection changes
watch(selectedNodeId, (newId) => {
  if (newId) {
    fetchNodeHistory(newId)
  } else {
    nodeHistory.value = []
  }
})

// --- Lifecycle ---

onMounted(async () => {
  updateChartWidth()
  window.addEventListener('resize', updateChartWidth)

  await refreshAll()

  if (autoRefresh.value) {
    startAutoRefresh()
  }
})

onUnmounted(() => {
  stopAutoRefresh()
  window.removeEventListener('resize', updateChartWidth)
})
</script>
