"use client"

import { Fragment, useState, useMemo, useEffect } from "react"
import { PageHeader } from "@/components/page-header"
import { DataPageToolbar } from "@/components/data-page-toolbar"
import { BgpSessionsDataGrid } from "@/components/data-grids/bgp-sessions-data-grid"
import type { Filter } from "@/components/reui/filters"
import { applyReuiFilters } from "@/lib/data-filters/apply-reui-filters"
import { BGP_FILTER_FIELDS } from "@/lib/data-filters/bgp-filter-fields"
import type { BgpSessionRow, BgpState, BgpType } from "@/lib/bgp/types"
import { BGP_AS_NAMES } from "@/lib/bgp/types"
import { Frame, FramePanel } from "@/components/reui/frame"
import { KpiStatGrid } from "@/components/reui-kit/kpi-stat-grid"
import { OpsPanel } from "@/components/ops-panel"
import { DataPageCard } from "@/components/data-page-card"
import { Button } from "@/components/ui/button"
import { Input } from "@/components/ui/input"
import { Alert, AlertDescription, AlertTitle } from "@/components/ui/alert"
import { cn } from "@/lib/utils"
import {
  RefreshCwIcon, DownloadIcon, SearchIcon,
  ActivityIcon, BarChart3Icon, ChevronRightIcon, ChevronDownIcon,
  ArrowDownIcon, ClipboardCopyIcon, ServerIcon,
  XIcon, AlertCircleIcon, GitMergeIcon, CheckCircleIcon,
} from "lucide-react"
import { useDataSource } from "@/lib/data-source"
import { requestJson } from "@/shared/api/http-client"

// ─── types ────────────────────────────────────────────────────────────────────

type BgpSession = BgpSessionRow
type BgpTab    = "sessions" | "routers" | "analytics"
type StateFilter = "all" | BgpState
type TypeFilter  = "all" | BgpType

const AS_NAMES = BGP_AS_NAMES
// ─── mock data ────────────────────────────────────────────────────────────────

