"use client"

import { useCallback, useEffect, useMemo, useState } from "react"
import { usePathname } from "next/navigation"
import Link from "next/link"
import { PageHeader } from "@/components/page-header"
import { OpsPanel } from "@/components/ops-panel"
import { KpiStatGrid } from "@/components/reui-kit/kpi-stat-grid"
import { StatusDot } from "@/components/status-dot"
import { StatusBadge } from "@/components/status-badge"
import { LatencyChart } from "@/components/dashboard/latency-chart"
import { BandwidthChart } from "@/components/dashboard/bandwidth-chart"
import { InternetPathMapCard } from "@/components/dashboard/internet-path-map"
import {
  servers as mockServers,
  pingProbes,
  dashLatency,
  traffic,
  serverFilterRulesets,
} from "@/lib/data"
import type { PingProbe, Server, ServerStatus, ServerType } from "@/lib/data"
import type { GreTunnel } from "@/lib/data"
import { useDataSource } from "@/lib/data-source"
import { Flag } from "@/components/flag"
import { AlertCircleIcon, AlertTriangleIcon, BellIcon, FilterIcon, GitMergeIcon, InfoIcon, DownloadIcon, ServerIcon } from "lucide-react"
import { Button, buttonVariants } from "@/components/ui/button"
import { DataPageCard } from "@/components/data-page-card"
import { DashboardActiveProbesDataGrid } from "@/components/data-grids/dashboard-active-probes-data-grid"
import { cn } from "@/lib/utils"
import { requestJson } from "@/shared/api/http-client"
import { buildLatencySeriesByProbeSource } from "@/lib/dashboard-latency"
import {
  buildDashboardInternetPath,
  type HomeWanRuntime,
  resolveDefaultRouteLookup,
  type InternetPathViewModel,
} from "@/lib/dashboard-internet-path"
import type { FiltersRulesetRow, RouteOptimizerSpeedProbe } from "@/lib/route-optimizer-data"
import { listEvents } from "@/shared/api/events"
import type { EventItem } from "@mmapp/contracts/events"

function makeApiFetch(backendUrl: string) {
  return async function apiFetch<T>(path: string, init?: RequestInit): Promise<T> {
    return requestJson<T>(backendUrl, path, init)
  }
}

function fmtIntRu(n: number): string {
  return n.toLocaleString("ru-RU")
}

function formatEventAge(iso: string): string {
  const ts = Date.parse(iso)
  if (!Number.isFinite(ts)) return "—"
  const diffMs = Math.max(0, Date.now() - ts)
  const minutes = Math.floor(diffMs / 60_000)
  if (minutes < 1) return "сейчас"
  if (minutes < 60) return `${minutes}м`
  const hours = Math.floor(minutes / 60)
  if (hours < 24) return `${hours}ч`
  const days = Math.floor(hours / 24)
  return `${days}д`
}

interface LiveKpiSnapshot {
  filters: { ruleTotal: number; serversWithRules: number } | null
  bgp: { prefixSum: number; establishedCount: number } | null
}

/** Синхронизируется со страницей мониторинга (mock) */
const MOCK_DASH_STARS_LS = "mm:dashboard-probe-ids"
const UPTIME_PROBES_CHANGED = "mm:uptime-probes-changed"

function readMockDashboardStarIds(): Set<string> {
  if (typeof window === "undefined") return new Set()
  try {
    const raw = localStorage.getItem(MOCK_DASH_STARS_LS)
    const arr = raw ? (JSON.parse(raw) as unknown) : []
    return new Set(Array.isArray(arr) ? arr.filter((x): x is string => typeof x === "string") : [])
  } catch {
    return new Set()
  }
}

interface BackendServerRow {
  id: number
  name: string
  host: string
  site: string
  country: string
  asn: string
  type: ServerType
  enabled: boolean
  status: "online" | "offline" | null
  latency: number | null
  os: string | null
  model: string | null
  sessions?: number
  wanUplinks?: Array<{
    id: string
    name: string
    isp: string
    iface: string
    ip: string
    maxDl: number
    maxUl: number
  }>
}

interface ApiGreTunnelRow {
  id: string
  name: string
  serverId: string
  localAddress: string
  remoteAddress: string
  localInnerIp: string
  remoteInnerIp: string
  poolId: string
  ipsec: null
  mtu: number
  keepaliveInterval: number
  keepaliveRetries: number
  dscp: "inherit" | number
  clampTcpMss: boolean
  allowFastPath: boolean
  comment: string
  enabled: boolean
  status: "up" | "down" | "degraded"
}

