"use client"

import { useCallback, useEffect, useMemo, useState } from "react"
import { PageHeader } from "@/components/page-header"
import { Card } from "@/components/ui/card"
import { Button } from "@/components/ui/button"
import { Input } from "@/components/ui/input"
import { Sheet, SheetContent, SheetDescription, SheetFooter, SheetHeader, SheetTitle } from "@/components/ui/sheet"
import { StatusDot } from "@/components/status-dot"
import { Flag } from "@/components/flag"
import { useDataSource } from "@/lib/data-source"
import { cn } from "@/lib/utils"
import { servers as mockServers, type Server } from "@/lib/data"
import { PlusIcon, SaveIcon, TrashIcon, SearchIcon, XIcon, PencilIcon, ChevronDownIcon, ChevronRightIcon, AlertCircleIcon, CheckIcon } from "lucide-react"

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 GatewayOption {
  id: string
  name: string
  ip: string
  status: "up" | "down"
}

const INFER_COUNTRIES = [
  { code: "RU", keys: ["MSK", "SPB", "RTK", "MTS", "VPSVILLE", "IHOR"] },
  { code: "SE", keys: ["SWE", "STO"] },
  { code: "FI", keys: ["HEL", "FIN"] },
  { code: "DE", keys: ["FRA", "GER", "DE"] },
  { code: "NL", keys: ["AMS", "NLD", "NL"] },
  { code: "SG", keys: ["SGP", "SIN", "SG"] },
  { code: "TR", keys: ["TUR", "TR"] },
  { code: "US", keys: ["USA", "US", "NYC", "LAX"] },
]

function inferCountry(name: string): string | null {
  const upper = name.toUpperCase()
  for (const c of INFER_COUNTRIES) {
    if (c.keys.some(k => upper.includes(k))) return c.code
  }
  return null
}

const COUNTRY_OPTIONS = [
  { code: "RU", label: "Россия" }, { code: "DE", label: "Германия" },
  { code: "NL", label: "Нидерланды" }, { code: "SG", label: "Сингапур" },
  { code: "FI", label: "Финляндия" }, { code: "SE", label: "Швеция" },
  { code: "FR", label: "Франция" }, { code: "GB", label: "Великобритания" },
  { code: "PL", label: "Польша" }, { code: "US", label: "США" },
  { code: "UA", label: "Украина" }, { code: "TR", label: "Турция" },
  { code: "JP", label: "Япония" }, { code: "HK", label: "Гонконг" },
  { code: "KZ", label: "Казахстан" }, { code: "BY", label: "Беларусь" },
  { code: "LT", label: "Литва" }, { code: "LV", label: "Латвия" },
  { code: "EE", label: "Эстония" }, { code: "CZ", label: "Чехия" },
  { code: "AT", label: "Австрия" }, { code: "CH", label: "Швейцария" },
  { code: "NO", label: "Норвегия" },
]

interface RecursiveRouteRow {
  id: string
  dstAddress: string
  gateway: string
  distance: number
  scope: number | null
  targetScope: number | null
  routingTable: string
  checkGateway: string
  comment: string
  disabled: boolean
  country: string
}

interface RouteGroup {
  key: string
  dstAddress: string
  routingTable: string
  comment: string
  endpoints: RecursiveRouteRow[]
}

function makeApiFetch(backendUrl: string) {
  return async function apiFetch<T>(path: string, init?: RequestInit): Promise<T> {
    const headers: HeadersInit = init?.body ? { "Content-Type": "application/json" } : {}
    const res = await fetch(backendUrl.replace(/\/$/, "") + path, {
      ...init,
      headers: { ...headers, ...(init?.headers ?? {}) },
    })
    if (!res.ok) {
      let message = res.statusText
      try {
        const err = await res.json() as { error?: string }
        message = err.error ?? message
      } catch {
        const text = await res.text().catch(() => "")
        if (text) message = text
      }
      throw new Error(message)
    }
    return res.json() as Promise<T>
  }
}

function mapBackendServer(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,
  }
}

interface RouteForm {
  dstAddress: string
  routingTable: string
  comment: string
  endpoints: RouteEndpointForm[]
}

interface RouteEndpointForm {
  id: string
  gateway: string
  distance: number
  scope: number | null
  targetScope: number | null
  checkGateway: string
  country: string
}

const newEndpoint = (): RouteEndpointForm => ({
  id: `ep-${Date.now()}-${Math.random().toString(36).slice(2, 6)}`,
  gateway: "",
  distance: 1,
  scope: null,
  targetScope: null,
  checkGateway: "ping",
  country: "",
})

