"use client"

import { useCallback, useEffect, useMemo, useState } from "react"
import { PageHeader } from "@/components/page-header"
import { DataPageCard } from "@/components/data-page-card"
import { DataPageToolbar, DataPageToolbarFrame } from "@/components/data-page-toolbar"
import { GreTunnelsDataGrid } from "@/components/data-grids/gre-tunnels-data-grid"
import { GrePoolsDataGrid } from "@/components/data-grids/gre-pools-data-grid"
import { FormField, FormToggle, SectionTitle, SegmentedControl } from "@/components/form-kit"
import { greTunnels as mockGreTunnels, grePools as mockGrePools, servers as mockServers } from "@/lib/data"
import type { GrePool, GreTunnel, GreStatus, IpsecEncAlg, IpsecAuthAlg, IpsecDhGroup, IkeVersion, Server } from "@/lib/data"
import { useDataSource } from "@/lib/data-source"
import { requestJson } from "@/shared/api/http-client"
import { cn } from "@/lib/utils"
import { toast } from "sonner"
import { Frame, FramePanel } from "@/components/reui/frame"
import { IconTile } from "@/components/reui/icon-tile"
import { OpsPanel } from "@/components/ops-panel"
import { Button } from "@/components/ui/button"
import { Input } from "@/components/ui/input"
import {
  Sheet, SheetContent, SheetHeader, SheetTitle,
  SheetDescription, SheetFooter, SheetClose,
} from "@/components/ui/sheet"
import {
  DropdownMenu, DropdownMenuTrigger, DropdownMenuContent,
  DropdownMenuItem, DropdownMenuSeparator, DropdownMenuLabel, DropdownMenuGroup,
} from "@/components/ui/dropdown-menu"
import { Flag } from "@/components/flag"
import {
  PlusIcon, RefreshCwIcon, MoreHorizontalIcon,
  LockIcon, LockOpenIcon, ShieldCheckIcon, NetworkIcon,
  EyeIcon, EyeOffIcon, ChevronDownIcon, ChevronRightIcon,
  CodeXmlIcon, PencilIcon, PowerIcon, Trash2Icon, CopyIcon, CheckIcon,
  DatabaseIcon,
} from "lucide-react"

// ─── label maps ─────────────────────────────────────────────────────────────

const ENC_LABELS: Record<IpsecEncAlg, string>  = { "aes-128": "AES-128", "aes-192": "AES-192", "aes-256": "AES-256" }
const AUTH_LABELS: Record<IpsecAuthAlg, string> = { sha1: "SHA-1", sha256: "SHA-256", sha512: "SHA-512" }
const DH_LABELS: Record<IpsecDhGroup, string>   = {
  modp1024: "DH-2 (1024)", modp2048: "DH-14 (2048)", modp4096: "DH-16 (4096)",
  ecp256: "ECP-256", ecp384: "ECP-384", ecp521: "ECP-521",
}
const IKE_LABELS: Record<IkeVersion, string> = { ikev1: "IKEv1", ikev2: "IKEv2" }

const ENC_ROS: Record<IpsecEncAlg, string>  = { "aes-128": "aes-128-cbc", "aes-192": "aes-192-cbc", "aes-256": "aes-256-cbc" }
const AUTH_ROS: Record<IpsecAuthAlg, string> = { sha1: "sha1", sha256: "sha256", sha512: "sha512" }

const STATUS_MAP: Record<GreStatus, { label: string; dot: string }> = {
  up:       { label: "Up",       dot: "bg-emerald-500" },
  degraded: { label: "Degraded", dot: "bg-amber-500"   },
  down:     { label: "Down",     dot: "bg-red-500"      },
}

// ─── RouterOS code generator ─────────────────────────────────────────────────