interface InternetPathSnapshotPayload {
  sampledAt: string
  servers: BackendServerRow[]
  greTunnels: ApiGreTunnelRow[]
  filtersRulesets: FiltersRulesetRow[]
  speedProbes: RouteOptimizerSpeedProbe[]
  routeLookupByServerId: Record<string, { gateway: string | null; routingMark: string | null } | null>
  wanRuntimeByHomeId: Record<string, HomeWanRuntime | null>
}

function apiGreToGreTunnel(t: ApiGreTunnelRow): GreTunnel {
  return {
    id: t.id,
    name: t.name,
    serverId: String(t.serverId),
    localAddress: t.localAddress,
    remoteAddress: t.remoteAddress,
    localInnerIp: t.localInnerIp,
    remoteInnerIp: t.remoteInnerIp,
    poolId: t.poolId || "live",
    ipsec: null,
    mtu: t.mtu,
    keepaliveInterval: t.keepaliveInterval,
    keepaliveRetries: t.keepaliveRetries,
    dscp: t.dscp,
    clampTcpMss: t.clampTcpMss,
    allowFastPath: t.allowFastPath,
    comment: t.comment,
    enabled: t.enabled,
    status: t.status,
  }
}

function mapBackendToServer(s: BackendServerRow): Server {
  const wanUplinks = Array.isArray(s.wanUplinks)
    ? s.wanUplinks
      .filter((w) => typeof w === "object" && w != null)
      .map((w, idx) => ({
        id: String(w.id || `wan-${s.id}-${idx + 1}`),
        name: String(w.name || `WAN${idx + 1}`),
        isp: String(w.isp || "—"),
        iface: String(w.iface || ""),
        ip: String(w.ip || ""),
        maxDl: Math.max(1, Math.round(Number(w.maxDl) || 100)),
        maxUl: Math.max(1, Math.round(Number(w.maxUl) || 100)),
      }))
    : []
  return {
    id: String(s.id),
    name: s.name || s.host,
    host: s.host,
    model: s.model ?? "—",
    os: s.os ?? "—",
    site: s.site || "—",
    country: s.country || "UN",
    asn: s.asn,
    type: s.type,
    enabled: s.enabled,
    status: (s.status ?? "offline") as ServerStatus,
    latency: s.latency != null ? Math.round(s.latency) : null,
    sessions: s.sessions ?? 0,
    wanUplinks,
  }
}