const emptyForm = (): RouteForm => ({
  dstAddress: "",
  routingTable: "main",
  comment: "",
  endpoints: [newEndpoint()],
})

function Field({ label, hint, required, children }: {
  label: string
  hint?: string
  required?: boolean
  children: React.ReactNode
}) {
  return (
    <div className="flex flex-col gap-1.5">
      <label className="text-sm font-medium">
        {label}{required && <span className="text-destructive ml-0.5">*</span>}
      </label>
      {children}
      {hint && <p className="text-xs text-muted-foreground">{hint}</p>}
    </div>
  )
}

function SectionTitle({ children }: { children: React.ReactNode }) {
  return (
    <div className="flex items-center gap-2 py-0.5">
      <span className="text-xs font-semibold uppercase tracking-wider text-muted-foreground">{children}</span>
      <div className="flex-1 h-px bg-border" />
    </div>
  )
}

function RouteGroupRows({
  group, expanded, onToggle, onEdit, onDelete,
}: {
  group: RouteGroup
  expanded: boolean
  onToggle: () => void
  onEdit: () => void
  onDelete: () => void
}) {
  const [confirmDel, setConfirmDel] = useState(false)
  const bestDistance = Math.min(...group.endpoints.map(ep => ep.distance))
  const sorted = [...group.endpoints].sort((a, b) => a.distance - b.distance)
  return (
    <>
      <tr
        className={cn(
          "hover:bg-muted/40 transition-colors cursor-pointer group",
          expanded && "bg-muted/30",
        )}
        onClick={onToggle}
      >
        <td className="px-5 py-3">
          <div className="flex items-start gap-2">
            {expanded
              ? <ChevronDownIcon className="size-3.5 mt-0.5 shrink-0 text-muted-foreground" />
              : <ChevronRightIcon className="size-3.5 mt-0.5 shrink-0 text-muted-foreground/40" />}
            <div className="min-w-0">
              <p className="font-medium truncate">{group.dstAddress}</p>
              <p className="text-xs font-mono text-muted-foreground">{group.comment || "—"}</p>
            </div>
          </div>
        </td>
        <td className="px-4 py-3">
          <div className="flex flex-col gap-0.5">
            {sorted.map((ep, idx) => {
              const code = ep.country || inferCountry(ep.gateway)
              return (
                <div key={ep.id} className="flex items-center gap-1.5 text-[11px] font-mono">
                  <span className={cn(
                    "size-1.5 rounded-full shrink-0",
                    idx === 0 ? "bg-emerald-500" : "bg-sky-500",
                  )} />
                  {code ? <Flag code={code} size={14} className="shrink-0" /> : <span className="text-[10px] text-muted-foreground w-3.5 text-center shrink-0">?</span>}
                  <span className="font-semibold text-sky-600 dark:text-sky-400 truncate min-w-0">{ep.gateway}</span>
                </div>
              )
            })}
          </div>
        </td>
        <td className="px-4 py-3 text-xs tabular-nums">{group.endpoints.length}</td>
        <td className="px-4 py-3 font-mono text-xs">d{bestDistance}</td>
        <td className="px-4 py-3 font-mono text-xs text-muted-foreground">{group.routingTable || "main"}</td>
        <td className="px-3 py-3" onClick={e => e.stopPropagation()}>
          <div className="flex items-center gap-0.5 opacity-0 group-hover:opacity-100 transition-opacity">
            <Button size="sm" variant="ghost" className="size-7 p-0 text-muted-foreground hover:text-foreground" onClick={onEdit}><PencilIcon className="size-3.5" /></Button>
            <Button size="sm" variant="ghost" className={cn("size-7 p-0 transition-colors", confirmDel ? "text-destructive bg-destructive/10 hover:bg-destructive/20" : "text-muted-foreground hover:text-destructive")} onClick={() => { if (!confirmDel) setConfirmDel(true); else onDelete() }} onBlur={() => setConfirmDel(false)}>
              {confirmDel ? <AlertCircleIcon className="size-3.5" /> : <TrashIcon className="size-3.5" />}
            </Button>
          </div>
        </td>
      </tr>
      {expanded && (
        <tr className="bg-muted/20">
          <td colSpan={6} className="px-8 py-5 border-b border-border/50">
            <div className="flex flex-col gap-4">
              <div className="flex flex-wrap gap-x-6 gap-y-2 text-xs">
                <span className="text-muted-foreground">Route: <span className="font-mono text-foreground">{group.dstAddress}</span></span>
                <span className="text-muted-foreground">Table: <span className="font-mono text-foreground">{group.routingTable || "main"}</span></span>
                {group.comment && <span className="text-muted-foreground italic">{group.comment}</span>}
              </div>
              <div className="grid grid-cols-1 md:grid-cols-2 gap-2.5">
                {sorted.map((ep, idx) => (
                  <div key={ep.id} className="rounded-lg border border-border bg-background px-4 py-3">
                    <div className="flex items-center justify-between">
                      <div className="flex items-center gap-1.5">
                        {(ep.country || inferCountry(ep.gateway)) && (
                          <Flag code={ep.country || inferCountry(ep.gateway) || ""} size={16} />
                        )}
                        <span className="text-[11px] font-semibold text-muted-foreground uppercase tracking-wide">Endpoint {idx + 1}</span>
                      </div>
                      <span className="text-[11px] font-mono">distance: {ep.distance}</span>
                    </div>
                    <p className="mt-1.5 font-mono text-sm break-all leading-tight">{ep.gateway}</p>
                    <div className="mt-1.5 text-[11px] text-muted-foreground flex items-center gap-3">
                      <span>scope: {ep.scope ?? "—"}</span>
                      <span>t.scope: {ep.targetScope ?? "—"}</span>
                      <span>check: {ep.checkGateway || "—"}</span>
                    </div>
                  </div>
                ))}
              </div>
            </div>
          </td>
        </tr>
      )}
    </>
  )
}