const SESSIONS: BgpSession[] = [
  // ── srv1 (mt-msk-core-01) ────────────────────────────────────────────
  {
    id: "s1", serverId: "srv1", serverLabel: "mt-msk-core-01", serverSite: "MSK",
    peerIp: "185.1.1.1", remoteAs: 8359, localAs: 65001,
    routerId: "185.1.1.1", description: "МТС — upstream transit",
    state: "Established", type: "eBGP", afi: "IPv4 Unicast",
    uptime: "14д 6ч 22м", holdTime: 90, keepalive: 30,
    prefixesRx: 280241, prefixesTx: 42, prefixesActive: 277894,
    inputMessages: 1842204, outputMessages: 14412,
    capabilities: ["4-byte-AS", "Route Refresh", "MP-BGP", "ADD-PATH"],
    lastError: null,
  },
  {
    id: "s2", serverId: "srv1", serverLabel: "mt-msk-core-01", serverSite: "MSK",
    peerIp: "77.88.8.1", remoteAs: 13238, localAs: 65001,
    routerId: "77.88.44.1", description: "Яндекс — IX peering",
    state: "Established", type: "eBGP", afi: "IPv4 Unicast",
    uptime: "12д 3ч 11м", holdTime: 90, keepalive: 30,
    prefixesRx: 11840, prefixesTx: 42, prefixesActive: 11840,
    inputMessages: 184220, outputMessages: 14100,
    capabilities: ["4-byte-AS", "Route Refresh", "MP-BGP"],
    lastError: null,
  },
  {
    id: "s3", serverId: "srv1", serverLabel: "mt-msk-core-01", serverSite: "MSK",
    peerIp: "10.200.0.1", remoteAs: 65001, localAs: 65001,
    routerId: "10.0.0.2", description: "iBGP → SPB-EDGE",
    state: "Established", type: "iBGP", afi: "IPv4 Unicast",
    uptime: "8д 14ч 5м", holdTime: 90, keepalive: 30,
    prefixesRx: 620, prefixesTx: 280283, prefixesActive: 620,
    inputMessages: 48210, outputMessages: 512800,
    capabilities: ["4-byte-AS", "Route Refresh", "MP-BGP", "Route-Target Constraint"],
    lastError: null,
  },
  {
    id: "s4", serverId: "srv1", serverLabel: "mt-msk-core-01", serverSite: "MSK",
    peerIp: "10.200.1.1", remoteAs: 65001, localAs: 65001,
    routerId: "10.0.0.3", description: "iBGP → FRA-EDGE",
    state: "Established", type: "iBGP", afi: "IPv4 Unicast",
    uptime: "12д 2ч 18м", holdTime: 90, keepalive: 30,
    prefixesRx: 512, prefixesTx: 280283, prefixesActive: 512,
    inputMessages: 41022, outputMessages: 488200,
    capabilities: ["4-byte-AS", "Route Refresh", "MP-BGP"],
    lastError: null,
  },
  {
    id: "s5", serverId: "srv1", serverLabel: "mt-msk-core-01", serverSite: "MSK",
    peerIp: "10.200.2.1", remoteAs: 65001, localAs: 65001,
    routerId: "10.0.0.4", description: "iBGP → AMS-EDGE",
    state: "Active", type: "iBGP", afi: "IPv4 Unicast",
    uptime: null, holdTime: 90, keepalive: 30,
    prefixesRx: 0, prefixesTx: 0, prefixesActive: 0,
    inputMessages: 0, outputMessages: 0,
    capabilities: [],
    lastError: "Hold timer expired",
  },

  // ── srv2 (mt-spb-edge-01) ────────────────────────────────────────────
  {
    id: "s6", serverId: "srv2", serverLabel: "mt-spb-edge-01", serverSite: "SPB",
    peerIp: "195.54.55.1", remoteAs: 12389, localAs: 65001,
    routerId: "195.54.55.1", description: "Ростелеком — upstream",
    state: "Established", type: "eBGP", afi: "IPv4 Unicast",
    uptime: "9д 1ч 44м", holdTime: 90, keepalive: 30,
    prefixesRx: 192440, prefixesTx: 42, prefixesActive: 191002,
    inputMessages: 982440, outputMessages: 12200,
    capabilities: ["4-byte-AS", "Route Refresh", "MP-BGP"],
    lastError: null,
  },
  {
    id: "s7", serverId: "srv2", serverLabel: "mt-spb-edge-01", serverSite: "SPB",
    peerIp: "10.200.0.1", remoteAs: 65001, localAs: 65001,
    routerId: "10.0.0.1", description: "iBGP → MSK-CORE",
    state: "Established", type: "iBGP", afi: "IPv4 Unicast",
    uptime: "8д 14ч 3м", holdTime: 90, keepalive: 30,
    prefixesRx: 280283, prefixesTx: 620, prefixesActive: 277000,
    inputMessages: 512800, outputMessages: 48210,
    capabilities: ["4-byte-AS", "Route Refresh", "MP-BGP", "Route-Target Constraint"],
    lastError: null,
  },

  // ── srv3 (mt-fra-edge-01) ────────────────────────────────────────────
  {
    id: "s8", serverId: "srv3", serverLabel: "mt-fra-edge-01", serverSite: "FRA",
    peerIp: "91.108.4.1", remoteAs: 24940, localAs: 65001,
    routerId: "91.108.4.1", description: "Hetzner — upstream",
    state: "Established", type: "eBGP", afi: "IPv4 Unicast",
    uptime: "21д 7ч 12м", holdTime: 90, keepalive: 30,
    prefixesRx: 218820, prefixesTx: 42, prefixesActive: 215400,
    inputMessages: 1184200, outputMessages: 11800,
    capabilities: ["4-byte-AS", "Route Refresh", "MP-BGP", "Graceful Restart"],
    lastError: null,
  },
  {
    id: "s9", serverId: "srv3", serverLabel: "mt-fra-edge-01", serverSite: "FRA",
    peerIp: "193.188.128.1", remoteAs: 6777, localAs: 65001,
    routerId: "193.188.128.1", description: "AMS-IX — peering (idle)",
    state: "Idle", type: "eBGP", afi: "IPv4 Unicast",
    uptime: null, holdTime: 90, keepalive: 30,
    prefixesRx: 0, prefixesTx: 0, prefixesActive: 0,
    inputMessages: 821, outputMessages: 412,
    capabilities: [],
    lastError: "Administratively down",
  },
  {
    id: "s10", serverId: "srv3", serverLabel: "mt-fra-edge-01", serverSite: "FRA",
    peerIp: "10.200.1.1", remoteAs: 65001, localAs: 65001,
    routerId: "10.0.0.1", description: "iBGP → MSK-CORE",
    state: "Established", type: "iBGP", afi: "IPv4 Unicast",
    uptime: "12д 2ч 14м", holdTime: 90, keepalive: 30,
    prefixesRx: 280283, prefixesTx: 512, prefixesActive: 278100,
    inputMessages: 488200, outputMessages: 41022,
    capabilities: ["4-byte-AS", "Route Refresh", "MP-BGP"],
    lastError: null,
  },

  // ── srv6 (mt-ams-test-01) ────────────────────────────────────────────
  {
    id: "s11", serverId: "srv6", serverLabel: "mt-ams-test-01", serverSite: "AMS",
    peerIp: "80.249.208.1", remoteAs: 6777, localAs: 65001,
    routerId: "0.0.0.0", description: "AMS-IX — negotiating",
    state: "OpenSent", type: "eBGP", afi: "IPv4 Unicast",
    uptime: null, holdTime: 90, keepalive: 30,
    prefixesRx: 0, prefixesTx: 0, prefixesActive: 0,
    inputMessages: 2, outputMessages: 1,
    capabilities: [],
    lastError: null,
  },
  {
    id: "s12", serverId: "srv6", serverLabel: "mt-ams-test-01", serverSite: "AMS",
    peerIp: "10.200.5.1", remoteAs: 65001, localAs: 65001,
    routerId: "10.0.0.1", description: "iBGP → MSK-CORE",
    state: "Established", type: "iBGP", afi: "IPv4 Unicast",
    uptime: "5д 9ч 17м", holdTime: 90, keepalive: 30,
    prefixesRx: 280283, prefixesTx: 14, prefixesActive: 276000,
    inputMessages: 184200, outputMessages: 4100,
    capabilities: ["4-byte-AS", "Route Refresh", "MP-BGP"],
    lastError: null,
  },
]