function generateRosCommands(t: GreTunnel, serverById: Record<string, Server>): string {
  const lines: string[] = []
  const srv = serverById[t.serverId]

  lines.push(`# Сервер: ${srv?.name ?? t.serverId} (${srv?.host ?? ""})`)
  lines.push(`# Сгенерировано MikrotikManager · ${new Date().toLocaleDateString("ru")}`)
  lines.push("")

  // GRE interface
  lines.push("# ── GRE-интерфейс ────────────────────────────────────────────")
  const greParts = [
    `/interface gre add`,
    `  name=${t.name}`,
    `  remote-address=${t.remoteAddress}`,
    t.localAddress !== "0.0.0.0" ? `  local-address=${t.localAddress}` : null,
    t.ipsec ? `  ipsec-secret="${t.ipsec.secret}"` : null,
    `  mtu=${t.mtu}`,
    t.keepaliveInterval > 0 ? `  keepalive=${t.keepaliveInterval}s,${t.keepaliveRetries}` : `  keepalive=0`,
    `  dscp=${t.dscp}`,
    `  clamp-tcp-mss=${t.clampTcpMss ? "yes" : "no"}`,
    `  allow-fast-path=${t.allowFastPath ? "yes" : "no"}`,
    t.comment ? `  comment="${t.comment}"` : null,
    !t.enabled ? `  disabled=yes` : null,
  ].filter(Boolean) as string[]
  lines.push(greParts.join(" \\\n"))

  // Inner IP
  lines.push("")
  lines.push("# ── Внутренний IP ────────────────────────────────────────────")
  lines.push(`/ip address add \\`)
  lines.push(`  address=${t.localInnerIp} \\`)
  lines.push(`  interface=${t.name}`)

  // IPsec manual equivalent
  if (t.ipsec) {
    const ikeMode = t.ipsec.ikeVersion === "ikev2" ? "ike2" : "ike1"
    const pfsGroup = t.ipsec.pfs ? t.ipsec.dhGroup : "none"

    lines.push("")
    lines.push("# ── IPsec (авто через ipsec-secret; ручной эквивалент) ───────")
    lines.push("")
    lines.push(`/ip ipsec peer add \\`)
    lines.push(`  name=${t.name} \\`)
    lines.push(`  address=${t.remoteAddress} \\`)
    lines.push(`  exchange-mode=${ikeMode} \\`)
    lines.push(`  auth-method=pre-shared-key \\`)
    lines.push(`  secret="${t.ipsec.secret}"`)
    lines.push("")
    lines.push(`/ip ipsec proposal add \\`)
    lines.push(`  name=${t.name} \\`)
    lines.push(`  enc-algorithms=${ENC_ROS[t.ipsec.encAlg]} \\`)
    lines.push(`  auth-algorithms=${AUTH_ROS[t.ipsec.authAlg]} \\`)
    lines.push(`  pfs-group=${pfsGroup} \\`)
    lines.push(`  lifetime=${t.ipsec.lifetime}`)
    lines.push("")
    lines.push(`/ip ipsec policy add \\`)
    lines.push(`  src-address=${t.localAddress !== "0.0.0.0" ? t.localAddress + "/32" : "0.0.0.0/0"} \\`)
    lines.push(`  dst-address=${t.remoteAddress}/32 \\`)
    lines.push(`  proposal=${t.name} \\`)
    lines.push(`  tunnel=yes`)
  }

  return lines.join("\n")
}

// ─── small ui helpers ────────────────────────────────────────────────────────

function TunnelStatus({ status }: { status: GreStatus }) {
  const s = STATUS_MAP[status]
  return (
    <span className="inline-flex items-center gap-1.5 text-sm">
      <span className={`size-1.5 rounded-full ${s.dot}`} />
      {s.label}
    </span>
  )
}

function IpsecBadge({ secured }: { secured: boolean }) {
  return secured ? (
    <span className="inline-flex items-center gap-1 text-xs font-medium border rounded px-2 py-0.5 bg-emerald-500/10 text-emerald-400 border-emerald-500/20">
      <LockIcon className="size-3" /> IPsec
    </span>
  ) : (
    <span className="inline-flex items-center gap-1 text-xs font-medium border rounded px-2 py-0.5 bg-muted text-muted-foreground border-border">
      <LockOpenIcon className="size-3" /> Открытый
    </span>
  )
}

// ─── Live API (как на /filters) ─────────────────────────────────────────────

interface BackendServer {
  id: number
  name: string
  host: string
  type: "jump-host" | "exit-node" | "home-router"
  site: string
  country: string
  asn: string
  enabled: boolean
  status: "online" | "offline" | null
  latency: number | null
}

interface GreTunnelsApiResponse {
  tunnels: GreTunnel[]
}

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

function mapBackendToServer(s: BackendServer): Server {
  return {
    id: String(s.id),
    name: s.name || s.host,
    host: s.host,
    model: "—",
    os: "—",
    site: s.site,
    country: s.country || "UN",
    asn: s.asn,
    type: s.type,
    enabled: s.enabled,
    status: (s.status ?? "offline") as Server["status"],
    latency: s.latency != null ? Math.round(s.latency) : null,
    sessions: 0,
  }
}

function derivePoolsFromTunnels(tunnels: GreTunnel[]): GrePool[] {
  const ids = [...new Set(tunnels.map((t) => t.poolId).filter(Boolean))]
  return ids.map((id) => {
    const list = tunnels.filter((t) => t.poolId === id)
    const allocated = list.length
    return {
      id,
      name: id === "live" ? "С устройств (опрос)" : id,
      cidr: "—",
      allocated,
      total: Math.max(allocated, 1),
      comment: "По poolId из данных GRE",
    }
  })
}

// ─── default form states ─────────────────────────────────────────────────────

const defaultTunnelForm = {
  name: "", serverId: "", localAddress: "", remoteAddress: "",
  poolId: "", localInnerIp: "", remoteInnerIp: "", comment: "", enabled: true,
  ipsecEnabled: false, ipsecSecret: "", ipsecShowSecret: false,
  ipsecIkeVersion: "ikev2" as IkeVersion,
  ipsecEncAlg: "aes-256" as IpsecEncAlg,
  ipsecAuthAlg: "sha256" as IpsecAuthAlg,
  ipsecDhGroup: "modp2048" as IpsecDhGroup,
  ipsecLifetime: "1d 00:00:00", ipsecPfs: true,
  mtu: 1476, keepaliveInterval: 10, keepaliveRetries: 10,
  dscp: "inherit", clampTcpMss: true, allowFastPath: true,
  showAdvanced: false,
}