function EndpointCountryField({ value, onChange }: { value: string; onChange: (v: string) => void }) {
  const [query, setQuery] = useState("")
  const q = query.trim().toUpperCase()
  const visible = q.length === 0
    ? COUNTRY_OPTIONS
    : COUNTRY_OPTIONS.filter(c => c.code.startsWith(q) || c.label.toLowerCase().includes(query.trim().toLowerCase()))

  return (
    <div className="flex flex-col gap-2">
      <label className="text-sm font-medium">Страна</label>
      <div className="flex items-center gap-2">
        <div className="relative flex-1">
          <Input placeholder="Поиск или код (RU, DE…)" value={query}
            onChange={e => {
              const v = e.target.value.toUpperCase().slice(0, 3)
              setQuery(v)
              if (v.length === 2) {
                const match = COUNTRY_OPTIONS.find(c => c.code === v)
                if (match) onChange(match.code)
                else onChange(v)
              }
            }}
            className="font-mono pr-10 h-8 text-sm" />
          {value && (
            <span className="absolute right-2.5 top-1/2 -translate-y-1/2">
              <Flag code={value} size={20} />
            </span>
          )}
        </div>
        {value && (
          <span className="text-sm font-mono text-muted-foreground shrink-0">
            {COUNTRY_OPTIONS.find(c => c.code === value)?.label ?? value}
          </span>
        )}
      </div>
      <div className="grid grid-cols-5 gap-1.5 max-h-40 overflow-y-auto pr-0.5">
        {visible.map(c => (
          <button key={c.code} type="button"
            onClick={() => { onChange(c.code); setQuery("") }}
            title={`${c.code} · ${c.label}`}
            className={cn(
              "flex flex-col items-center gap-1 px-1 py-2 rounded-lg border text-[10px] transition-all",
              value === c.code
                ? "border-primary bg-primary/5 ring-1 ring-primary/30 font-semibold text-primary"
                : "border-border hover:border-muted-foreground/40 hover:bg-muted/40 text-muted-foreground",
            )}>
            <Flag code={c.code} size={24} />
            <span className="font-mono leading-none">{c.code}</span>
          </button>
        ))}
      </div>
    </div>
  )
}