// ─── visual helpers ───────────────────────────────────────────────────────────

const STATE_STYLE: Record<BgpState, { bg: string; text: string; dot: string; label: string }> = {
  Established: { bg: "bg-emerald-500/10", text: "text-emerald-600 dark:text-emerald-400", dot: "bg-emerald-500", label: "Established" },
  Active:      { bg: "bg-amber-500/10",   text: "text-amber-600 dark:text-amber-400",     dot: "bg-amber-500",  label: "Active"       },
  Idle:        { bg: "bg-slate-500/10",   text: "text-slate-500 dark:text-slate-400",      dot: "bg-slate-500",  label: "Idle"         },
  Connect:     { bg: "bg-blue-500/10",    text: "text-blue-600 dark:text-blue-400",        dot: "bg-blue-500",   label: "Connect"      },
  OpenSent:    { bg: "bg-violet-500/10",  text: "text-violet-600 dark:text-violet-400",   dot: "bg-violet-500", label: "OpenSent"     },
  OpenConfirm: { bg: "bg-violet-500/10",  text: "text-violet-600 dark:text-violet-400",   dot: "bg-violet-500", label: "OpenConfirm"  },
}
const TYPE_STYLE: Record<BgpType, { bg: string; text: string }> = {
  eBGP: { bg: "bg-blue-500/10",   text: "text-blue-600 dark:text-blue-400"   },
  iBGP: { bg: "bg-purple-500/10", text: "text-purple-600 dark:text-purple-400" },
}

function StateBadge({ state }: { state: BgpState }) {
  const s = STATE_STYLE[state]
  return (
    <span className={cn("inline-flex items-center gap-1.5 rounded border px-2 py-0.5 text-[11px] font-semibold",
      s.bg, s.text, "border-current/20")}>
      <span className={cn("size-1.5 rounded-full shrink-0", s.dot)} />
      {s.label}
    </span>
  )
}

function TypeBadge({ type }: { type: BgpType }) {
  const s = TYPE_STYLE[type]
  return (
    <span className={cn("inline-flex items-center rounded border px-1.5 py-0.5 text-[10px] font-semibold",
      s.bg, s.text, "border-current/20")}>
      {type}
    </span>
  )
}


function fmtNum(n: number) {
  if (n >= 1_000_000) return `${(n / 1_000_000).toFixed(1)}M`
  if (n >= 1_000)     return `${(n / 1_000).toFixed(0)}k`
  return String(n)
}

// ─── backend mapping ──────────────────────────────────────────────────────────

interface BackendBgpSession {
  id: string; serverId: number; serverName: string; serverSite: string; serverCountry: string
  name: string; peerIp: string; remoteAs: number; localAs: number
  localId: string; remoteId: string; state: string; type: "eBGP" | "iBGP"
  uptime: string | null; holdTime: number; keepalive: number
  prefixesRx: number; prefixesTx: number
  inputMessages: number; outputMessages: number
  capabilities: string[]; lastError: string | null
}