const defaultPoolForm = { name: "", cidr: "", comment: "" }

type TabFilter = "all" | "up" | "ipsec" | "plain"
type PageTab   = "tunnels" | "pools"

// ════════════════════════════════════════════════════════════════════════════
export default function GrePage() {
  const { mode, backendUrl } = useDataSource()
  const isLive = mode === "live"
  const apiFetch = useMemo(() => makeApiFetch(backendUrl), [backendUrl])

  const [liveServers, setLiveServers] = useState<Server[]>([])
  const [liveTunnels, setLiveTunnels] = useState<GreTunnel[]>([])
  const [dataLoading, setDataLoading] = useState(false)
  const [dataError, setDataError] = useState<string | null>(null)
  const [syncJhBusy, setSyncJhBusy] = useState(false)

  const [pageTab,   setPageTab]   = useState<PageTab>("tunnels")
  const [tabFilter, setTabFilter] = useState<TabFilter>("all")
  const [search,    setSearch]    = useState("")

  const [tunnelOpen,      setTunnelOpen]      = useState(false)
  const [poolOpen,        setPoolOpen]        = useState(false)
  const [codePreviewTunnel, setCodePreviewTunnel] = useState<GreTunnel | null>(null)
  const [copied,          setCopied]          = useState(false)

  const [tForm, setTForm] = useState(defaultTunnelForm)
  const [pForm, setPForm] = useState(defaultPoolForm)

  const setT = <K extends keyof typeof defaultTunnelForm>(k: K, v: (typeof defaultTunnelForm)[K]) =>
    setTForm((f) => ({ ...f, [k]: v }))

  const loadLive = useCallback(async () => {
    if (!isLive) return
    setDataLoading(true)
    setDataError(null)
    try {
      const [backendServers, greRes] = await Promise.all([
        apiFetch<BackendServer[]>("/api/servers"),
        apiFetch<GreTunnelsApiResponse>("/api/filters/gre-tunnels"),
      ])
      setLiveServers(backendServers.map(mapBackendToServer))
      setLiveTunnels(greRes.tunnels)
    } catch (e) {
      setDataError(e instanceof Error ? e.message : "Ошибка загрузки")
      setLiveServers([])
      setLiveTunnels([])
    } finally {
      setDataLoading(false)
    }
  }, [isLive, apiFetch])

  useEffect(() => {
    if (!isLive) {
      queueMicrotask(() => {
        setLiveServers([])
        setLiveTunnels([])
        setDataError(null)
      })
      return
    }
    queueMicrotask(() => {
      void loadLive()
    })
  }, [isLive, loadLive])

  const displayTunnels = isLive ? liveTunnels : mockGreTunnels
  const displayServers = isLive ? liveServers : mockServers
  const displayPools   = useMemo(
    () => (isLive ? derivePoolsFromTunnels(displayTunnels) : mockGrePools),
    [isLive, displayTunnels],
  )

  const serverById = useMemo(
    () => Object.fromEntries(displayServers.map((s) => [s.id, s])),
    [displayServers],
  )
  const poolById = useMemo(
    () => Object.fromEntries(displayPools.map((p) => [p.id, p])),
    [displayPools],
  )

  const syncJhToDb = useCallback(async () => {
    if (!isLive || syncJhBusy) return
    const jh = displayServers.filter((s) => s.type === "jump-host" && s.enabled)
    if (jh.length === 0) {
      toast.info("Нет включённых Jump Host в списке серверов")
      return
    }
    setSyncJhBusy(true)
    const errors: string[] = []
    try {
      for (const s of jh) {
        try {
          await apiFetch<{ ok: boolean }>("/api/filters/sync/from-router", {
            method: "POST",
            body: JSON.stringify({ serverId: s.id }),
          })
        } catch (e) {
          errors.push(`${s.name}: ${e instanceof Error ? e.message : "ошибка"}`)
        }
      }
      const fresh = await apiFetch<GreTunnelsApiResponse>("/api/filters/gre-tunnels")
      setLiveTunnels(fresh.tunnels)
      if (errors.length) {
        toast.warning(`Синхронизировано JH: ${jh.length - errors.length}/${jh.length}. Ошибки: ${errors.join("; ")}`)
      } else {
        toast.success(`Правила с ${jh.length} JH записаны в БД, список GRE обновлён.`)
      }
    } catch (e) {
      toast.error(e instanceof Error ? e.message : "Ошибка после синхронизации")
    } finally {
      setSyncJhBusy(false)
    }
  }, [isLive, syncJhBusy, apiFetch, displayServers])

  useEffect(() => {
    if (dataError) toast.error(dataError)
  }, [dataError])

  const filtered = useMemo(() => {
    return displayTunnels.filter((t) => {
      if (tabFilter === "up"    && t.status !== "up") return false
      if (tabFilter === "ipsec" && !t.ipsec)          return false
      if (tabFilter === "plain" && t.ipsec)           return false
      if (!search) return true
      const q = search.toLowerCase()
      return (
        t.name.toLowerCase().includes(q) ||
        t.remoteAddress.includes(q)      ||
        t.localInnerIp.includes(q)       ||
        serverById[t.serverId]?.name.toLowerCase().includes(q)
      )
    })
  }, [tabFilter, search, displayTunnels, serverById])

  const upCount    = displayTunnels.filter((t) => t.status === "up").length
  const ipsecCount = displayTunnels.filter((t) => t.ipsec).length

  const tunnelTabs: { value: TabFilter; label: string; count: number }[] = [
    { value: "all",   label: "Все",       count: displayTunnels.length },
    { value: "up",    label: "Активные",  count: upCount           },
    { value: "ipsec", label: "С IPsec",   count: ipsecCount        },
    { value: "plain", label: "Без IPsec", count: displayTunnels.length - ipsecCount },
  ]

  function handleCopy(code: string) {
    navigator.clipboard.writeText(code).then(() => {
      setCopied(true)
      toast.success("Команды скопированы")
      setTimeout(() => setCopied(false), 2000)
    })
  }

  return (
    <div className="flex flex-col h-full">
      <PageHeader
        crumbs={[{ label: "Управление" }, { label: "GRE-туннели" }]}
        actions={
          <>
            <Button
              variant="outline"
              size="sm"
              onClick={() => { void loadLive() }}
              disabled={!isLive || dataLoading}
              title={!isLive ? "Включите Live и доступный бэкенд в настройках источника данных" : "Обновить список GRE с устройств"}
            >
              <RefreshCwIcon className={cn("size-4", dataLoading && "animate-spin")} />
              Обновить
            </Button>
            <Button
              variant="outline"
              size="sm"
              onClick={() => { void syncJhToDb() }}
              disabled={!isLive || syncJhBusy || dataLoading}
              title="Загрузить правила фильтрации с каждого Jump Host в БД и обновить опрос GRE"
            >
              <DatabaseIcon className={cn("size-4", syncJhBusy && "animate-pulse")} />
              JH → БД
            </Button>
            <Button size="sm" onClick={() => { setTForm(defaultTunnelForm); setTunnelOpen(true) }}>
              <PlusIcon className="size-4" />Добавить туннель
            </Button>
          </>
        }
      />

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

          {/* Legacy banner */}
          <div className="flex items-start gap-3 rounded-lg bg-amber-500/5 border border-amber-500/20 px-4 py-3 text-sm">
            <ShieldCheckIcon className="size-5 text-amber-500 shrink-0 mt-0.5" />
            <div>
              <p className="font-medium text-amber-600 dark:text-amber-400">
                В RouterOS 7.x рекомендуется использовать WireGuard вместо GRE+IPsec
              </p>
              <p className="text-muted-foreground text-xs mt-0.5">
                WireGuard проще в настройке, обеспечивает лучшую производительность и современную криптографию (ChaCha20-Poly1305).
                GRE-туннели остаются поддерживаемыми, но WireGuard — предпочтительный выбор для новых развёртываний.
              </p>
            </div>
          </div>

          {/* Stats */}
          <div className="grid grid-cols-4 gap-4">
            {[
              { label: "Всего туннелей",  value: displayTunnels.length, icon: <NetworkIcon     className="size-4 text-muted-foreground" /> },
              { label: "Активно",         value: upCount,             icon: <ShieldCheckIcon className="size-4 text-emerald-500" />     },
              { label: "Защищены IPsec",  value: ipsecCount,          icon: <LockIcon        className="size-4 text-violet-400" />      },
              { label: "IP-пулов",        value: displayPools.length, icon: <NetworkIcon     className="size-4 text-sky-400" />         },
            ].map((s) => (
              <Frame key={s.label} className="h-full">
                <FramePanel className="relative isolate flex h-full items-start gap-3">
                  <IconTile variant="elevated" aria-hidden="true" className="size-10.5 text-muted-foreground">
                    {s.icon}
                  </IconTile>
                  <div className="flex min-w-0 flex-1 flex-col gap-0.5">
                    <p className="text-muted-foreground text-sm font-medium">{s.label}</p>
                    <p className="text-2xl leading-none font-bold tabular-nums">{s.value}</p>
                  </div>
                </FramePanel>
              </Frame>
            ))}
          </div>

          {/* Page tabs */}
          <div className="flex items-center gap-1 border-b">
            {(["tunnels", "pools"] as PageTab[]).map((tab) => (
              <button key={tab} onClick={() => setPageTab(tab)}
                className={`px-4 py-2 text-sm font-medium border-b-2 transition-colors -mb-px ${pageTab === tab ? "border-foreground text-foreground" : "border-transparent text-muted-foreground hover:text-foreground"}`}>
                {tab === "tunnels" ? "Туннели" : "IP-пулы"}
              </button>
            ))}
          </div>

          {/* ── Tunnels ── */}
          {pageTab === "tunnels" && (
            <DataPageCard>
              <DataPageToolbar
                segmented={{
                  value: tabFilter,
                  onChange: setTabFilter,
                  options: tunnelTabs.map((t) => ({
                    value: t.value,
                    label: t.label,
                    count: t.count,
                  })),
                }}
                search={search}
                onSearchChange={setSearch}
                searchPlaceholder="Поиск по имени, IP…"
                countLabel={`${filtered.length} туннелей`}
              />

              <GreTunnelsDataGrid
                tunnels={filtered}
                servers={displayServers}
                pools={displayPools}
                onCodePreview={setCodePreviewTunnel}
              />
            </DataPageCard>
          )}

          {/* ── IP Pools ── */}
          {pageTab === "pools" && (
            <DataPageCard>
              <DataPageToolbarFrame className="justify-between">
                <span className="text-sm text-muted-foreground">{displayPools.length} пула</span>
                <Button size="sm" variant="outline" onClick={() => { setPForm(defaultPoolForm); setPoolOpen(true) }}>
                  <PlusIcon className="size-4" />
                  Добавить пул
                </Button>
              </DataPageToolbarFrame>
              <GrePoolsDataGrid pools={displayPools} />

              <div className="border-t px-5 py-4">
                <p className="text-xs font-medium text-muted-foreground mb-3">Назначения по пулам</p>
                <div className="flex flex-col gap-3">
                  {displayPools.map((pool) => {
                    const poolTunnels = displayTunnels.filter((t) => t.poolId === pool.id)
                    return (
                      <div key={pool.id}>
                        <p className="text-xs font-mono font-medium mb-1.5">{pool.name}</p>
                        <div className="flex flex-wrap gap-2">
                          {poolTunnels.map((t, tunnelIndex) => (
                            <div key={`${t.id}:${t.serverId}:${t.name}:${tunnelIndex}`} className="flex items-center gap-2 border border-border rounded-md px-3 py-1.5 bg-muted/30 text-xs">
                              <span className={`size-1.5 rounded-full ${STATUS_MAP[t.status].dot}`} />
                              <span className="font-mono font-medium">{t.name}</span>
                              <span className="text-muted-foreground">{t.localInnerIp} ↔ {t.remoteInnerIp}</span>
                              {t.ipsec && <LockIcon className="size-3 text-emerald-400" />}
                            </div>
                          ))}
                        </div>
                      </div>
                    )
                  })}
                </div>
              </div>
            </DataPageCard>
          )}

          {/* RouterOS reference */}
          <OpsPanel title="RouterOS 7.20+ — параметры GRE-интерфейса" contentClassName="px-5 py-4">
              <div className="grid grid-cols-2 gap-x-8 gap-y-1 text-xs font-mono">
                {[
                  ["/interface gre add", ""],
                  ["  name=",            "имя интерфейса"],
                  ["  remote-address=",  "IP удалённого конца (обязателен)"],
                  ["  local-address=",   "0.0.0.0 = авто"],
                  ["  ipsec-secret=",    "PSK → auto peer+policy+proposal"],
                  ["  mtu=",             "по умолчанию 1476"],
                  ["  keepalive=",       "10s,10 (интервал,попытки)"],
                  ["  dscp=",            "inherit | 0-63"],
                  ["  clamp-tcp-mss=",   "yes | no"],
                  ["  allow-fast-path=", "yes | no"],
                  ["/ip address add",    ""],
                  ["  address=x.x.x.x/30", "внутренний IP туннеля"],
                  ["  interface=<name>", ""],
                ].map(([cmd, desc], i) => (
                  <div key={i} className="flex gap-2 py-0.5">
                    <span className="text-foreground/70 shrink-0">{cmd}</span>
                    {desc && <span className="text-muted-foreground"># {desc}</span>}
                  </div>
                ))}
              </div>
          </OpsPanel>
        </div>
      </div>

      {/* ══ Sheet: Code Preview ════════════════════════════════════════════════ */}
      <Sheet open={!!codePreviewTunnel} onOpenChange={(open) => { if (!open) setCodePreviewTunnel(null) }}>
        <SheetContent side="right" className="w-full sm:max-w-2xl flex flex-col gap-0 p-0">
          {codePreviewTunnel && (() => {
            const code = generateRosCommands(codePreviewTunnel, serverById)
            return (
              <>
                <SheetHeader className="px-6 pt-6 pb-4 border-b shrink-0">
                  <div className="flex items-start justify-between gap-4">
                    <div>
                      <SheetTitle className="font-mono">{codePreviewTunnel.name}</SheetTitle>
                      <SheetDescription>Команды RouterOS 7.20+ для создания туннеля</SheetDescription>
                    </div>
                    <Button
                      variant="outline" size="sm"
                      className="shrink-0 gap-1.5"
                      onClick={() => handleCopy(code)}
                    >
                      {copied
                        ? <><CheckIcon className="size-3.5 text-emerald-500" /> Скопировано</>
                        : <><CopyIcon  className="size-3.5" /> Копировать</>}
                    </Button>
                  </div>
                </SheetHeader>

                <div className="flex-1 overflow-y-auto">
                  {/* meta strip */}
                  <div className="flex flex-wrap gap-3 px-6 py-3 border-b bg-muted/30 text-xs">
                    <span className="flex items-center gap-1.5">
                      <span className={`size-1.5 rounded-full ${STATUS_MAP[codePreviewTunnel.status].dot}`} />
                      {STATUS_MAP[codePreviewTunnel.status].label}
                    </span>
                    <span className="text-muted-foreground">·</span>
                    <span>{serverById[codePreviewTunnel.serverId]?.name}</span>
                    <span className="text-muted-foreground">·</span>
                    <span className="font-mono">{codePreviewTunnel.localAddress === "0.0.0.0" ? "авто" : codePreviewTunnel.localAddress} → {codePreviewTunnel.remoteAddress}</span>
                    {codePreviewTunnel.ipsec && (
                      <>
                        <span className="text-muted-foreground">·</span>
                        <span className="flex items-center gap-1 text-emerald-400"><LockIcon className="size-3" /> IPsec {IKE_LABELS[codePreviewTunnel.ipsec.ikeVersion]}</span>
                      </>
                    )}
                  </div>

                  {/* code block */}
                  <pre className="px-6 py-5 text-xs font-mono leading-relaxed text-foreground/90 whitespace-pre overflow-x-auto select-all">
                    {code.split("\n").map((line, i) => {
                      const isComment  = line.startsWith("#")
                      const isSection  = isComment && line.includes("──")
                      const isKey      = /^\s+[a-z]/.test(line)
                      return (
                        <span key={i} className={
                          isSection ? "text-muted-foreground/60"
                          : isComment ? "text-muted-foreground"
                          : isKey    ? "text-sky-400/90"
                          : "text-foreground"
                        }>
                          {line}
                          {"\n"}
                        </span>
                      )
                    })}
                  </pre>
                </div>

                <SheetFooter className="px-6 py-4 border-t shrink-0 flex-row gap-2">
                  <SheetClose render={<Button variant="outline" className="flex-1" />}>Закрыть</SheetClose>
                  <Button className="flex-1 gap-1.5" onClick={() => handleCopy(code)}>
                    {copied ? <CheckIcon className="size-4" /> : <CopyIcon className="size-4" />}
                    {copied ? "Скопировано" : "Копировать команды"}
                  </Button>
                </SheetFooter>
              </>
            )
          })()}
        </SheetContent>
      </Sheet>

      {/* ══ Sheet: Add Tunnel ══════════════════════════════════════════════════ */}
      <Sheet open={tunnelOpen} onOpenChange={setTunnelOpen}>
        <SheetContent side="right" className="w-full sm:max-w-lg flex flex-col gap-0 p-0">
          <SheetHeader className="px-6 pt-6 pb-4 border-b shrink-0">
            <SheetTitle>Новый GRE-туннель</SheetTitle>
            <SheetDescription>RouterOS 7.20+ · /interface gre add</SheetDescription>
          </SheetHeader>

          <div className="flex-1 overflow-y-auto px-6 py-5 flex flex-col gap-5">
            <div className="flex flex-col gap-4">
              <SectionTitle>Основные</SectionTitle>
              <FormField label="Имя интерфейса" required hint="Только латиница, цифры и дефис, например gre-msk-spb">
                <Input className="font-mono" placeholder="gre-msk-spb" value={tForm.name} onChange={(e) => setT("name", e.target.value)} />
              </FormField>
              <FormField label="Сервер (MikroTik)" required>
                <select value={tForm.serverId} onChange={(e) => setT("serverId", e.target.value)}
                  className="h-8 w-full rounded-lg border border-input bg-background px-2.5 text-sm text-foreground outline-none focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring/50">
                  <option value="" disabled>Выбрать сервер…</option>
                  {displayServers.map((s) => <option key={s.id} value={s.id}>{s.name} ({s.site})</option>)}
                </select>
              </FormField>
              <FormField label="Комментарий">
                <Input placeholder="Описание туннеля" value={tForm.comment} onChange={(e) => setT("comment", e.target.value)} />
              </FormField>
              <div className="flex items-center justify-between">
                <span className="text-sm font-medium">Включён</span>
                <FormToggle checked={tForm.enabled} onChange={(v) => setT("enabled", v)} />
              </div>
            </div>

            <div className="flex flex-col gap-4">
              <SectionTitle>Эндпоинты</SectionTitle>
              <FormField label="Локальный адрес" hint="Оставьте пустым или 0.0.0.0 для автоопределения">
                <Input className="font-mono" placeholder="0.0.0.0" value={tForm.localAddress} onChange={(e) => setT("localAddress", e.target.value)} />
              </FormField>
              <FormField label="Удалённый адрес" required hint="Внешний IP удалённого MikroTik">
                <Input className="font-mono" placeholder="203.0.113.1" value={tForm.remoteAddress} onChange={(e) => setT("remoteAddress", e.target.value)} />
              </FormField>
            </div>

            <div className="flex flex-col gap-4">
              <SectionTitle>Внутренний IP</SectionTitle>
              <FormField label="IP-пул" required hint="Из какого пула выделяется /30-блок">
                <select value={tForm.poolId} onChange={(e) => setT("poolId", e.target.value)}
                  className="h-8 w-full rounded-lg border border-input bg-background px-2.5 text-sm text-foreground outline-none focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring/50">
                  <option value="" disabled>Выбрать пул…</option>
                  {displayPools.map((p) => <option key={p.id} value={p.id}>{p.name} ({p.cidr}) — свободно {p.total - p.allocated} блоков</option>)}
                </select>
              </FormField>
              <div className="grid grid-cols-2 gap-3">
                <FormField label="Локальный IP" required hint="/ip address на этом конце">
                  <Input className="font-mono" placeholder="10.200.0.1/30" value={tForm.localInnerIp} onChange={(e) => setT("localInnerIp", e.target.value)} />
                </FormField>
                <FormField label="Удалённый IP" required hint="/ip address на другом конце">
                  <Input className="font-mono" placeholder="10.200.0.2/30" value={tForm.remoteInnerIp} onChange={(e) => setT("remoteInnerIp", e.target.value)} />
                </FormField>
              </div>
            </div>

            <div className="flex flex-col gap-4">
              <SectionTitle>IPsec</SectionTitle>
              <div className="flex items-center justify-between">
                <div>
                  <p className="text-sm font-medium">Включить IPsec</p>
                  <p className="text-xs text-muted-foreground">RouterOS автоматически создаст peer, policy и proposal</p>
                </div>
                <FormToggle checked={tForm.ipsecEnabled} onChange={(v) => setT("ipsecEnabled", v)} />
              </div>

              {tForm.ipsecEnabled && (
                <div className="flex flex-col gap-4 pl-4 border-l-2 border-emerald-500/30">
                  <FormField label="Пароль (PSK)" required hint="ipsec-secret — pre-shared key для автоматического IKE">
                    <div className="relative">
                      <Input type={tForm.ipsecShowSecret ? "text" : "password"} className="font-mono pr-9"
                        placeholder="Минимум 8 символов" value={tForm.ipsecSecret} onChange={(e) => setT("ipsecSecret", e.target.value)} />
                      <button type="button" onClick={() => setT("ipsecShowSecret", !tForm.ipsecShowSecret)}
                        className="absolute right-2.5 top-1/2 -translate-y-1/2 text-muted-foreground hover:text-foreground">
                        {tForm.ipsecShowSecret ? <EyeOffIcon className="size-3.5" /> : <EyeIcon className="size-3.5" />}
                      </button>
                    </div>
                  </FormField>
                  <FormField label="IKE-версия">
                    <SegmentedControl value={tForm.ipsecIkeVersion} onChange={(v) => setT("ipsecIkeVersion", v)}
                      options={[{ value: "ikev1", label: "IKEv1" }, { value: "ikev2", label: "IKEv2 (рек.)" }]} />
                  </FormField>
                  <div className="grid grid-cols-2 gap-3">
                    <FormField label="Шифрование">
                      <select value={tForm.ipsecEncAlg} onChange={(e) => setT("ipsecEncAlg", e.target.value as IpsecEncAlg)}
                        className="h-8 w-full rounded-lg border border-input bg-background px-2.5 text-sm text-foreground outline-none focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring/50">
                        {(Object.entries(ENC_LABELS) as [IpsecEncAlg, string][]).map(([v, l]) => <option key={v} value={v}>{l}</option>)}
                      </select>
                    </FormField>
                    <FormField label="Хеш-алгоритм">
                      <select value={tForm.ipsecAuthAlg} onChange={(e) => setT("ipsecAuthAlg", e.target.value as IpsecAuthAlg)}
                        className="h-8 w-full rounded-lg border border-input bg-background px-2.5 text-sm text-foreground outline-none focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring/50">
                        {(Object.entries(AUTH_LABELS) as [IpsecAuthAlg, string][]).map(([v, l]) => <option key={v} value={v}>{l}</option>)}
                      </select>
                    </FormField>
                  </div>
                  <FormField label="DH-группа" hint="Группа Диффи-Хеллмана для обмена ключами">
                    <select value={tForm.ipsecDhGroup} onChange={(e) => setT("ipsecDhGroup", e.target.value as IpsecDhGroup)}
                      className="h-8 w-full rounded-lg border border-input bg-background px-2.5 text-sm text-foreground outline-none focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring/50">
                      {(Object.entries(DH_LABELS) as [IpsecDhGroup, string][]).map(([v, l]) => <option key={v} value={v}>{l}</option>)}
                    </select>
                  </FormField>
                  <div className="grid grid-cols-2 gap-3">
                    <FormField label="Срок жизни SA" hint="Формат: 1d 00:00:00">
                      <Input className="font-mono" value={tForm.ipsecLifetime} onChange={(e) => setT("ipsecLifetime", e.target.value)} />
                    </FormField>
                    <div className="flex items-center justify-between pt-6">
                      <span className="text-sm font-medium">PFS</span>
                      <FormToggle checked={tForm.ipsecPfs} onChange={(v) => setT("ipsecPfs", v)} />
                    </div>
                  </div>
                </div>
              )}
            </div>

            <div className="flex flex-col gap-4">
              <button type="button" onClick={() => setT("showAdvanced", !tForm.showAdvanced)}
                className="flex items-center gap-2 text-xs font-semibold uppercase tracking-wider text-muted-foreground hover:text-foreground transition-colors">
                {tForm.showAdvanced ? <ChevronDownIcon className="size-3.5" /> : <ChevronRightIcon className="size-3.5" />}
                Дополнительно
                <div className="flex-1 h-px bg-border" />
              </button>
              {tForm.showAdvanced && (
                <div className="flex flex-col gap-4">
                  <div className="grid grid-cols-3 gap-3">
                    <FormField label="MTU" hint="По умолч. 1476">
                      <Input type="number" className="font-mono" value={tForm.mtu} onChange={(e) => setT("mtu", Number(e.target.value))} />
                    </FormField>
                    <FormField label="Keepalive, с" hint="0 = откл.">
                      <Input type="number" className="font-mono" value={tForm.keepaliveInterval} onChange={(e) => setT("keepaliveInterval", Number(e.target.value))} />
                    </FormField>
                    <FormField label="Попытки">
                      <Input type="number" className="font-mono" value={tForm.keepaliveRetries} onChange={(e) => setT("keepaliveRetries", Number(e.target.value))} />
                    </FormField>
                  </div>
                  <FormField label="DSCP">
                    <select value={tForm.dscp} onChange={(e) => setT("dscp", e.target.value)}
                      className="h-8 w-full rounded-lg border border-input bg-background px-2.5 text-sm text-foreground outline-none focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring/50">
                      <option value="inherit">inherit</option>
                      {Array.from({ length: 64 }, (_, i) => <option key={i} value={String(i)}>{i}</option>)}
                    </select>
                  </FormField>
                  {[
                    { key: "clampTcpMss" as const, label: "Clamp TCP MSS", desc: "Ограничить MSS до MTU туннеля" },
                    { key: "allowFastPath" as const, label: "Allow Fast Path", desc: "Аппаратное ускорение трафика" },
                  ].map(({ key, label, desc }) => (
                    <div key={key} className="flex items-center justify-between">
                      <div>
                        <p className="text-sm font-medium">{label}</p>
                        <p className="text-xs text-muted-foreground">{desc}</p>
                      </div>
                      <FormToggle checked={tForm[key] as boolean} onChange={(v) => setT(key, v)} />
                    </div>
                  ))}
                </div>
              )}
            </div>
          </div>

          <SheetFooter className="px-6 py-4 border-t shrink-0 flex-row gap-2">
            <SheetClose render={<Button variant="outline" className="flex-1" />}>Отмена</SheetClose>
            <Button className="flex-1" onClick={() => setTunnelOpen(false)}>Создать туннель</Button>
          </SheetFooter>
        </SheetContent>
      </Sheet>

      {/* ══ Sheet: Add Pool ════════════════════════════════════════════════════ */}
      <Sheet open={poolOpen} onOpenChange={setPoolOpen}>
        <SheetContent side="right" className="w-full sm:max-w-md flex flex-col gap-0 p-0">
          <SheetHeader className="px-6 pt-6 pb-4 border-b shrink-0">
            <SheetTitle>Новый IP-пул</SheetTitle>
            <SheetDescription>Пул адресов для назначения внутренних IP GRE-туннелям</SheetDescription>
          </SheetHeader>
          <div className="flex-1 overflow-y-auto px-6 py-5 flex flex-col gap-5">
            <div className="flex flex-col gap-4">
              <SectionTitle>Параметры пула</SectionTitle>
              <FormField label="Имя пула" required hint="Например pool-gre-office или pool-gre-dc2">
                <Input className="font-mono" placeholder="pool-gre-core" value={pForm.name}
                  onChange={(e) => setPForm((f) => ({ ...f, name: e.target.value }))} />
              </FormField>
              <FormField label="Диапазон CIDR" required hint="Блок, из которого будут нарезаться /30 на каждый туннель">
                <Input className="font-mono" placeholder="10.200.0.0/24" value={pForm.cidr}
                  onChange={(e) => setPForm((f) => ({ ...f, cidr: e.target.value }))} />
              </FormField>
              <FormField label="Назначение / Комментарий">
                <Input placeholder="Ядровые межузловые туннели" value={pForm.comment}
                  onChange={(e) => setPForm((f) => ({ ...f, comment: e.target.value }))} />
              </FormField>
              {pForm.cidr && /\/\d+$/.test(pForm.cidr) && (() => {
                const prefix = parseInt(pForm.cidr.split("/")[1] ?? "0")
                const blocks = prefix <= 30 ? Math.pow(2, 30 - prefix) : 0
                return blocks > 0 ? (
                  <div className="rounded-lg border border-border bg-muted/30 px-4 py-3 text-sm">
                    <p className="text-muted-foreground">
                      Доступно <span className="font-semibold text-foreground font-mono">{blocks}</span> блоков /30
                      {" "}= до <span className="font-semibold text-foreground font-mono">{blocks}</span> туннелей
                    </p>
                  </div>
                ) : null
              })()}
            </div>
          </div>
          <SheetFooter className="px-6 py-4 border-t shrink-0 flex-row gap-2">
            <SheetClose render={<Button variant="outline" className="flex-1" />}>Отмена</SheetClose>
            <Button className="flex-1" onClick={() => setPoolOpen(false)}>Создать пул</Button>
          </SheetFooter>
        </SheetContent>
      </Sheet>
    </div>
  )
}