function RouteSheet({
  open, mode, initial, onSave, onClose, gateways,
}: {
  open: boolean
  mode: "create" | "edit"
  initial: RouteForm
  onSave: (v: RouteForm) => void
  onClose: () => void
  gateways: GatewayOption[]
}) {
  const [form, setForm] = useState<RouteForm>(initial)
  const [error, setError] = useState<string | null>(null)

  // eslint-disable-next-line react-hooks/set-state-in-effect
  useEffect(() => { setForm(initial); setError(null) }, [initial, open])
  const set = <K extends keyof RouteForm>(k: K, v: RouteForm[K]) => setForm(f => ({ ...f, [k]: v }))
  const setEp = <K extends keyof RouteEndpointForm>(id: string, k: K, v: RouteEndpointForm[K]) =>
    setForm(f => ({ ...f, endpoints: f.endpoints.map(ep => ep.id === id ? { ...ep, [k]: v } : ep) }))

  function handleSave() {
    if (!form.dstAddress.trim()) { setError("Dst Address обязателен"); return }
    if (form.endpoints.length === 0) { setError("Добавь хотя бы одну конечную точку"); return }
    if (form.endpoints.some(ep => !ep.gateway.trim())) { setError("У каждой конечной точки должен быть Gateway"); return }
    setError(null)
    onSave(form)
  }

  return (
    <Sheet open={open} onOpenChange={v => { if (!v) onClose() }}>
      <SheetContent side="right" className="w-full sm:max-w-lg flex flex-col gap-0 p-0" showCloseButton={false}>
        <SheetHeader className="px-5 pt-5 pb-4 border-b shrink-0">
          <div className="flex items-start justify-between gap-2">
            <div>
              <SheetTitle className="text-base">{mode === "create" ? "Новый маршрут" : "Редактировать маршрут"}</SheetTitle>
              <SheetDescription className="text-xs mt-0.5">Рекурсивный статический маршрут</SheetDescription>
            </div>
            <Button variant="ghost" size="icon-sm" onClick={onClose} className="shrink-0 mt-0.5"><XIcon className="size-4" /></Button>
          </div>
        </SheetHeader>
        <div className="flex-1 overflow-y-auto overflow-x-hidden px-6 py-5 flex flex-col gap-5">
          <div className="flex flex-col gap-4">
            <SectionTitle>Основные</SectionTitle>
            <Field label="Dst Address" required hint="Например 8.8.8.8/32 или 1.1.1.0/24">
              <Input className="font-mono h-9" placeholder="8.8.8.8/32" value={form.dstAddress} onChange={(e) => set("dstAddress", e.target.value)} />
            </Field>
          </div>

          <div className="flex flex-col gap-4">
            <SectionTitle>Конечные точки</SectionTitle>
            <div className="flex flex-col gap-3">
              {form.endpoints.map((ep, idx) => (
                <div key={ep.id} className="rounded-lg border border-border bg-muted/20 p-3 flex flex-col gap-3">
                  <div className="flex items-center justify-between">
                    <span className="text-xs font-semibold text-muted-foreground uppercase tracking-wide">
                      Endpoint {idx + 1}
                    </span>
                    <button
                      type="button"
                      onClick={() => setForm(f => ({ ...f, endpoints: f.endpoints.filter(x => x.id !== ep.id) }))}
                      className="text-muted-foreground hover:text-destructive transition-colors"
                      disabled={form.endpoints.length <= 1}
                    >
                      <TrashIcon className="size-3.5" />
                    </button>
                  </div>

                  <EndpointCountryField value={ep.country} onChange={(v) => setEp(ep.id, "country", v)} />

                  <Field label="Gateway" required hint="Можно выбрать карточкой ниже или ввести вручную в формате ip%gateway">
                    <Input className="font-mono h-9" placeholder="1.2.3.4%GW-NAME" value={ep.gateway} onChange={(e) => setEp(ep.id, "gateway", e.target.value)} />
                  </Field>

                  <div className="grid grid-cols-2 gap-2">
                    <Field label="Distance (приоритет)">
                      <Input type="number" className="h-9" value={ep.distance} onChange={(e) => setEp(ep.id, "distance", Number(e.target.value) || 1)} />
                    </Field>
                    <Field label="Check Gateway">
                      <Input className="h-9 font-mono" placeholder="ping" value={ep.checkGateway} onChange={(e) => setEp(ep.id, "checkGateway", e.target.value)} />
                    </Field>
                  </div>

                  <div className="grid grid-cols-2 gap-2">
                    <Field label="Scope">
                      <Input type="number" className="h-9" value={ep.scope ?? ""} onChange={(e) => setEp(ep.id, "scope", e.target.value ? Number(e.target.value) : null)} />
                    </Field>
                    <Field label="T.Scope">
                      <Input type="number" className="h-9" value={ep.targetScope ?? ""} onChange={(e) => setEp(ep.id, "targetScope", e.target.value ? Number(e.target.value) : null)} />
                    </Field>
                  </div>

                  <div className="flex flex-col gap-1.5 max-h-[180px] overflow-y-auto overflow-x-hidden pr-1">
              {gateways.map((g) => {
                const value = `${g.ip}%${g.name}`
                const selected = ep.gateway === value
                const country = inferCountry(g.name)
                return (
                  <button
                    key={`${ep.id}-${g.id}`}
                    type="button"
                    onClick={() => setEp(ep.id, "gateway", value)}
                    className={cn(
                      "text-left rounded-lg border px-3 py-2.5 transition-all",
                      selected
                        ? "border-primary bg-primary/5 ring-1 ring-primary/30"
                        : "border-border hover:border-muted-foreground/40 hover:bg-muted/40",
                    )}
                  >
                    <div className="flex items-center gap-2">
                      <span className={cn("size-1.5 rounded-full shrink-0", g.status === "up" ? "bg-emerald-500" : "bg-red-500")} />
                      {country && <Flag code={country} className="shrink-0" />}
                      <span className="font-mono text-xs font-semibold flex-1 truncate">{g.name}</span>
                      {selected && <CheckIcon className="size-3.5 text-primary shrink-0" />}
                    </div>
                    <div className="mt-1.5 grid grid-cols-[auto_auto_1fr] items-start gap-2 text-[11px] text-muted-foreground font-mono">
                      <span className="text-muted-foreground/50 break-all">{g.ip}</span>
                      <span className="text-muted-foreground/30">→</span>
                      <span className={cn("min-w-0 break-all whitespace-normal leading-tight", selected ? "text-primary font-medium" : "")}>
                        {value}
                      </span>
                    </div>
                  </button>
                )
              })}
                  </div>
                </div>
              ))}
              <Button type="button" variant="outline" size="sm" className="w-fit gap-1.5" onClick={() => setForm(f => ({ ...f, endpoints: [...f.endpoints, newEndpoint()] }))}>
                <PlusIcon className="size-3.5" />Добавить endpoint
              </Button>
              {gateways.length === 0 && (
                <div className="rounded-md border border-dashed p-4 text-center text-sm text-muted-foreground">
                  Нет доступных шлюзов на выбранном роутере.
                </div>
              )}
            </div>
          </div>

          <div className="flex flex-col gap-4">
            <SectionTitle>Параметры</SectionTitle>
            <Field label="Routing Table"><Input className="h-9 font-mono" value={form.routingTable} onChange={(e) => set("routingTable", e.target.value)} /></Field>
            <Field label="Комментарий">
              <Input className="h-9" value={form.comment} onChange={(e) => set("comment", e.target.value)} />
            </Field>
          </div>
          {error && <div className="flex items-center gap-2 text-sm text-destructive bg-destructive/10 border border-destructive/20 px-3 py-2 rounded-md"><AlertCircleIcon className="size-4 shrink-0" />{error}</div>}
        </div>
        <SheetFooter className="px-6 py-4 border-t shrink-0 gap-2">
          <Button variant="outline" onClick={onClose} className="flex-1">Отмена</Button>
          <Button onClick={handleSave} className="flex-1"><CheckIcon className="size-4" />{mode === "create" ? "Добавить" : "Сохранить"}</Button>
        </SheetFooter>
      </SheetContent>
    </Sheet>
  )
}