function backendToFrontend(b: BackendBgpSession): BgpSession {
  return {
    id:             `${b.serverId}-${b.id}`,
    serverId:       String(b.serverId),
    serverLabel:    b.serverName,
    serverSite:     b.serverSite,
    peerIp:         b.peerIp,
    remoteAs:       b.remoteAs,
    localAs:        b.localAs,
    routerId:       b.remoteId || b.localId,
    description:    b.name,
    state:          (b.state as BgpState) || "Idle",
    type:           b.type,
    afi:            "IPv4 Unicast",
    uptime:         b.uptime,
    holdTime:       b.holdTime,
    keepalive:      b.keepalive,
    prefixesRx:     b.prefixesRx,
    prefixesTx:     b.prefixesTx,
    prefixesActive: b.prefixesRx,
    inputMessages:  b.inputMessages,
    outputMessages: b.outputMessages,
    capabilities:   b.capabilities,
    lastError:      b.lastError,
  }
}

// ─── sessions tab ─────────────────────────────────────────────────────────────

const BGP_FILTER_ACCESSORS = {
  state: (s: BgpSession) => s.state,
  type: (s: BgpSession) => s.type,
  afi: (s: BgpSession) => s.afi,
}

function SessionsTab({ sessions }: { sessions: BgpSession[] }) {
  const [search, setSearch] = useState("")
  const [stateFilter, setStateFilter] = useState<StateFilter>("all")
  const [typeFilter, setTypeFilter] = useState<TypeFilter>("all")
  const [advancedFilters, setAdvancedFilters] = useState<Filter[]>([])

  const q = search.toLowerCase()
  const filtered = useMemo(() => {
    const base = sessions.filter((s) => {
      if (stateFilter !== "all" && s.state !== stateFilter) return false
      if (typeFilter !== "all" && s.type !== typeFilter) return false
      if (
        q &&
        !s.peerIp.includes(q) &&
        !s.description.toLowerCase().includes(q) &&
        !s.serverLabel.includes(q) &&
        !String(s.remoteAs).includes(q) &&
        !(AS_NAMES[s.remoteAs] ?? "").toLowerCase().includes(q)
      ) {
        return false
      }
      return true
    })
    return applyReuiFilters(base, advancedFilters, BGP_FILTER_ACCESSORS)
  }, [sessions, q, stateFilter, typeFilter, advancedFilters])

  return (
    <div className="flex flex-col gap-4">
      <DataPageCard>
        <DataPageToolbar
          segmented={{
            value: stateFilter,
            onChange: setStateFilter,
            options: [
              { value: "all", label: "Все", count: sessions.length },
              { value: "Established", label: "Established", count: sessions.filter((s) => s.state === "Established").length },
              { value: "Active", label: "Active", count: sessions.filter((s) => s.state === "Active").length },
              { value: "Idle", label: "Idle", count: sessions.filter((s) => s.state === "Idle").length },
              { value: "OpenSent", label: "OpenSent", count: sessions.filter((s) => s.state === "OpenSent").length },
            ],
          }}
          filters={advancedFilters}
          onFiltersChange={setAdvancedFilters}
          filterFields={BGP_FILTER_FIELDS}
          search={search}
          onSearchChange={setSearch}
          searchPlaceholder="IP, AS, описание…"
          countLabel={`${filtered.length} из ${sessions.length}`}
          actions={
            <div className="flex items-center gap-0.5 p-0.5 rounded-md border border-border bg-muted/40">
              {(["all", "eBGP", "iBGP"] as const).map((t) => (
                <button
                  key={t}
                  type="button"
                  onClick={() => setTypeFilter(t)}
                  className={cn(
                    "px-2.5 py-1 text-[11px] rounded transition-colors",
                    typeFilter === t
                      ? "bg-background text-foreground shadow-sm"
                      : "text-muted-foreground hover:text-foreground",
                  )}
                >
                  {t === "all" ? "Все типы" : t}
                </button>
              ))}
            </div>
          }
        />
        <BgpSessionsDataGrid sessions={filtered} />
      </DataPageCard>
    </div>
  )
}

// ─── routers tab ──────────────────────────────────────────────────────────────