export default function DashboardPage() {
  const pathname = usePathname()
  const { mode, backendUrl, prefsHydrated } = useDataSource()
  const isLive = prefsHydrated && mode === "live"
  const apiFetch = useMemo(() => makeApiFetch(backendUrl), [backendUrl])

  const [liveProbes, setLiveProbes] = useState<PingProbe[] | null>(null)
  const [liveServersResolved, setLiveServersResolved] = useState<Server[] | null>(null)
  const [liveKpi, setLiveKpi] = useState<LiveKpiSnapshot | null>(null)
  const [probesLoading, setProbesLoading] = useState(false)
  const [probesError, setProbesError] = useState<string | null>(null)
  const [recentEvents, setRecentEvents] = useState<EventItem[]>([])
  const [eventsLoading, setEventsLoading] = useState(false)
  const [eventsError, setEventsError] = useState<string | null>(null)
  const [internetPath, setInternetPath] = useState<InternetPathViewModel | null>(null)
  const [internetPathLoading, setInternetPathLoading] = useState(false)
  const [internetPathError, setInternetPathError] = useState<string | null>(null)

  const probeServerCatalog = useMemo(() => {
    if (!prefsHydrated) return []
    if (!isLive) return mockServers
    return liveServersResolved ?? []
  }, [prefsHydrated, isLive, liveServersResolved])

  const fetchProbes = useCallback(async (silent: boolean) => {
    if (!isLive) return
    if (!silent) setProbesLoading(true)
    if (!silent) setInternetPathLoading(true)
    try {
      const overview = await apiFetch<{ probes: PingProbe[] }>("/api/uptime/overview?range=1h")
      setLiveProbes(overview.probes)
      setProbesError(null)
      setInternetPathError(null)
      let serversMapped: Server[] = []
      try {
        const backendServers = await apiFetch<BackendServerRow[]>("/api/servers")
        serversMapped = backendServers.map(mapBackendToServer)
        setLiveServersResolved(serversMapped)
      } catch {
        serversMapped = []
        setLiveServersResolved([])
      }

      const [fr, br, ipRes] = await Promise.allSettled([
        apiFetch<{ rulesets: Array<{ rules?: unknown[] }> }>("/api/filters/rules"),
        apiFetch<Array<{ state?: string; prefixesRx?: number }>>("/api/bgp/sessions"),
        apiFetch<{ snapshot: InternetPathSnapshotPayload | null }>("/api/internet-path/latest"),
      ])

      let filtersPart: LiveKpiSnapshot["filters"] = null
      if (fr.status === "fulfilled") {
        const rs = fr.value.rulesets ?? []
        const ruleTotal = rs.reduce((n, x) => n + (Array.isArray(x.rules) ? x.rules.length : 0), 0)
        const serversWithRules = rs.filter((x) => Array.isArray(x.rules) && x.rules.length > 0).length
        filtersPart = { ruleTotal, serversWithRules }
      }

      let bgpPart: LiveKpiSnapshot["bgp"] = null
      if (br.status === "fulfilled") {
        let prefixSum = 0
        let establishedCount = 0
        for (const s of br.value) {
          const st = String(s.state ?? "")
          if (/established/i.test(st)) {
            establishedCount += 1
            prefixSum += Number(s.prefixesRx ?? 0)
          }
        }
        bgpPart = { prefixSum, establishedCount }
      }

      setLiveKpi({ filters: filtersPart, bgp: bgpPart })
      if (serversMapped.length > 0) {
        const snap = ipRes.status === "fulfilled" ? ipRes.value.snapshot : null
        if (snap) {
          const snapshotServers = snap.servers.map(mapBackendToServer)
          setInternetPath(buildDashboardInternetPath({
            servers: snapshotServers,
            greTunnels: (snap.greTunnels ?? []).map(apiGreToGreTunnel),
            probes: snap.speedProbes ?? [],
            filtersRulesets: snap.filtersRulesets ?? [],
            routeLookupByServerId: snap.routeLookupByServerId ?? {},
            wanRuntimeByHomeId: snap.wanRuntimeByHomeId ?? {},
          }))
        } else {
          const filterRulesets: FiltersRulesetRow[] =
            fr.status === "fulfilled"
              ? (fr.value.rulesets as FiltersRulesetRow[] ?? [])
              : []
          const homes = serversMapped.filter((s) => s.type === "home-router")
          const lookups = await Promise.all(
            homes.map(async (h) => ({
              id: h.id,
              lookup: await resolveDefaultRouteLookup(apiFetch, h.id),
            })),
          )
          const wanRuntimeRows = await Promise.all(
            homes.map(async (h) => {
              try {
                const rt = await apiFetch<HomeWanRuntime>(`/api/servers/${h.id}/wan-runtime`)
                return { id: h.id, runtime: rt }
              } catch {
                return { id: h.id, runtime: null }
              }
            }),
          )
          const speedRes = await apiFetch<{ probes?: RouteOptimizerSpeedProbe[] }>("/api/uptime/speed-probes").catch(() => ({ probes: [] }))
          const greRes = await apiFetch<{ tunnels?: ApiGreTunnelRow[] }>("/api/filters/gre-tunnels").catch(() => ({ tunnels: [] }))
          const lookupById = Object.fromEntries(lookups.map((x) => [x.id, x.lookup]))
          const wanRuntimeById = Object.fromEntries(wanRuntimeRows.map((x) => [x.id, x.runtime]))
          setInternetPath(buildDashboardInternetPath({
            servers: serversMapped,
            greTunnels: (greRes.tunnels ?? []).map(apiGreToGreTunnel),
            probes: speedRes.probes ?? [],
            filtersRulesets: filterRulesets,
            routeLookupByServerId: lookupById,
            wanRuntimeByHomeId: wanRuntimeById,
          }))
        }
      } else {
        setInternetPath(null)
      }
    } catch (e) {
      const msg = e instanceof Error ? e.message : "Не удалось загрузить пробы"
      setProbesError(msg)
      setLiveKpi(null)
      setInternetPathError(msg)
      if (!silent) {
        setLiveProbes(null)
        setLiveServersResolved(null)
        setInternetPath(null)
      }
    } finally {
      if (!silent) setProbesLoading(false)
      if (!silent) setInternetPathLoading(false)
    }
  }, [apiFetch, isLive])

  const fetchRecentEvents = useCallback(async (silent: boolean) => {
    if (!isLive) {
      setRecentEvents([])
      setEventsError(null)
      return
    }
    if (!silent) setEventsLoading(true)
    try {
      const rows = await listEvents(backendUrl, { limit: 8 })
      setRecentEvents(rows)
      setEventsError(null)
    } catch (error) {
      setRecentEvents([])
      setEventsError(error instanceof Error ? error.message : "Не удалось загрузить события")
    } finally {
      if (!silent) setEventsLoading(false)
    }
  }, [backendUrl, isLive])

  useEffect(() => {
    if (!isLive) {
      queueMicrotask(() => {
        setLiveProbes(null)
        setLiveServersResolved(null)
        setLiveKpi(null)
        setProbesError(null)
        setInternetPath(null)
        setInternetPathError(null)
      })
      return
    }
    let cancelled = false
    queueMicrotask(() => {
      if (cancelled) return
      void fetchProbes(false)
    })
    return () => { cancelled = true }
  }, [isLive, fetchProbes])

  useEffect(() => {
    queueMicrotask(() => {
      void fetchRecentEvents(false)
    })
  }, [fetchRecentEvents])

  useEffect(() => {
    const id = setInterval(() => {
      queueMicrotask(() => { void fetchRecentEvents(true) })
    }, 20_000)
    return () => clearInterval(id)
  }, [fetchRecentEvents])

  useEffect(() => {
    if (!isLive) return
    const id = setInterval(() => {
      queueMicrotask(() => { void fetchProbes(true) })
    }, 60_000)
    return () => clearInterval(id)
  }, [isLive, fetchProbes])

  /** Live: после звезды на мониторинге данные на дашборде должны подтянуться сразу (раньше только при монтировании и раз в 60 с). */
  useEffect(() => {
    if (!isLive) return
    queueMicrotask(() => { void fetchProbes(true) })
  }, [pathname, isLive, fetchProbes])

  const [mockDashEpoch, setMockDashEpoch] = useState(0)
  useEffect(() => {
    const bumpMock = () => setMockDashEpoch((x) => x + 1)
    const onStorage = (e: StorageEvent) => {
      if (e.key === MOCK_DASH_STARS_LS) bumpMock()
    }
    const onVis = () => {
      if (document.visibilityState === "visible") bumpMock()
    }
    const onUptimeChanged = () => {
      bumpMock()
      if (isLive) void fetchProbes(true)
    }
    window.addEventListener(UPTIME_PROBES_CHANGED, onUptimeChanged)
    window.addEventListener("storage", onStorage)
    document.addEventListener("visibilitychange", onVis)
    return () => {
      window.removeEventListener(UPTIME_PROBES_CHANGED, onUptimeChanged)
      window.removeEventListener("storage", onStorage)
      document.removeEventListener("visibilitychange", onVis)
    }
  }, [isLive, fetchProbes])
  useEffect(() => {
    queueMicrotask(() => setMockDashEpoch((x) => x + 1))
  }, [pathname])

  const mockActiveProbes = useMemo(() => {
    void mockDashEpoch
    const stars = readMockDashboardStarIds()
    return pingProbes.filter(p => p.enabled && stars.has(p.id))
  }, [mockDashEpoch])

  const activeProbesTable = useMemo(() => {
    if (!prefsHydrated) return []
    if (!isLive) return mockActiveProbes
    if (liveProbes === null && probesLoading) return []
    if (liveProbes === null) return []
    return liveProbes.filter((p) => p.enabled && p.showOnDashboard === true)
  }, [prefsHydrated, isLive, liveProbes, probesLoading, mockActiveProbes])

  const probesSubtitle = useMemo(() => {
    if (!prefsHydrated) return "Загрузка…"
    if (!isLive) {
      const starred = mockActiveProbes.length
      return starred > 0
        ? `${starred} на дашборде · мок-данные · отметьте звезды в мониторинге`
        : "Нет проб на дашборде · отметьте звёздочкой на странице мониторинга"
    }
    if (probesError && liveProbes === null) return probesError
    const n = activeProbesTable.length
    return n > 0
      ? `${n} на дашборде · последний час (API)`
      : "Нет проб на дашборде · отметьте звёздочкой на странице мониторинга"
  }, [prefsHydrated, isLive, mockActiveProbes.length, probesError, liveProbes, activeProbesTable.length])

  /** Карточка «Состояние серверов»: в Live — `/api/servers` (тот же запрос, что и для каталога проб). */
  const serverStatusModel = useMemo(() => {
    if (!prefsHydrated) {
      return { kind: "loading" as const, subtitle: "Загрузка…" }
    }
    if (!isLive) {
      return {
        kind: "mock" as const,
        servers: mockServers.slice(0, 5),
        subtitle: `${mockServers.length} узлов MikroTik · демо`,
      }
    }
    if (liveServersResolved === null) {
      if (probesLoading) {
        return { kind: "loading" as const, subtitle: "Загрузка…" }
      }
      return {
        kind: "unavailable" as const,
        subtitle: probesError ? "Нет данных · проверьте backend" : "Нет данных о серверах",
      }
    }
    const servers = [...liveServersResolved].sort((a, b) => {
      if (a.enabled !== b.enabled) return a.enabled ? -1 : 1
      return a.name.localeCompare(b.name, "ru")
    })
    const onlineN = servers.filter((s) => s.status === "online").length
    return {
      kind: "live" as const,
      servers,
      subtitle: `${servers.length} узлов · ${onlineN} онлайн · API`,
    }
  }, [prefsHydrated, isLive, liveServersResolved, probesLoading, probesError])

  const latencyChartBlock = useMemo(() => {
    if (!prefsHydrated) {
      return { kind: "loading" as const }
    }
    if (!isLive) {
      return {
        kind: "mock" as const,
        series: dashLatency,
        labels: undefined as Record<string, string> | undefined,
        subtitle:
          "Последние 60 минут · ping от монитора → серверы MikroTik · демо",
      }
    }
    if (liveProbes === null && probesLoading) {
      return { kind: "loading" as const }
    }
    if (!liveProbes?.length) {
      return {
        kind: "empty" as const,
        message: "Нет данных проб. Откройте мониторинг и проверьте сборщик uptime.",
      }
    }
    const catalog = liveServersResolved ?? []
    const { series, labels } = buildLatencySeriesByProbeSource(
      liveProbes,
      catalog,
      { maxServers: 8, points: 60 },
    )
    if (Object.keys(series).length === 0) {
      return {
        kind: "empty" as const,
        message:
          "Нет включённых проб с историей RTT. Включите пробы на странице «Мониторинг».",
      }
    }
    return {
      kind: "live" as const,
      series,
      labels,
      subtitle:
        "Средний RTT по источникам проб · окно 1 ч · до 8 узлов · API",
    }
  }, [prefsHydrated, isLive, liveProbes, probesLoading, liveServersResolved])

  const dashboardKpi = useMemo(() => {
    const sparkSrv = [5, 5, 6, 6, 5, 6, 6, 6, 7, 6, 6, 6, 6, 6, 6, 6, 6]
    const sparkFlt = [3, 4, 4, 5, 4, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5]
    const sparkBgp = [7800, 7900, 8000, 8100, 8050, 8120, 8200, 8240, 8300, 8350, 8380, 8400, 8420, 8430, 8432]
    const sparkAlt = [1, 2, 2, 3, 3, 4, 5, 4, 4, 4, 3, 3, 4, 4, 4]

    const loadingKpi = (sparkColor: string) => ({
      value: "—",
      unit: undefined as string | undefined,
      delta: "Загрузка…",
      deltaDir: "up" as const,
      spark: undefined as number[] | undefined,
      sparkColor,
    })

    if (!prefsHydrated) {
      return {
        servers: loadingKpi("var(--chart-line-1)"),
        filters: loadingKpi("var(--chart-line-2)"),
        bgp: loadingKpi("var(--chart-line-4)"),
        alerts: loadingKpi("var(--chart-5)"),
      }
    }

    if (!isLive) {
      const totalSrv = mockServers.length
      const onlineSrv = mockServers.filter((s) => s.status === "online").length
      const ruleTotal = serverFilterRulesets.reduce((n, rs) => n + rs.rules.length, 0)
      const serversWithRules = serverFilterRulesets.filter((rs) => rs.rules.length > 0).length
      const en = pingProbes.filter((p) => p.enabled)
      const down = en.filter((p) => p.status === "down").length
      const warn = en.filter((p) => p.status === "warn").length
      return {
        servers: {
          value: String(onlineSrv),
          unit: `/ ${totalSrv}`,
          delta: `${totalSrv - onlineSrv} offline`,
          deltaDir: onlineSrv < totalSrv ? ("down" as const) : ("up" as const),
          spark: sparkSrv,
          sparkColor: "var(--chart-line-1)",
        },
        filters: {
          value: String(ruleTotal),
          unit: `/ ${serverFilterRulesets.length}`,
          delta: `${serversWithRules} серверов с правилами · демо`,
          deltaDir: "up" as const,
          spark: sparkFlt,
          sparkColor: "var(--chart-line-2)",
        },
        bgp: {
          value: "8 432",
          unit: undefined as string | undefined,
          delta: "демо · не из API",
          deltaDir: "up" as const,
          spark: sparkBgp,
          sparkColor: "var(--chart-line-4)",
        },
        alerts: {
          value: String(down + warn),
          unit: undefined as string | undefined,
          delta: `${down} down · ${warn} warn`,
          deltaDir: down + warn > 0 ? ("down" as const) : ("up" as const),
          spark: sparkAlt,
          sparkColor: "var(--chart-5)",
        },
      }
    }

    const liveDataPending = liveServersResolved === null || liveProbes === null
    const loadingBlock = liveDataPending || (probesLoading && liveProbes === null)
    const srvList = liveServersResolved ?? []
    const totalSrv = srvList.length
    const onlineSrv = srvList.filter((s) => s.status === "online").length
    const offlineSrv = Math.max(0, totalSrv - onlineSrv)

    const en = liveProbes?.filter((p) => p.enabled) ?? []
    const down = en.filter((p) => p.status === "down").length
    const warn = en.filter((p) => p.status === "warn").length

    const filters = liveKpi?.filters
    const bgp = liveKpi?.bgp

    return {
      servers: {
        value: loadingBlock ? "—" : String(onlineSrv),
        unit: loadingBlock ? undefined : `/ ${totalSrv}`,
        delta: loadingBlock
          ? "Загрузка…"
          : offlineSrv > 0
            ? `${offlineSrv} offline · опрос API`
            : totalSrv > 0
              ? "Все узлы online в последнем опросе"
              : "Нет серверов в базе",
        deltaDir: offlineSrv > 0 ? ("down" as const) : ("up" as const),
        spark: undefined as number[] | undefined,
        sparkColor: "var(--chart-line-1)",
      },
      filters: {
        value: loadingBlock ? "—" : filters ? String(filters.ruleTotal) : "—",
        unit:
          filters && filters.serversWithRules > 0
            ? `на ${filters.serversWithRules} серв.`
            : undefined,
        delta: loadingBlock
          ? "Загрузка…"
          : filters
            ? `${filters.serversWithRules} серверов с правилами · /api/filters/rules`
            : "Не удалось загрузить правила",
        deltaDir: filters ? ("up" as const) : ("down" as const),
        spark: undefined as number[] | undefined,
        sparkColor: "var(--chart-line-2)",
      },
      bgp: {
        value: loadingBlock ? "—" : bgp ? fmtIntRu(bgp.prefixSum) : "—",
        unit: undefined as string | undefined,
        delta: loadingBlock
          ? "Загрузка…"
          : bgp
            ? `Σ prefixes Rx · ${bgp.establishedCount} Established · /api/bgp/sessions`
            : "Не удалось загрузить BGP",
        deltaDir: (bgp ? "up" : "down") as "up" | "down",
        spark: undefined as number[] | undefined,
        sparkColor: "var(--chart-line-4)",
      },
      alerts: {
        value: loadingBlock ? "—" : String(down + warn),
        unit: undefined as string | undefined,
        delta: loadingBlock
          ? "Загрузка…"
          : `${down} down · ${warn} warn · включённые пробы`,
        deltaDir: down + warn > 0 ? ("down" as const) : ("up" as const),
        spark: undefined as number[] | undefined,
        sparkColor: "var(--chart-5)",
      },
    }
  }, [prefsHydrated, isLive, liveServersResolved, liveKpi, liveProbes, probesLoading])

  return (
    <div className="flex flex-col h-full">
      <PageHeader
        crumbs={[{ label: "Обзор" }, { label: "Дашборд" }]}
        actions={
          <>
            <Button variant="outline" size="sm"><DownloadIcon className="size-4" />Экспорт</Button>
          </>
        }
      />

      <div className="flex-1 overflow-y-auto">
        <div className="p-6 flex flex-col gap-6">

          <KpiStatGrid
            aria-label="Сводка дашборда"
            items={[
              {
                id: "servers",
                label: "Серверы онлайн",
                value: dashboardKpi.servers.unit
                  ? `${dashboardKpi.servers.value} ${dashboardKpi.servers.unit}`
                  : dashboardKpi.servers.value,
                hint: dashboardKpi.servers.delta,
                icon: <ServerIcon className="size-4" />,
                iconClassName: "text-muted-foreground",
                variant: dashboardKpi.servers.deltaDir === "down" ? "warning" : "default",
              },
              {
                id: "filters",
                label: "Активные фильтры",
                value: dashboardKpi.filters.unit
                  ? `${dashboardKpi.filters.value} ${dashboardKpi.filters.unit}`
                  : dashboardKpi.filters.value,
                hint: dashboardKpi.filters.delta,
                icon: <FilterIcon className="size-4" />,
                iconClassName: "text-info",
              },
              {
                id: "bgp",
                label: "BGP-префиксы",
                value: dashboardKpi.bgp.unit
                  ? `${dashboardKpi.bgp.value} ${dashboardKpi.bgp.unit}`
                  : dashboardKpi.bgp.value,
                hint: dashboardKpi.bgp.delta,
                icon: <GitMergeIcon className="size-4" />,
                iconClassName: "text-primary",
              },
              {
                id: "alerts",
                label: "Активные алерты",
                value: dashboardKpi.alerts.unit
                  ? `${dashboardKpi.alerts.value} ${dashboardKpi.alerts.unit}`
                  : dashboardKpi.alerts.value,
                hint: dashboardKpi.alerts.delta,
                icon: <BellIcon className="size-4" />,
                iconClassName: dashboardKpi.alerts.deltaDir === "down" ? "text-destructive" : "text-muted-foreground",
                variant: dashboardKpi.alerts.deltaDir === "down" ? "destructive" : "default",
              },
            ]}
          />

          {/* Latency chart + Events */}
          <div className="grid grid-cols-1 lg:grid-cols-[2fr_1fr] gap-4">
            <OpsPanel
              title="Задержка до серверов"
              description={
                latencyChartBlock.kind === "mock" || latencyChartBlock.kind === "live"
                  ? latencyChartBlock.subtitle
                  : latencyChartBlock.kind === "loading"
                    ? "Загрузка…"
                    : "Нет серии RTT для графика"
              }
              contentClassName="px-3 pb-3"
            >
                {latencyChartBlock.kind === "loading" && (
                  <div
                    className="w-full rounded-md bg-muted/50 animate-pulse"
                    style={{ height: 220 }}
                  />
                )}
                {latencyChartBlock.kind === "empty" && (
                  <p className="text-sm text-muted-foreground py-10 text-center px-4">
                    {latencyChartBlock.message}
                  </p>
                )}
                {(latencyChartBlock.kind === "mock" || latencyChartBlock.kind === "live") && (
                  <LatencyChart
                    series={latencyChartBlock.series}
                    labels={latencyChartBlock.labels}
                  />
                )}
            </OpsPanel>

            <OpsPanel
              title="Последние события"
              description="Система и BGP-активность"
              headerRight={
                  <Link
                    href="/alerts"
                    className={cn(buttonVariants({ variant: "ghost", size: "sm" }), "text-xs h-7")}
                  >
                    Все →
                  </Link>
              }
            >
                <div className="divide-y divide-border">
                  {eventsLoading && recentEvents.length === 0 && (
                    <div className="px-5 py-6 text-sm text-muted-foreground">Загрузка событий...</div>
                  )}
                  {eventsError && recentEvents.length === 0 && (
                    <div className="px-5 py-6 text-sm text-destructive">{eventsError}</div>
                  )}
                  {!eventsLoading && !eventsError && recentEvents.length === 0 && (
                    <div className="px-5 py-6 text-sm text-muted-foreground">Событий пока нет.</div>
                  )}
                  {recentEvents.map((e) => (
                    <div key={e.id} className="grid grid-cols-[20px_1fr_auto] gap-3 px-5 py-3 items-start">
                      <div className="mt-0.5">
                        {e.level === "critical" && <AlertCircleIcon className="size-4 text-destructive" />}
                        {e.level === "warning"  && <AlertTriangleIcon className="size-4 text-[var(--status-degraded)]" />}
                        {e.level === "info"     && <InfoIcon className="size-4 text-[var(--chart-1)]" />}
                      </div>
                      <div>
                        <p className="text-[13px] font-medium leading-tight">{e.title}</p>
                        <p className="text-xs text-muted-foreground mt-0.5 leading-snug">{e.message}</p>
                      </div>
                      <span className="text-[11px] font-mono text-muted-foreground">{formatEventAge(e.createdAt)}</span>
                    </div>
                  ))}
                </div>
            </OpsPanel>
          </div>

          {/* Bandwidth + Server status */}
          <div className="grid grid-cols-1 lg:grid-cols-[3fr_2fr] gap-4">
            <OpsPanel
              title="Пропускная способность"
              description="Суммарный RX / TX по всем серверам"
              headerRight={
                  <div className="flex items-center gap-3 text-xs">
                    <span className="flex items-center gap-1.5"><span className="w-3 h-0.5 bg-foreground/80 rounded inline-block" />RX 318 Мбит/с</span>
                    <span className="flex items-center gap-1.5"><span className="w-3 h-0.5 bg-[var(--chart-tx)] rounded inline-block" />TX 244 Мбит/с</span>
                  </div>
              }
              contentClassName="px-3 pb-3"
            >
                <BandwidthChart rx={traffic.rx} tx={traffic.tx} />
            </OpsPanel>

            <OpsPanel
              title="Состояние серверов"
              description={
                <span className="truncate" title={serverStatusModel.subtitle}>
                  {serverStatusModel.subtitle}
                </span>
              }
              headerRight={
                  <Link
                    href="/servers"
                    className={cn(buttonVariants({ variant: "outline", size: "sm" }), "h-7 text-xs shrink-0")}
                  >
                    Управление →
                  </Link>
              }
            >
                <div className="divide-y divide-border">
                  {serverStatusModel.kind === "loading" && (
                    <>
                      {Array.from({ length: 5 }, (_, i) => (
                        <div key={i} className="grid grid-cols-[10px_1fr_auto_auto] gap-3 px-5 py-2.5 items-center">
                          <div className="size-2 rounded-full bg-muted animate-pulse" />
                          <div className="space-y-2 min-w-0">
                            <div className="h-4 rounded bg-muted/80 animate-pulse max-w-[180px]" />
                            <div className="h-3 rounded bg-muted/60 animate-pulse max-w-[240px]" />
                          </div>
                          <div className="h-4 w-12 rounded bg-muted/60 animate-pulse" />
                          <div className="h-5 w-16 rounded bg-muted/60 animate-pulse justify-self-end" />
                        </div>
                      ))}
                    </>
                  )}
                  {serverStatusModel.kind === "unavailable" && (
                    <div className="px-5 py-8 text-center text-sm text-muted-foreground">
                      {serverStatusModel.subtitle}
                    </div>
                  )}
                  {(serverStatusModel.kind === "mock" || serverStatusModel.kind === "live") && serverStatusModel.servers.length === 0 && (
                    <div className="px-5 py-8 text-center text-sm text-muted-foreground">
                      Нет серверов в базе. Добавьте узел на странице «Серверы».
                    </div>
                  )}
                  {(serverStatusModel.kind === "mock" || serverStatusModel.kind === "live") && serverStatusModel.servers.map((s) => (
                    <div
                      key={s.id}
                      className={cn(
                        "grid grid-cols-[10px_1fr_auto_auto] gap-3 px-5 py-2.5 items-center",
                        !s.enabled && "opacity-60",
                      )}
                    >
                      <StatusDot status={s.status} pulse={s.status === "online"} />
                      <div className="min-w-0">
                        <p className="text-[13px] font-medium leading-tight truncate">{s.name}</p>
                        <p className="text-[11px] font-mono text-muted-foreground flex items-center gap-1 truncate">
                          <Flag code={s.country} className="not-mono shrink-0" />
                          <span className="truncate">{s.host} · {s.site} · {s.asn}</span>
                        </p>
                      </div>
                      <span className={`text-xs font-mono tabular-nums shrink-0 ${s.latency == null ? "text-[var(--status-offline-fg)]" : s.latency > 60 ? "text-[var(--status-degraded-fg)]" : "text-muted-foreground"}`}>
                        {s.latency == null ? "недоступен" : `${s.latency}мс`}
                      </span>
                      <StatusBadge status={s.status} />
                    </div>
                  ))}
                </div>
            </OpsPanel>
          </div>

          <div className="grid grid-cols-1 gap-4">
            {internetPathError && isLive && (
              <div className="text-sm text-destructive">{internetPathError}</div>
            )}
            {internetPathLoading && isLive && !internetPath && (
              <div className="h-24 rounded-md bg-muted/40 animate-pulse" />
            )}
            {(!isLive || internetPath || !internetPathLoading) && (
              <InternetPathMapCard model={internetPath} />
            )}
          </div>

          {/* Ping probes table */}
          <OpsPanel
            title="Активные пробы"
            description={
              <span className={probesError && isLive ? "text-destructive" : undefined}>
                {probesSubtitle}
              </span>
            }
            headerRight={
                <Link
                  href="/uptime"
                  className={cn(buttonVariants({ variant: "outline", size: "sm" }), "h-7 text-xs")}
                >
                  Открыть монитор →
                </Link>
            }
          >
              <DataPageCard className="rounded-none border-0 shadow-none">
                <DashboardActiveProbesDataGrid
                  probes={activeProbesTable}
                  catalog={probeServerCatalog}
                  isLoading={isLive && probesLoading && liveProbes === null}
                  emptyDescription={
                    isLive && probesError
                      ? "Нет данных о пробах. Проверьте сборщик uptime и настройки проб на странице мониторинга."
                      : "Нет проб с звездой на дашборде. Включите пробу и отметьте ★ в разделе «Мониторинг»."
                  }
                />
              </DataPageCard>
          </OpsPanel>

        </div>
      </div>
    </div>
  )
}