function TypeChip({ type }: { type: "jump-host" | "exit-node" | "home-router" }) {
  return (
    <span className={cn(
      "inline-flex items-center rounded px-1.5 py-0.5 text-[10px] font-bold border shrink-0",
      type === "home-router" ? "bg-emerald-500/10 text-emerald-600 dark:text-emerald-400 border-emerald-500/20" :
      type === "jump-host"
        ? "bg-violet-500/10 text-violet-600 dark:text-violet-400 border-violet-500/20"
        : "bg-sky-500/10 text-sky-600 dark:text-sky-400 border-sky-500/20",
    )}>
      {type === "jump-host" ? "JH" : type === "home-router" ? "HR" : "EN"}
    </span>
  )
}

export default function RecursiveRoutesPage() {
  const { mode, backendUrl, backendStatus } = useDataSource()
  const isLive = mode === "live" && backendStatus === true
  const apiFetch = useMemo(() => makeApiFetch(backendUrl), [backendUrl])

  const [servers, setServers] = useState<Server[]>(mockServers)
  const [selectedServerId, setSelectedServerId] = useState<string>(mockServers[0]?.id ?? "")
  const [rows, setRows] = useState<RecursiveRouteRow[]>([])
  const [busy, setBusy] = useState<"load" | "save" | "from" | "to" | null>(null)
  const [search, setSearch] = useState("")
  const [sheetOpen, setSheetOpen] = useState(false)
  const [sheetMode, setSheetMode] = useState<"create" | "edit">("create")
  const [sheetInitial, setSheetInitial] = useState<RouteForm>(emptyForm())
  const [editingGroupKey, setEditingGroupKey] = useState<string | null>(null)
  const [gatewayOptions, setGatewayOptions] = useState<GatewayOption[]>([])
  const [expandedGroupKey, setExpandedGroupKey] = useState<string | null>(null)
  const [opError, setOpError] = useState<string | null>(null)

  useEffect(() => {
    if (!isLive) {
      // eslint-disable-next-line react-hooks/set-state-in-effect
      setServers(mockServers)
      setSelectedServerId(mockServers[0]?.id ?? "")
      setRows([])
      return
    }
    apiFetch<BackendServer[]>("/api/servers")
      .then((data) => {
        const mapped = data.map(mapBackendServer)
        setServers(mapped)
        setSelectedServerId(prev => (mapped.some(s => s.id === prev) ? prev : (mapped[0]?.id ?? "")))
      })
      .catch(() => {
        setServers([])
        setSelectedServerId("")
      })
  }, [isLive, apiFetch])

  const loadRoutes = useCallback(async () => {
    if (!isLive || !selectedServerId) return
    setOpError(null)
    setBusy("load")
    try {
      const res = await apiFetch<{ routes: RecursiveRouteRow[] }>(`/api/recursive-routes?serverId=${selectedServerId}`)
      setRows(res.routes)
    } catch (e) {
      setRows([])
      setOpError(e instanceof Error ? e.message : "Не удалось загрузить маршруты")
    } finally {
      setBusy(null)
    }
  }, [isLive, selectedServerId, apiFetch])

  const loadGateways = useCallback(async () => {
    if (!isLive || !selectedServerId) return
    try {
      const res = await apiFetch<{ gateways: GatewayOption[] }>(`/api/recursive-routes/gateways?serverId=${selectedServerId}`)
      setGatewayOptions(res.gateways)
    } catch {
      setGatewayOptions([])
    }
  }, [isLive, selectedServerId, apiFetch])

  useEffect(() => {
    // eslint-disable-next-line react-hooks/set-state-in-effect
    void loadRoutes()
  }, [loadRoutes])

  useEffect(() => {
    // eslint-disable-next-line react-hooks/set-state-in-effect
    void loadGateways()
  }, [loadGateways])

  const saveToDb = useCallback(async () => {
    if (!isLive || !selectedServerId) return
    setOpError(null)
    setBusy("save")
    try {
      await apiFetch<{ ok: boolean }>("/api/recursive-routes", {
        method: "PUT",
        body: JSON.stringify({ serverId: selectedServerId, routes: rows }),
      })
      await loadRoutes()
    } catch (e) {
      setOpError(e instanceof Error ? e.message : "Не удалось сохранить маршруты в БД")
    } finally {
      setBusy(null)
    }
  }, [isLive, selectedServerId, rows, apiFetch, loadRoutes])

  const syncFromRouter = useCallback(async () => {
    if (!isLive || !selectedServerId) return
    setOpError(null)
    setBusy("from")
    try {
      await apiFetch<{ ok: boolean }>("/api/recursive-routes/sync/from-router", {
        method: "POST",
        body: JSON.stringify({ serverId: selectedServerId }),
      })
      await loadRoutes()
    } catch (e) {
      setOpError(e instanceof Error ? e.message : "Не удалось синхронизировать маршруты с роутера")
    } finally {
      setBusy(null)
    }
  }, [isLive, selectedServerId, apiFetch, loadRoutes])

  const syncToRouter = useCallback(async () => {
    if (!isLive || !selectedServerId) return
    setOpError(null)
    setBusy("to")
    try {
      await apiFetch<{ ok: boolean }>("/api/recursive-routes/sync/to-router", {
        method: "POST",
        body: JSON.stringify({ serverId: selectedServerId }),
      })
    } catch (e) {
      setOpError(e instanceof Error ? e.message : "Не удалось применить маршруты на роутер")
    } finally {
      setBusy(null)
    }
  }, [isLive, selectedServerId, apiFetch])

  function groupKeyOf(row: RecursiveRouteRow): string {
    return row.dstAddress.trim().toLowerCase()
  }
  function openCreate() {
    setSheetMode("create")
    setEditingGroupKey(null)
    setSheetInitial(emptyForm())
    setSheetOpen(true)
  }
  function openEdit(group: RouteGroup) {
    setSheetMode("edit")
    setEditingGroupKey(group.key)
    setSheetInitial({
      dstAddress: group.dstAddress,
      routingTable: group.routingTable,
      comment: group.comment,
      endpoints: group.endpoints.map(ep => ({
        id: ep.id,
        gateway: ep.gateway,
        distance: ep.distance,
        scope: ep.scope,
        targetScope: ep.targetScope,
        checkGateway: ep.checkGateway,
        country: ep.country || "",
      })),
    })
    setSheetOpen(true)
  }
  function handleSaveSheet(v: RouteForm) {
    const toRow = (ep: RouteEndpointForm, id: string): RecursiveRouteRow => ({
      id,
      dstAddress: v.dstAddress,
      gateway: ep.gateway,
      distance: ep.distance,
      scope: ep.scope,
      targetScope: ep.targetScope,
      routingTable: v.routingTable,
      checkGateway: ep.checkGateway,
      comment: v.comment,
      disabled: false,
      country: ep.country || inferCountry(ep.gateway) || "",
    })
    if (sheetMode === "create") {
      const base = `new-${Date.now()}-${Math.random().toString(36).slice(2, 6)}`
      const expanded = v.endpoints.map((ep, i) => toRow(ep, `${base}-${i}`))
      setRows(prev => [...prev, ...expanded])
    } else if (editingGroupKey) {
      setRows(prev => {
        const kept = prev.filter(r => groupKeyOf(r) !== editingGroupKey)
        const base = `edit-${Date.now()}-${Math.random().toString(36).slice(2, 6)}`
        const expanded = v.endpoints.map((ep, i) => toRow(ep, `${base}-${i}`))
        return [...kept, ...expanded]
      })
    }
    setSheetOpen(false)
  }

  const currentServer = servers.find(s => s.id === selectedServerId)
  const filteredRows = useMemo(() => {
    const q = search.trim().toLowerCase()
    if (!q) return rows
    return rows.filter(r =>
      r.dstAddress.toLowerCase().includes(q) ||
      r.gateway.toLowerCase().includes(q) ||
      r.routingTable.toLowerCase().includes(q) ||
      r.comment.toLowerCase().includes(q),
    )
  }, [rows, search])
  const groupedRoutes = useMemo(() => {
    const map = new Map<string, RouteGroup>()
    for (const r of filteredRows) {
      const key = groupKeyOf(r)
      const ex = map.get(key)
      if (ex) {
        ex.endpoints.push(r)
        if (!ex.comment && r.comment) ex.comment = r.comment
      }
      else map.set(key, {
        key,
        dstAddress: r.dstAddress,
        routingTable: r.routingTable,
        comment: r.comment,
        endpoints: [r],
      })
    }
    return [...map.values()]
  }, [filteredRows])

  return (
    <div className="flex h-full flex-col">
      <PageHeader
        crumbs={[{ label: "Управление" }, { label: "Рекурсивные маршруты" }]}
        actions={
          <>
            <Button variant="outline" size="sm" onClick={syncFromRouter} disabled={!isLive || busy !== null}>
              {busy === "from" ? "Синхронизация..." : "Router => DB"}
            </Button>
            <Button variant="outline" size="sm" onClick={syncToRouter} disabled={!isLive || busy !== null}>
              {busy === "to" ? "Применение..." : "DB => Router"}
            </Button>
            <Button variant="outline" size="sm" onClick={saveToDb} disabled={!isLive || busy !== null}>
              <SaveIcon className="size-4" />Сохранить в БД
            </Button>
            <Button size="sm" onClick={openCreate} disabled={!isLive || busy !== null}>
              <PlusIcon className="size-4" />Добавить
            </Button>
          </>
        }
      />

      <div className="border-b bg-muted/20 px-6 py-3 flex items-center gap-3 flex-wrap">
        <div className="flex items-center gap-1.5 text-xs">
          <span className="text-muted-foreground">Всего маршрутов</span>
          <span className="font-semibold tabular-nums">{rows.length}</span>
        </div>
        <div className="w-px h-4 bg-border mx-1 shrink-0" />
        {servers.map((s) => {
          const count  = s.id === selectedServerId ? rows.length : 0
          const active = selectedServerId === s.id
          return (
            <button key={s.id}
              onClick={() => setSelectedServerId(s.id)}
              className={cn(
                "inline-flex items-center gap-1.5 rounded-full border px-2.5 py-1 text-[11px] font-medium transition-all",
                active
                  ? "bg-foreground text-background border-foreground"
                  : "border-border text-muted-foreground hover:text-foreground hover:border-foreground/40",
                !s.enabled && !active && "opacity-40",
              )}>
              <StatusDot status={s.status} />
              <Flag code={s.country} size={12} />
              <span className="font-mono">{s.name}</span>
              <TypeChip type={s.type} />
              <span className={cn(
                "tabular-nums font-semibold",
                active ? "" : count > 0 ? "text-foreground" : "opacity-40",
              )}>{count}</span>
            </button>
          )
        })}
      </div>

      <div className="px-6 py-3 flex items-center gap-3 border-b flex-wrap shrink-0">
        <div className="relative min-w-[200px] max-w-xs flex-1">
          <SearchIcon className="absolute left-2.5 top-1/2 -translate-y-1/2 size-3.5 text-muted-foreground pointer-events-none" />
          <Input className="pl-8 h-8 text-sm" placeholder="Dst, gateway, table, comment…"
            value={search} onChange={(e) => setSearch(e.target.value)} />
          {search && (
            <button onClick={() => setSearch("")}
              className="absolute right-2 top-1/2 -translate-y-1/2 text-muted-foreground hover:text-foreground">
              <XIcon className="size-3.5" />
            </button>
          )}
        </div>
        <p className="text-xs text-muted-foreground ml-auto">
          {filteredRows.length !== rows.length ? `${filteredRows.length} из ${rows.length} маршрутов` : `${rows.length} маршрутов`}
        </p>
        {opError && (
          <div className="w-full text-xs text-destructive bg-destructive/10 border border-destructive/20 rounded-md px-3 py-2">
            {opError}
          </div>
        )}
      </div>

      <div className="flex-1 overflow-y-auto p-6">
        {!isLive ? (
          <Card className="p-6 text-sm text-muted-foreground">
            Раздел работает в режиме &quot;Живые данные&quot;. Переключи источник данных в настройках.
          </Card>
        ) : (
          <Card className="overflow-hidden py-0 gap-0">
            {currentServer && (
              <div className="flex items-center gap-2.5 px-4 py-3 border-b bg-muted/10">
                <StatusDot status={currentServer.status} pulse={currentServer.status === "online"} />
                <Flag code={currentServer.country} size={16} />
                <span className="font-mono text-sm font-semibold">{currentServer.name}</span>
                <TypeChip type={currentServer.type} />
                <code className="text-[11px] font-mono text-muted-foreground">{currentServer.host}</code>
                <span className="text-xs text-muted-foreground">{currentServer.asn}</span>
                {currentServer.latency !== null && (
                  <span className={cn(
                    "text-xs font-mono",
                    currentServer.latency > 60 ? "text-amber-600 dark:text-amber-400" : "text-muted-foreground",
                  )}>{currentServer.latency}мс</span>
                )}
                <div className="ml-auto flex items-center gap-1.5 text-xs text-muted-foreground">
                  <span>{rows.length} маршрутов</span>
                </div>
              </div>
            )}

            <div className="overflow-x-auto">
              <table className="w-full text-sm">
                <thead>
                  <tr className="border-b border-border text-xs text-muted-foreground">
                    <th className="text-left font-medium px-5 py-3">Route / Comment</th>
                    <th className="text-left font-medium px-4 py-3">Gateways</th>
                    <th className="text-left font-medium px-4 py-3">EP</th>
                    <th className="text-left font-medium px-4 py-3">Priority</th>
                    <th className="text-left font-medium px-4 py-3">Table</th>
                    <th className="w-10 px-3 py-3" />
                  </tr>
                </thead>
                <tbody className="divide-y divide-border">
                  {groupedRoutes.map((g) => (
                    <RouteGroupRows
                      key={g.key}
                      group={g}
                      expanded={expandedGroupKey === g.key}
                      onToggle={() => setExpandedGroupKey(prev => prev === g.key ? null : g.key)}
                      onEdit={() => openEdit(g)}
                      onDelete={() => setRows(prev => prev.filter(r => groupKeyOf(r) !== g.key))}
                    />
                  ))}
                </tbody>
              </table>
              {groupedRoutes.length === 0 && (
                <div className="p-8 text-center text-sm text-muted-foreground">
                  Нет маршрутов в БД для этого сервера. Нажми &quot;Router =&gt; DB&quot; для загрузки.
                </div>
              )}
            </div>
            <button onClick={openCreate}
              className="w-full flex items-center gap-2 px-4 py-2 text-xs text-muted-foreground hover:text-foreground hover:bg-muted/20 transition-colors border-t">
              <PlusIcon className="size-3.5" />
              Добавить маршрут
            </button>
          </Card>
        )}
      </div>

      <RouteSheet
        open={sheetOpen}
        mode={sheetMode}
        initial={sheetInitial}
        onSave={handleSaveSheet}
        onClose={() => setSheetOpen(false)}
        gateways={gatewayOptions}
      />
    </div>
  )
}