function RoutersTab({ sessions }: { sessions: BgpSession[] }) {
  const byRouter = useMemo(() => {
    const map: Record<string, { label: string; site: string; sessions: BgpSession[] }> = {}
    sessions.forEach(s => {
      if (!map[s.serverId]) map[s.serverId] = { label: s.serverLabel, site: s.serverSite, sessions: [] }
      map[s.serverId].sessions.push(s)
    })
    return Object.entries(map).map(([id, v]) => ({ id, ...v }))
  }, [sessions])

  return (
    <div className="grid grid-cols-1 xl:grid-cols-2 gap-4">
      {byRouter.map(router => {
        const estCnt   = router.sessions.filter(s => s.state === "Established").length
        const downCnt  = router.sessions.length - estCnt
        const totalRx  = router.sessions.reduce((a, s) => a + s.prefixesRx, 0)

        return (
          <Frame key={router.id} dense className="w-full overflow-hidden">
            <FramePanel className="p-0 overflow-hidden">
            {/* header */}
            <div className="flex items-center gap-3 px-4 py-3 border-b">
              <ServerIcon className="size-4 text-muted-foreground shrink-0" />
              <div className="flex-1 min-w-0">
                <p className="text-sm font-medium leading-none">{router.label}</p>
                <p className="text-[11px] font-mono text-muted-foreground mt-0.5">{router.site} · AS65001</p>
              </div>
              <div className="flex items-center gap-2 shrink-0">
                {estCnt > 0 && (
                  <span className="text-[10px] px-1.5 py-0.5 rounded-full border border-current/25 font-medium"
                    style={{ background: "var(--status-online-bg)", color: "var(--status-online-fg)" }}>
                    ✓ {estCnt}
                  </span>
                )}
                {downCnt > 0 && (
                  <span className="text-[10px] px-1.5 py-0.5 rounded-full border border-current/25 font-medium"
                    style={{ background: "var(--status-degraded-bg)", color: "var(--status-degraded-fg)" }}>
                    ⚠ {downCnt}
                  </span>
                )}
              </div>
            </div>

            {/* session rows */}
            <div className="divide-y divide-border/60">
              {router.sessions.map(s => {
                const ss = STATE_STYLE[s.state]
                return (
                  <div key={s.id} className="flex items-center gap-3 px-4 py-2.5 hover:bg-muted/30 transition-colors">
                    <span className={cn("size-2 rounded-full shrink-0", ss.dot)} />
                    <div className="flex-1 min-w-0">
                      <div className="flex items-center gap-2">
                        <span className="text-xs font-mono">{s.peerIp}</span>
                        <TypeBadge type={s.type} />
                      </div>
                      <div className="flex items-center gap-1.5 mt-0.5">
                        <span className="text-[10px] text-muted-foreground">AS{s.remoteAs}</span>
                        {AS_NAMES[s.remoteAs] && (
                          <span className="text-[10px] text-muted-foreground/60">· {AS_NAMES[s.remoteAs]}</span>
                        )}
                      </div>
                    </div>
                    <div className="shrink-0 text-right">
                      {s.prefixesRx > 0 && (
                        <p className="text-[10px] font-mono text-emerald-500 tabular-nums">
                          ↓ {fmtNum(s.prefixesRx)}
                        </p>
                      )}
                      <p className={cn("text-[10px] font-semibold", ss.text)}>{s.state}</p>
                    </div>
                  </div>
                )
              })}
            </div>

            {/* footer summary */}
            {totalRx > 0 && (
              <div className="flex items-center gap-2 px-4 py-2 border-t bg-muted/20">
                <ArrowDownIcon className="size-3 text-emerald-500" />
                <span className="text-[11px] font-mono text-muted-foreground">
                  Всего получено: <span className="text-emerald-600 dark:text-emerald-400 font-semibold">{fmtNum(totalRx)}</span> префиксов
                </span>
              </div>
            )}
          </FramePanel>
          </Frame>
        )
      })}
    </div>
  )
}

// ─── analytics tab ────────────────────────────────────────────────────────────

function AnalyticsTab({ sessions }: { sessions: BgpSession[] }) {
  // top peers by prefixes received (eBGP only)
  const topPeers = useMemo(() =>
    [...sessions]
      .filter(s => s.prefixesRx > 0)
      .sort((a, b) => b.prefixesRx - a.prefixesRx)
      .slice(0, 8),
    [sessions]
  )
  const maxRx = topPeers[0]?.prefixesRx ?? 1

  // state distribution
  const stateCounts = useMemo(() => {
    const map: Record<string, number> = {}
    sessions.forEach(s => { map[s.state] = (map[s.state] ?? 0) + 1 })
    return Object.entries(map).sort((a, b) => b[1] - a[1]) as [BgpState, number][]
  }, [sessions])

  // total prefix stats
  const totalRx      = sessions.reduce((a, s) => a + s.prefixesRx, 0)
  const totalActive  = sessions.reduce((a, s) => a + s.prefixesActive, 0)
  const ebgpSessions = sessions.filter(s => s.type === "eBGP").length
  const ibgpSessions = sessions.filter(s => s.type === "iBGP").length

  return (
    <div className="flex flex-col gap-5">
      <KpiStatGrid
        aria-label="Сводка префиксов BGP"
        items={[
          {
            id: "rx",
            label: "Всего префиксов",
            value: fmtNum(totalRx),
            icon: <DownloadIcon className="size-4" />,
            iconClassName: "text-success",
          },
          {
            id: "active",
            label: "Активных маршрутов",
            value: fmtNum(totalActive),
            icon: <GitMergeIcon className="size-4" />,
            iconClassName: "text-info",
          },
          {
            id: "ebgp",
            label: "eBGP сессий",
            value: ebgpSessions,
            icon: <ActivityIcon className="size-4" />,
            iconClassName: "text-muted-foreground",
          },
          {
            id: "ibgp",
            label: "iBGP сессий",
            value: ibgpSessions,
            icon: <ServerIcon className="size-4" />,
            iconClassName: "text-muted-foreground",
          },
        ]}
      />

      <div className="grid grid-cols-1 xl:grid-cols-[1fr_320px] gap-5">
        {/* prefixes by peer — horizontal bar chart */}
        <OpsPanel title="Топ-8 пиров по полученным префиксам" contentClassName="pt-4 pb-4 px-5">
            <div className="flex flex-col gap-3">
              {topPeers.map((s, i) => {
                const pct = (s.prefixesRx / maxRx) * 100
                const ss  = STATE_STYLE[s.state]
                return (
                  <div key={s.id} className="flex items-center gap-3">
                    <span className="text-[11px] font-mono text-muted-foreground w-4 tabular-nums text-right">
                      {i + 1}
                    </span>
                    <div className="flex-1 min-w-0">
                      <div className="flex items-center justify-between mb-1 gap-2">
                        <div className="flex items-center gap-2 min-w-0">
                          <span className="text-xs font-mono truncate">{s.peerIp}</span>
                          <span className="text-[10px] text-muted-foreground shrink-0">AS{s.remoteAs}</span>
                          <TypeBadge type={s.type} />
                        </div>
                        <span className={cn("text-[10px] font-semibold shrink-0 tabular-nums font-mono", "text-emerald-600 dark:text-emerald-400")}>
                          {fmtNum(s.prefixesRx)}
                        </span>
                      </div>
                      <div className="h-2 rounded-full bg-muted overflow-hidden">
                        <div
                          className={cn("h-full rounded-full transition-all", ss.dot)}
                          style={{ width: `${pct}%`, opacity: s.state === "Established" ? 0.8 : 0.3 }}
                        />
                      </div>
                      <p className="text-[10px] text-muted-foreground mt-0.5">{s.serverLabel}</p>
                    </div>
                  </div>
                )
              })}
              {topPeers.length === 0 && (
                <p className="text-sm text-muted-foreground text-center py-4">Нет данных о префиксах</p>
              )}
            </div>
        </OpsPanel>

        {/* right column */}
        <div className="flex flex-col gap-4">
          {/* state distribution */}
          <OpsPanel title="Распределение состояний" contentClassName="pt-4 pb-4 px-5">
              {stateCounts.length === 0 ? (
                <p className="text-xs text-muted-foreground">Нет данных</p>
              ) : (
                <div className="flex flex-col gap-2">
                  {stateCounts.map(([state, count]) => {
                    const ss  = STATE_STYLE[state]
                    const pct = sessions.length > 0 ? (count / sessions.length) * 100 : 0
                    return (
                      <div key={state} className="flex items-center gap-2">
                        <span className={cn("size-2 rounded-full shrink-0", ss.dot)} />
                        <span className="text-xs w-24">{state}</span>
                        <div className="flex-1 h-2 rounded-full bg-muted overflow-hidden">
                          <div className={cn("h-full rounded-full", ss.dot)} style={{ width: `${pct}%`, opacity: 0.75 }} />
                        </div>
                        <span className="text-xs font-mono tabular-nums w-6 text-right">{count}</span>
                      </div>
                    )
                  })}
                </div>
              )}
          </OpsPanel>

          {/* eBGP vs iBGP */}
          <OpsPanel title="eBGP vs iBGP" contentClassName="pt-4 pb-4 px-5">
              {sessions.length === 0 ? (
                <p className="text-xs text-muted-foreground">Нет данных</p>
              ) : (
                <>
                  {[
                    { type: "eBGP" as BgpType, label: "Внешние (eBGP)", count: ebgpSessions },
                    { type: "iBGP" as BgpType, label: "Внутренние (iBGP)", count: ibgpSessions },
                  ].map(({ type, count }) => {
                    const pct = sessions.length > 0 ? (count / sessions.length) * 100 : 0
                    return (
                      <div key={type} className="flex items-center gap-2 mb-2 last:mb-0">
                        <TypeBadge type={type} />
                        <div className="flex-1 h-2 rounded-full bg-muted overflow-hidden">
                          <div className={cn("h-full rounded-full", type === "eBGP" ? "bg-blue-500" : "bg-purple-500")}
                            style={{ width: `${pct}%`, opacity: 0.75 }} />
                        </div>
                        <span className="text-xs font-mono w-4 text-right">{count}</span>
                      </div>
                    )
                  })}

                  {/* active AS list */}
                  <div className="mt-4 pt-3 border-t border-border/60">
                    <p className="text-[10px] text-muted-foreground uppercase tracking-wider mb-2 font-semibold">Автономные системы</p>
                    <div className="flex flex-col gap-1">
                      {Object.entries(
                        sessions.filter(s => s.type === "eBGP").reduce<Record<number, number>>((acc, s) => {
                          acc[s.remoteAs] = (acc[s.remoteAs] ?? 0) + 1; return acc
                        }, {})
                      ).sort((a, b) => b[1] - a[1]).map(([as, cnt]) => (
                        <div key={as} className="flex items-center justify-between text-[11px]">
                          <span className="font-mono text-muted-foreground">AS{as}</span>
                          <span className="text-muted-foreground/60">{AS_NAMES[Number(as)] ?? ""}</span>
                          <span className="font-mono tabular-nums">{cnt}</span>
                        </div>
                      ))}
                    </div>
                  </div>
                </>
              )}
          </OpsPanel>
        </div>
      </div>
    </div>
  )
}

// ─── page ─────────────────────────────────────────────────────────────────────

const TABS: Array<{ id: BgpTab; label: string; icon: React.ReactNode }> = [
  { id: "sessions",  label: "Сессии",       icon: <ActivityIcon  className="size-3.5" /> },
  { id: "routers",   label: "По роутерам",  icon: <ServerIcon    className="size-3.5" /> },
  { id: "analytics", label: "Аналитика",    icon: <BarChart3Icon className="size-3.5" /> },
]

export default function BgpPage() {
  const [activeTab, setActiveTab] = useState<BgpTab>("sessions")

  const { mode, backendUrl } = useDataSource()
  const isLive = mode === "live"

  const [liveSessions, setLiveSessions] = useState<BgpSession[]>([])
  const [loading,      setLoading]      = useState(false)
  const [fetchedAt,    setFetchedAt]    = useState<Date | null>(null)
  const [liveError,    setLiveError]    = useState<string | null>(null)
  const [fetchTick,    setFetchTick]    = useState(0)

  useEffect(() => {
    if (!isLive) return
    let cancelled = false
    queueMicrotask(() => {
      if (cancelled) return
      setLoading(true)
      setLiveError(null)
      void requestJson<BackendBgpSession[]>(backendUrl, "/api/bgp/sessions")
        .then(data => {
          if (cancelled) return
          setLiveSessions(data.map(backendToFrontend))
          setFetchedAt(new Date())
          setLoading(false)
        })
        .catch((err: unknown) => {
          if (cancelled) return
          setLiveError(err instanceof Error ? err.message : String(err))
          setLoading(false)
        })
    })
    return () => { cancelled = true }
  }, [isLive, backendUrl, fetchTick])

  // Use live or mock data for all tabs and KPI
  const sessions = isLive ? liveSessions : SESSIONS

  const established = sessions.filter(s => s.state === "Established").length
  const notEstab    = sessions.length - established
  const totalRx     = sessions.reduce((a, s) => a + s.prefixesRx, 0)
  const serverCount = useMemo(
    () => new Set(liveSessions.map(s => s.serverId)).size,
    [liveSessions],
  )

  return (
    <div className="flex flex-col h-full">
      <PageHeader
        crumbs={[{ label: "Управление" }, { label: "BGP" }]}
        actions={
          <>
            <Button variant="outline" size="sm" onClick={() => setFetchTick(t => t + 1)}>
              <RefreshCwIcon className={cn("size-4", loading && "animate-spin")} />
              Обновить
            </Button>
            <Button variant="outline" size="sm"><DownloadIcon  className="size-4" />Экспорт</Button>
          </>
        }
      />

      {/* tab bar */}
      <div className="border-b bg-background shrink-0">
        <div className="flex items-center px-6">
          {TABS.map(t => (
            <button key={t.id} onClick={() => setActiveTab(t.id)}
              className={cn(
                "flex items-center gap-2 px-4 py-3 text-sm font-medium border-b-2 transition-colors -mb-px",
                activeTab === t.id
                  ? "border-primary text-foreground"
                  : "border-transparent text-muted-foreground hover:text-foreground hover:border-border",
              )}>
              {t.icon}{t.label}
            </button>
          ))}
        </div>
      </div>

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

          {/* data source banner */}
          {isLive && loading && (
            <div className="flex items-center gap-2 text-xs text-muted-foreground">
              <RefreshCwIcon className="size-3.5 animate-spin" />
              Загрузка BGP-сессий…
            </div>
          )}
          {isLive && !loading && fetchedAt && !liveError && (
            <div className="flex items-center gap-2">
              <span className="inline-flex items-center gap-1.5 rounded-full border border-emerald-500/30 bg-emerald-500/10 px-2.5 py-0.5 text-[11px] font-medium text-emerald-600 dark:text-emerald-400">
                <span className="size-1.5 rounded-full bg-emerald-500" />
                Живые данные · {serverCount} серверов · обновлено {fetchedAt.toLocaleTimeString("ru")}
              </span>
              <button
                onClick={() => setFetchTick(t => t + 1)}
                className="flex items-center gap-1 text-[11px] text-muted-foreground hover:text-foreground transition-colors">
                <RefreshCwIcon className="size-3" />
                Обновить
              </button>
            </div>
          )}
          {isLive && liveError && (
            <Alert variant="warning" className="py-2">
              <AlertCircleIcon />
              <AlertDescription className="text-xs">Ошибка загрузки: {liveError}</AlertDescription>
            </Alert>
          )}
          {isLive && !loading && liveSessions.length === 0 && !liveError && fetchedAt && (
            <div className="rounded-md border border-border bg-muted/30 px-4 py-6 text-center text-sm text-muted-foreground">
              BGP не настроен ни на одном сервере
            </div>
          )}
          {mode === "mock" && (
            <span className="inline-flex w-fit items-center gap-1.5 rounded-full border border-border bg-muted/40 px-2.5 py-0.5 text-[11px] font-medium text-muted-foreground">
              Моковые данные
            </span>
          )}

          <KpiStatGrid
            aria-label="Сводка BGP"
            items={[
              {
                id: "sessions",
                label: "Сессий всего",
                value: sessions.length,
                icon: <GitMergeIcon className="size-4" />,
                iconClassName: "text-muted-foreground",
              },
              {
                id: "established",
                label: "Established",
                value: established,
                icon: <CheckCircleIcon className="size-4" />,
                iconClassName: "text-success",
              },
              {
                id: "not-estab",
                label: "Не установлено",
                value: notEstab,
                icon: <AlertCircleIcon className="size-4" />,
                iconClassName: notEstab > 0 ? "text-warning" : "text-muted-foreground",
                variant: notEstab > 0 ? "warning" : "default",
              },
              {
                id: "prefixes",
                label: "Получено префиксов",
                value: fmtNum(totalRx),
                icon: <DownloadIcon className="size-4" />,
                iconClassName: "text-info",
              },
            ]}
          />

          {/* alert: not-established sessions */}
          {notEstab > 0 && (
            <Alert variant="warning">
              <AlertCircleIcon />
              <AlertTitle>
                <span className="font-semibold">{notEstab} сессии</span> не в состоянии Established
              </AlertTitle>
              <AlertDescription>
                Проверьте {sessions.filter(s => s.state !== "Established").map(s => s.peerIp).join(", ")}
              </AlertDescription>
            </Alert>
          )}

          {/* tab content */}
          {activeTab === "sessions"  && <SessionsTab  sessions={sessions} />}
          {activeTab === "routers"   && <RoutersTab   sessions={sessions} />}
          {activeTab === "analytics" && <AnalyticsTab sessions={sessions} />}

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