"use client"

import { useCallback, useEffect, useMemo, useState } from "react"
import Link from "next/link"
import { PageHeader } from "@/components/page-header"
import { FormToggle } from "@/components/form-kit"
import { FileImportDialog } from "@/components/file-import-dialog"
import { OpsPanel } from "@/components/ops-panel"
import { Frame, FramePanel } from "@/components/reui/frame"
import { Button, buttonVariants } from "@/components/ui/button"
import { Input } from "@/components/ui/input"
import { useDataSource } from "@/lib/data-source"
import { useEvoBGP, type EvoBgpSavePayload, type EvoBgpTestDraft } from "@/lib/evobgp-context"
import { DEFAULT_ROUTE_AI_OPTIMIZER_SETTINGS } from "@/lib/route-optimizer-data"
import {
  AlertDialog,
  AlertDialogAction,
  AlertDialogCancel,
  AlertDialogContent,
  AlertDialogDescription,
  AlertDialogFooter,
  AlertDialogHeader,
  AlertDialogMedia,
  AlertDialogTitle,
} from "@/components/ui/alert-dialog"
import {
  SaveIcon, TrashIcon, PlusIcon, CheckIcon, CopyIcon,
  AlertCircleIcon, EyeIcon, EyeOffIcon,
  RefreshCwIcon, LoaderCircleIcon,
  DownloadIcon, UploadIcon, XIcon,
} from "lucide-react"
import { cn } from "@/lib/utils"
import { requestJson } from "@/shared/api/http-client"
import { downloadSystemDatabaseBackup, restoreSystemDatabaseBackup } from "@/shared/api/system-database"
import { purgeTrafficFlowData } from "@/shared/api/traffic-flow"
import { formatFlowPurgeResult, NetflowPurgeConfirm } from "@/components/traffic/netflow-purge-dialog"
import { toast } from "sonner"

interface ApiKey { id: string; name: string; prefix: string; created: string; last: string; scopes: string[] }


const INIT_API_KEYS: ApiKey[] = [
  { id: "k1", name: "Monitoring Script",  prefix: "rl_live_mU9k2…", created: "12 дн назад", last: "2м назад", scopes: ["servers:read", "probes:read"] },
  { id: "k2", name: "CI/CD deploy hook",  prefix: "rl_live_vB3p8…", created: "1 мес назад", last: "вчера",    scopes: ["filters:write", "domains:write"] },
  { id: "k3", name: "Grafana datasource", prefix: "rl_live_xK7r1…", created: "2 мес назад", last: "сейчас",   scopes: ["traffic:read", "servers:read"] },
]

const SECTIONS_NAV = ["Общие", "EvoBGP", "Уведомления", "API-ключи", "Безопасность"] as const
type NavSection = typeof SECTIONS_NAV[number]

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

function SettingRow({ label, description, children }: { label: string; description?: string; children: React.ReactNode }) {
  return (
    <div className="flex items-center justify-between gap-4 py-3.5">
      <div className="min-w-0">
        <p className="text-sm font-medium">{label}</p>
        {description && <p className="text-xs text-muted-foreground mt-0.5">{description}</p>}
      </div>
      <div className="shrink-0">{children}</div>
    </div>
  )
}

function DatabaseRestoreConfirm({
  open,
  filename,
  busy,
  onConfirm,
  onCancel,
}: {
  open: boolean
  filename: string
  busy?: boolean
  onConfirm: () => void
  onCancel: () => void
}) {
  return (
    <AlertDialog open={open} onOpenChange={(v) => { if (!v && !busy) onCancel() }}>
      <AlertDialogContent size="default">
        <AlertDialogHeader>
          <AlertDialogMedia className="bg-destructive/10 text-destructive">
            <AlertCircleIcon />
          </AlertDialogMedia>
          <AlertDialogTitle>Восстановить базу приложения?</AlertDialogTitle>
          <AlertDialogDescription>
            Файл: {filename}. Текущие данные SQLite на бекенде будут полностью заменены.
            Рекомендуется сначала скачать актуальный бэкап.
          </AlertDialogDescription>
        </AlertDialogHeader>
        <AlertDialogFooter>
          <AlertDialogCancel disabled={busy} onClick={onCancel}>Отмена</AlertDialogCancel>
          <AlertDialogAction
            variant="destructive"
            disabled={busy}
            onClick={onConfirm}
          >
            {busy ? <LoaderCircleIcon className="size-4 animate-spin" /> : null}
            {busy ? "Восстановление…" : "Восстановить"}
          </AlertDialogAction>
        </AlertDialogFooter>
      </AlertDialogContent>
    </AlertDialog>
  )
}

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

export default function SettingsPage() {
  const [section,  setSection]  = useState<NavSection>("Общие")
  const [saved,    setSaved]    = useState(false)
  const [copied,   setCopied]   = useState<string | null>(null)

  // data source
  const { mode, setMode, backendUrl, setBackendUrl, backendUrlLocked, mockModeAvailable, backendStatus, checkBackend } = useDataSource()
  const evo = useEvoBGP()
  const [evoBaseDraft, setEvoBaseDraft] = useState("")
  const [evoEnabledDraft, setEvoEnabledDraft] = useState(false)
  const [evoKeyDraft, setEvoKeyDraft] = useState("")
  const [evoSaveBusy, setEvoSaveBusy] = useState(false)
  const [evoSaveErr, setEvoSaveErr] = useState<string | null>(null)
  const [urlDraft, setUrlDraft] = useState(backendUrl)
  const apiFetch = useMemo(() => makeApiFetch(backendUrl), [backendUrl])

  useEffect(() => {
    setUrlDraft(backendUrl)
  }, [backendUrl])

  const commitBackendUrl = useCallback(() => {
    const normalized = urlDraft.trim().replace(/\/$/, "")
    setUrlDraft(normalized)
    setBackendUrl(normalized)
    void checkBackend()
  }, [urlDraft, setBackendUrl, checkBackend])
  const [evoTestResult, setEvoTestResult] = useState<{ ok: boolean; message: string } | null>(null)
  const [evoBusy, setEvoBusy] = useState<"test" | "refresh" | null>(null)
  const [showEvoKey, setShowEvoKey] = useState(false)

  // general
  const [lang,         setLang]         = useState("ru")
  const [theme,        setTheme]        = useState("system")
  const [timezone,     setTimezone]     = useState("Europe/Moscow")
  const [refreshSec,   setRefreshSec]   = useState("30")
  const [probeTimeout, setProbeTimeout] = useState("5")
  const [dbBackupBusy, setDbBackupBusy] = useState(false)
  const [dbRestoreBusy, setDbRestoreBusy] = useState(false)
  const [dbRestoreFile, setDbRestoreFile] = useState<File | null>(null)
  const [dbRestoreDialogOpen, setDbRestoreDialogOpen] = useState(false)
  const [dbPurgeOpen, setDbPurgeOpen] = useState(false)
  const [dbPurgeBusy, setDbPurgeBusy] = useState(false)

  // notifications
  const [notifEmail,   setNotifEmail]   = useState(true)
  const [notifSlack,   setNotifSlack]   = useState(false)
  const [notifWh,      setNotifWh]      = useState(true)
  const [notifDegr,    setNotifDegr]    = useState(true)
  const [notifOffline, setNotifOffline] = useState(true)
  const [notifBgp,     setNotifBgp]     = useState(true)
  const [notifBackup,  setNotifBackup]  = useState(false)

  // api keys
  const [apiKeys, setApiKeys] = useState<ApiKey[]>(INIT_API_KEYS)

  // security
  const [mfa,      setMfa]      = useState(true)
  const [sessMin,  setSessMin]  = useState("480")
  const [ipAllow,  setIpAllow]  = useState("10.0.0.0/8\n192.168.0.0/16")
  const [auditLog, setAuditLog] = useState(true)

  const handleCopy = (text: string) => {
    navigator.clipboard.writeText(text).catch(() => {})
    setCopied(text); setTimeout(() => setCopied(null), 1500)
    toast.success("Скопировано в буфер обмена")
  }

  const markSaved = useCallback(() => {
    setSaved(true)
    toast.success("Настройки сохранены")
    setTimeout(() => setSaved(false), 2000)
  }, [])

  useEffect(() => {
    if (section !== "EvoBGP") return
    if (mode === "live" && backendStatus === true) queueMicrotask(() => { void evo.loadSettings() })
  }, [section, mode, backendStatus, evo.loadSettings])

  useEffect(() => {
    if (section !== "EvoBGP" || !evo.settingsLoaded) return
    setEvoBaseDraft(evo.baseUrl)
    setEvoEnabledDraft(evo.enabled)
    setEvoKeyDraft("")
    setEvoSaveErr(null)
  }, [section, evo.settingsLoaded, evo.baseUrl, evo.enabled])

  const handleSave = useCallback(async () => {
    if (section === "EvoBGP") {
      if (mode !== "live" || backendStatus !== true) {
        markSaved()
        return
      }
      setEvoSaveBusy(true)
      setEvoSaveErr(null)
      try {
        const patch: EvoBgpSavePayload = {
          baseUrl: evoBaseDraft.trim(),
          enabled: evoEnabledDraft,
        }
        if (evoKeyDraft.trim()) patch.apiKey = evoKeyDraft.trim()
        await evo.saveSettings(patch)
        setEvoKeyDraft("")
        markSaved()
        toast.success("Настройки EvoBGP сохранены")
      } catch (e) {
        const msg = e instanceof Error ? e.message : "Ошибка сохранения"
        setEvoSaveErr(msg)
        toast.error(msg)
      } finally {
        setEvoSaveBusy(false)
      }
      return
    }
    markSaved()
  }, [
    section,
    mode,
    backendStatus,
    evoBaseDraft,
    evoEnabledDraft,
    evoKeyDraft,
    markSaved,
    evo.saveSettings,
  ])

  const systemDbAvailable = mode === "live" && backendStatus === true

  const handleSystemDatabaseBackup = useCallback(async () => {
    if (!systemDbAvailable) return
    setDbBackupBusy(true)
    try {
      const { blob, filename } = await downloadSystemDatabaseBackup(backendUrl)
      const url = URL.createObjectURL(blob)
      const a = document.createElement("a")
      a.href = url
      a.download = filename
      a.click()
      URL.revokeObjectURL(url)
      toast.success("Бэкап базы приложения скачан")
    } catch (e) {
      toast.error(e instanceof Error ? e.message : "Не удалось создать бэкап")
    } finally {
      setDbBackupBusy(false)
    }
  }, [backendUrl, systemDbAvailable])

  const handleSystemDatabaseRestoreConfirm = useCallback(async () => {
    if (!dbRestoreFile || !systemDbAvailable) return
    setDbRestoreBusy(true)
    try {
      await restoreSystemDatabaseBackup(backendUrl, dbRestoreFile)
      toast.success("База приложения восстановлена")
      setDbRestoreFile(null)
    } catch (e) {
      toast.error(e instanceof Error ? e.message : "Не удалось восстановить базу")
    } finally {
      setDbRestoreBusy(false)
    }
  }, [backendUrl, dbRestoreFile, systemDbAvailable])

  const handleNetflowPurgeConfirm = useCallback(async () => {
    if (!systemDbAvailable) return
    setDbPurgeBusy(true)
    try {
      const result = await purgeTrafficFlowData(backendUrl)
      toast.success(formatFlowPurgeResult(result))
      setDbPurgeOpen(false)
    } catch (e) {
      toast.error(e instanceof Error ? e.message : "Не удалось сбросить NetFlow")
    } finally {
      setDbPurgeBusy(false)
    }
  }, [backendUrl, systemDbAvailable])

  const renderContent = () => {
    const ra = DEFAULT_ROUTE_AI_OPTIMIZER_SETTINGS

    // ── Общие ──
    if (section === "Общие") return (
      <div className="flex flex-col gap-4">

      {/* ── Источник данных ── */}
      <OpsPanel
        title="Источник данных"
        description={
            mockModeAvailable
              ? "Переключите между живыми данными от бекенда и суррогатными моками из lib/data.ts"
              : "В продакшен-образе доступны только живые данные от бекенда"
        }
        contentClassName="divide-y px-5"
      >

          {/* mode toggle */}
          {mockModeAvailable ? (
            <SettingRow label="Режим данных" description="Моковые — статичные тестовые данные; Живые — RouterOS REST API через бекенд">
              <div className="flex rounded-md border border-input overflow-hidden h-8">
                {([["mock", "Моковые"], ["live", "Живые"]] as const).map(([v, l], i) => (
                  <button key={v} onClick={() => setMode(v)}
                    className={cn(
                      "px-3 text-xs transition-colors border-input",
                      i === 0 && "border-r",
                      mode === v
                        ? "bg-primary text-primary-foreground"
                        : "text-muted-foreground hover:bg-muted",
                    )}>
                    {l}
                  </button>
                ))}
              </div>
            </SettingRow>
          ) : (
            <SettingRow label="Режим данных" description="В Docker-образе приложение работает только с живыми данными бекенда">
              <span className="inline-flex h-8 items-center rounded-md border border-input bg-primary px-3 text-xs text-primary-foreground">
                Живые
              </span>
            </SettingRow>
          )}

          {/* backend URL — visible in both modes so user can configure before switching */}
          <SettingRow
            label="URL бекенда"
            description={
              backendUrlLocked
                ? "В Docker-образе URL API задаётся через прокси фронтенда на backend; поле только для просмотра."
                : "Адрес Node.js/Fastify сервера (запускается командой npm run dev в /backend)"
            }
          >
            <div className="flex items-center gap-2">
              <Input
                className="w-56 h-8 text-sm font-mono"
                value={urlDraft}
                onChange={e => setUrlDraft(e.target.value)}
                onBlur={commitBackendUrl}
                onKeyDown={e => {
                  if (e.key === "Enter") {
                    e.preventDefault()
                    commitBackendUrl()
                  }
                }}
                readOnly={backendUrlLocked}
                placeholder="http://localhost:8000"
              />
              <Button variant="outline" size="sm" className="h-8 px-2.5 shrink-0"
                onClick={commitBackendUrl}
                title="Проверить соединение">
                <RefreshCwIcon className="size-3.5" />
              </Button>
            </div>
          </SettingRow>

          {/* connection status */}
          <SettingRow label="Статус бекенда">
            <div className="flex items-center gap-2">
              {backendStatus === undefined && (
                <span className="flex items-center gap-1.5 text-xs text-muted-foreground">
                  <span className="size-2 rounded-full bg-muted-foreground/40 inline-block" />
                  Не проверялось
                </span>
              )}
              {backendStatus === true && (
                <span className="flex items-center gap-1.5 text-xs text-emerald-600 dark:text-emerald-400">
                  <CheckIcon className="size-3.5" />Доступен
                </span>
              )}
              {backendStatus === false && (
                <span className="flex items-center gap-1.5 text-xs text-destructive">
                  <XIcon className="size-3.5" />Недоступен
                </span>
              )}
              <Button variant="ghost" size="sm" className="h-7 px-2 text-xs text-muted-foreground"
                onClick={checkBackend}>
                Проверить
              </Button>
            </div>
          </SettingRow>

          {/* hint when live but backend unreachable */}
          {mode === "live" && backendStatus === false && (
            <div className="flex items-start gap-2 py-3 text-xs text-amber-600 dark:text-amber-400">
              <AlertCircleIcon className="size-3.5 mt-0.5 shrink-0" />
              <span>Бекенд недоступен — данные будут отображаться из кеша или моков. Запустите бекенд: <code className="font-mono bg-muted px-1 rounded">cd backend && npm run dev</code></span>
            </div>
          )}
      </OpsPanel>

      {/* ── Основные настройки ── */}
      <OpsPanel title="Основные настройки" contentClassName="divide-y px-5">
          <SettingRow label="Язык интерфейса">
            <select className="text-sm bg-background text-foreground border border-input rounded-md px-2 py-1 h-8 focus:outline-none focus:ring-1 focus:ring-ring"
              value={lang} onChange={e => setLang(e.target.value)}>
              <option value="ru">Русский</option>
              <option value="en">English</option>
            </select>
          </SettingRow>
          <SettingRow label="Тема оформления">
            <div className="flex rounded-md border border-input overflow-hidden h-8">
              {[["light","Светлая"],["dark","Тёмная"],["system","Авто"]].map(([v, l]) => (
                <button key={v} onClick={() => setTheme(v)}
                  className={cn("px-3 text-xs transition-colors border-r last:border-r-0 border-input",
                    theme === v ? "bg-primary text-primary-foreground" : "text-muted-foreground hover:bg-muted")}>
                  {l}
                </button>
              ))}
            </div>
          </SettingRow>
          <SettingRow label="Часовой пояс">
            <select className="text-sm bg-background text-foreground border border-input rounded-md px-2 py-1 h-8 focus:outline-none focus:ring-1 focus:ring-ring"
              value={timezone} onChange={e => setTimezone(e.target.value)}>
              {["Europe/Moscow","Europe/Berlin","Europe/Amsterdam","Asia/Singapore","UTC"].map(z => (
                <option key={z} value={z}>{z}</option>
              ))}
            </select>
          </SettingRow>
          <SettingRow label="Интервал автообновления (сек)"
            description="Как часто страницы обновляют данные">
            <Input className="w-20 h-8 text-sm" value={refreshSec} onChange={e => setRefreshSec(e.target.value)} />
          </SettingRow>
          <SettingRow label="Таймаут диагностики (сек)"
            description="Максимальное время ожидания при probe-тестах">
            <Input className="w-20 h-8 text-sm" value={probeTimeout} onChange={e => setProbeTimeout(e.target.value)} />
          </SettingRow>
      </OpsPanel>

      <OpsPanel
        title="База данных приложения"
        description="Резервная копия SQLite бекенда и сброс таблиц NetFlow. На время операции планировщик и коллектор IPFIX приостанавливаются. Preview: https://reui.io/preview/base/settings-16"
        contentClassName="divide-y px-5"
      >
          {!systemDbAvailable && (
            <div className="flex items-start gap-2 py-3 text-xs text-amber-600 dark:text-amber-400">
              <AlertCircleIcon className="size-3.5 mt-0.5 shrink-0" />
              <span>Доступно только в live-режиме при доступном бекенде.</span>
            </div>
          )}
          <SettingRow
            label="Скачать бэкап"
            description="Консистентная копия файла mikrotik.db"
          >
            <Button
              variant="outline"
              size="sm"
              className="h-8"
              disabled={!systemDbAvailable || dbBackupBusy || dbRestoreBusy || dbPurgeBusy}
              onClick={() => { void handleSystemDatabaseBackup() }}
            >
              {dbBackupBusy ? <LoaderCircleIcon className="size-4 animate-spin" /> : <DownloadIcon className="size-4" />}
              {dbBackupBusy ? "Подготовка…" : "Скачать"}
            </Button>
          </SettingRow>
          <SettingRow
            label="Восстановить из файла"
            description="Полностью заменяет текущую базу SQLite"
          >
            <div className="flex flex-col items-end gap-2">
              <Button
                variant="outline"
                size="sm"
                className="h-8"
                disabled={!systemDbAvailable || dbBackupBusy || dbRestoreBusy || dbPurgeBusy}
                onClick={() => setDbRestoreDialogOpen(true)}
              >
                <UploadIcon className="size-4" />
                Выбрать файл
              </Button>
              {dbRestoreFile && (
                <p className="text-xs text-muted-foreground max-w-[220px] text-right break-all">{dbRestoreFile.name}</p>
              )}
            </div>
          </SettingRow>
          <SettingRow
            label="Сбросить данные NetFlow"
            description="Удалит сессии и агрегаты из SQLite, затем VACUUM. Ключи WG и пиры не трогает"
          >
            <Button
              variant="destructive"
              size="sm"
              className="h-8"
              disabled={!systemDbAvailable || dbBackupBusy || dbRestoreBusy || dbPurgeBusy}
              onClick={() => setDbPurgeOpen(true)}
            >
              {dbPurgeBusy ? <LoaderCircleIcon className="size-4 animate-spin" /> : <TrashIcon className="size-4" />}
              {dbPurgeBusy ? "Сброс…" : "Сбросить"}
            </Button>
          </SettingRow>
      </OpsPanel>

      <OpsPanel
        id="route-ai"
        className="scroll-mt-4"
        title="Route AI"
        description="Параметры оптимизации маршрутов по умолчанию (тот же набор, что на странице «Оптимизатор маршрутов»). Пороги и веса настраиваются в инструменте, не через отдельный API."
        contentClassName="flex flex-col gap-4 px-5 pb-5"
      >
          <dl className="grid grid-cols-1 sm:grid-cols-2 gap-x-8 gap-y-2 text-xs">
            <div className="flex justify-between gap-4 border-b border-border/50 py-1.5">
              <dt className="text-muted-foreground">Мин. выигрыш (переключение)</dt>
              <dd className="font-mono tabular-nums">{ra.switchThreshold}%</dd>
            </div>
            <div className="flex justify-between gap-4 border-b border-border/50 py-1.5">
              <dt className="text-muted-foreground">Гистерезис</dt>
              <dd className="font-mono tabular-nums">{ra.hysteresisThreshold}%</dd>
            </div>
            <div className="flex justify-between gap-4 border-b border-border/50 py-1.5">
              <dt className="text-muted-foreground">Вес задержки (ping)</dt>
              <dd className="font-mono tabular-nums">{ra.pingWeight}%</dd>
            </div>
            <div className="flex justify-between gap-4 border-b border-border/50 py-1.5">
              <dt className="text-muted-foreground">Интервал зондирования</dt>
              <dd className="font-mono tabular-nums">{ra.probeIntervalMin} мин</dd>
            </div>
            <div className="flex justify-between gap-4 border-b border-border/50 py-1.5 sm:col-span-2">
              <dt className="text-muted-foreground">Автоприменение</dt>
              <dd>{ra.autoApply ? `да, каждые ${ra.autoApplyIntervalMin} мин` : "нет"}</dd>
            </div>
          </dl>
          <Link
            href="/route-optimizer"
            className={cn(buttonVariants({ variant: "outline", size: "sm" }), "h-8")}
          >
            Открыть оптимизатор маршрутов
          </Link>
      </OpsPanel>

      </div>
    )

    // ── EvoBGP ──
    if (section === "EvoBGP") return (
      <div className="flex flex-col gap-4">
        {(mode !== "live" || backendStatus !== true) && (
          <Frame dense className="w-full">
            <FramePanel className="px-4 py-4">
              <p className="text-sm font-medium">Интеграция доступна в live-режиме</p>
              <p className="text-xs text-muted-foreground mt-1">
                Включите «Живые» данные и убедитесь, что локальный бекенд доступен (раздел «Общие»).
              </p>
            </FramePanel>
          </Frame>
        )}

        <OpsPanel
          title="EvoBGP API"
          description={
            <>
              Control plane EvoBGP: Bearer-ключ и роль viewer+ — см.{" "}
              <a
                className="underline underline-offset-2"
                href="https://git.shx.one/denozord/EvoBGP/src/branch/main/docs/access.md"
                target="_blank"
                rel="noreferrer"
              >
                access.md
              </a>
              . URL и ключ хранятся в SQLite на сервере бекенда (
              <code className="font-mono bg-muted px-1 rounded">evobgp_settings</code>
              ). Каталог —{" "}
              <code className="font-mono bg-muted px-1 rounded">GET /v1/router-lists/catalog</code> через прокси.
            </>
          }
          contentClassName="divide-y px-5 flex flex-col gap-4 pb-5"
        >
            <SettingRow
              label="Базовый URL API"
              description="Например http://control.example:8080 — без суффикса /v1"
            >
              <Input
                className="w-full max-w-md h-8 text-sm font-mono"
                value={evoBaseDraft}
                onChange={(e) => setEvoBaseDraft(e.target.value)}
                placeholder="http://localhost:8080"
                disabled={mode !== "live" || backendStatus !== true || evoSaveBusy}
              />
            </SettingRow>
            <SettingRow
              label="API-ключ"
              description={
                evo.secretConfigured
                  ? "В БД уже есть ключ — введите новый только для замены"
                  : "Сохраняется общей кнопкой «Сохранить» в шапке страницы"
              }
            >
              <div className="flex items-center gap-2">
                <Input
                  className="w-full max-w-md h-8 text-sm font-mono"
                  type={showEvoKey ? "text" : "password"}
                  autoComplete="off"
                  value={evoKeyDraft}
                  onChange={(e) => setEvoKeyDraft(e.target.value)}
                  placeholder={evo.secretConfigured ? "Оставьте пустым, чтобы не менять" : "Bearer-токен"}
                  disabled={mode !== "live" || backendStatus !== true || evoSaveBusy}
                />
                <Button
                  type="button"
                  variant="outline"
                  size="sm"
                  className="h-8 shrink-0"
                  onClick={() => setShowEvoKey((v) => !v)}
                >
                  {showEvoKey ? <EyeOffIcon className="size-3.5" /> : <EyeIcon className="size-3.5" />}
                </Button>
              </div>
            </SettingRow>
            <SettingRow
              label="Подставлять данные EvoBGP"
              description="На страницах Домены, IP-диапазоны, ASN и Communities вместо моков из lib/data"
            >
              <FormToggle
                checked={evoEnabledDraft}
                onChange={(v) => setEvoEnabledDraft(v)}
              />
            </SettingRow>
            <div className="flex flex-wrap items-center gap-2 pt-2">
              <Button
                type="button"
                variant="outline"
                size="sm"
                className="h-8 text-destructive border-destructive/40 hover:bg-destructive/10"
                disabled={mode !== "live" || backendStatus !== true || evoSaveBusy || !evo.secretConfigured}
                onClick={async () => {
                  setEvoSaveBusy(true)
                  setEvoSaveErr(null)
                  try {
                    await evo.saveSettings({ apiKey: null })
                    setEvoKeyDraft("")
                    markSaved()
                  } catch (e) {
                    setEvoSaveErr(e instanceof Error ? e.message : "Ошибка")
                  } finally {
                    setEvoSaveBusy(false)
                  }
                }}
              >
                Удалить ключ из БД
              </Button>
            </div>
            {evoSaveErr && <p className="text-xs text-destructive">{evoSaveErr}</p>}
            <p className="text-[11px] text-muted-foreground">
              «Проверить ключ» использует поля формы; незаполненное поле подставляется из БД. «Обновить каталог» — только по сохранённым в БД настройкам.
            </p>
            <div className="flex flex-wrap items-center gap-2 pt-1">
              <Button
                type="button"
                variant="outline"
                size="sm"
                className="h-8"
                disabled={
                  mode !== "live" ||
                  backendStatus !== true ||
                  evoBusy !== null ||
                  evoSaveBusy ||
                  !(
                    (evoBaseDraft.trim() || evo.baseUrl.trim()) &&
                    (evoKeyDraft.trim() || evo.secretConfigured)
                  )
                }
                onClick={async () => {
                  setEvoBusy("test")
                  setEvoTestResult(null)
                  const b = evoBaseDraft.trim()
                  const k = evoKeyDraft.trim()
                  let draft: EvoBgpTestDraft | undefined
                  if (b || k) {
                    draft = {}
                    if (b) draft.baseUrl = b
                    if (k) draft.apiKey = k
                  }
                  const r = await evo.testConnection(draft)
                  setEvoTestResult({ ok: r.ok, message: r.message })
                  setEvoBusy(null)
                }}
              >
                {evoBusy === "test" ? <RefreshCwIcon className="size-3.5 animate-spin" /> : null}
                Проверить ключ
              </Button>
              <Button
                type="button"
                variant="outline"
                size="sm"
                className="h-8"
                disabled={mode !== "live" || backendStatus !== true || evoBusy !== null || evoSaveBusy || !evo.enabled}
                onClick={async () => {
                  setEvoBusy("refresh")
                  await evo.refresh()
                  setEvoBusy(null)
                }}
              >
                {evoBusy === "refresh" || evo.loading ? <RefreshCwIcon className="size-3.5 animate-spin" /> : null}
                Обновить каталог
              </Button>
              {evo.snapshot?.fetchedAt && (
                <span className="text-xs text-muted-foreground">
                  Загружено: {new Date(evo.snapshot.fetchedAt).toLocaleString("ru-RU")}
                </span>
              )}
            </div>
            {evoTestResult && (
              <p className={cn(
                "text-xs",
                evoTestResult.ok ? "text-emerald-600 dark:text-emerald-400" : "text-destructive",
              )}>
                {evoTestResult.message}
              </p>
            )}
            {evo.error && (
              <p className="text-xs text-destructive">{evo.error}</p>
            )}
        </OpsPanel>
      </div>
    )

    // ── Уведомления ──
    if (section === "Уведомления") return (
      <div className="flex flex-col gap-4">
        <OpsPanel title="Каналы уведомлений" contentClassName="divide-y px-5">
            <SettingRow label="Email" description="Отправка уведомлений на admin@routerlists.io">
              <FormToggle checked={notifEmail} onChange={setNotifEmail} />
            </SettingRow>
            {notifEmail && <div className="py-3"><Input className="text-sm h-8" defaultValue="admin@routerlists.io" /></div>}
            <SettingRow label="Slack" description="Webhook-интеграция с каналом #alerts">
              <FormToggle checked={notifSlack} onChange={setNotifSlack} />
            </SettingRow>
            {notifSlack && <div className="py-3"><Input className="text-sm h-8 font-mono" placeholder="https://hooks.slack.com/…" /></div>}
            <SettingRow label="Webhook" description="POST-запрос на произвольный endpoint">
              <FormToggle checked={notifWh} onChange={setNotifWh} />
            </SettingRow>
            {notifWh && <div className="py-3"><Input className="text-sm h-8 font-mono" defaultValue="https://hooks.example.com/routerlists" /></div>}
      </OpsPanel>
        <OpsPanel title="Триггеры" contentClassName="divide-y px-5">
            <SettingRow label="Деградация узла" description="Потери пакетов > 5% или RTT > 100мс">
              <FormToggle checked={notifDegr} onChange={setNotifDegr} />
            </SettingRow>
            <SettingRow label="Узел ушёл offline">
              <FormToggle checked={notifOffline} onChange={setNotifOffline} />
            </SettingRow>
            <SettingRow label="Падение BGP-сессии">
              <FormToggle checked={notifBgp} onChange={setNotifBgp} />
            </SettingRow>
            <SettingRow label="Просроченный бэкап" description="Если последний бэкап старше 2 дней">
              <FormToggle checked={notifBackup} onChange={setNotifBackup} />
            </SettingRow>
      </OpsPanel>
      </div>
    )

    // ── API-ключи ──
    if (section === "API-ключи") return (
      <div className="flex flex-col gap-4">
        <div className="flex justify-end">
          <Button size="sm"><PlusIcon className="size-4" />Создать ключ</Button>
        </div>
        <div className="flex flex-col gap-3">
          {apiKeys.map(k => (
            <Frame key={k.id} dense className="w-full">
              <FramePanel className="pt-4 pb-3 px-4">
                <div className="flex items-start justify-between gap-4">
                  <div className="min-w-0">
                    <p className="text-sm font-medium">{k.name}</p>
                    <div className="flex items-center gap-2 mt-1">
                      <code className="text-xs font-mono text-muted-foreground bg-muted px-1.5 py-0.5 rounded">{k.prefix}</code>
                      <button onClick={() => handleCopy(k.prefix)} className="text-muted-foreground/40 hover:text-muted-foreground transition-colors">
                        {copied === k.prefix ? <CheckIcon className="size-3" /> : <CopyIcon className="size-3" />}
                      </button>
                    </div>
                    <div className="flex flex-wrap gap-1 mt-2">
                      {k.scopes.map(s => (
                        <span key={s} className="text-[10px] font-mono bg-muted px-1.5 py-0.5 rounded text-muted-foreground">{s}</span>
                      ))}
                    </div>
                  </div>
                  <div className="text-right shrink-0">
                    <p className="text-xs text-muted-foreground">Создан: {k.created}</p>
                    <p className="text-xs text-muted-foreground mt-0.5">Использован: {k.last}</p>
                    <div className="flex gap-1 justify-end mt-2">
                      <Button size="sm" variant="ghost" className="h-7 px-2 text-xs">Переиздать</Button>
                      <Button size="sm" variant="ghost" className="size-7 p-0 text-destructive hover:text-destructive"
                        onClick={() => setApiKeys(p => p.filter(x => x.id !== k.id))}>
                        <TrashIcon className="size-3.5" />
                      </Button>
                    </div>
                  </div>
                </div>
              </FramePanel>
            </Frame>
          ))}
        </div>
        <OpsPanel
          title="Документация API"
          description="Base URL: https://api.routerlists.io/v1"
          contentClassName="px-4 pb-4"
        >
            <div className="grid grid-cols-2 gap-2 text-xs">
              {[
                ["GET",  "/servers",    "Список серверов"],
                ["GET",  "/traffic",    "Данные трафика"],
                ["GET",  "/filters",    "Фильтры маршрутов"],
                ["POST", "/filters",    "Создать фильтр"],
                ["GET",  "/probes",     "Диагностические пробы"],
                ["POST", "/probes/run", "Запустить тест"],
              ].map(([method, path, desc], i) => (
                <div key={`${method}-${path}-${i}`} className="flex items-baseline gap-2">
                  <span className={cn("font-mono text-[10px] font-bold w-8 shrink-0",
                    method === "GET" ? "text-emerald-500" : "text-sky-500")}>{method}</span>
                  <code className="font-mono text-muted-foreground">{path}</code>
                  <span className="text-muted-foreground/60 text-[10px]">{desc}</span>
                </div>
              ))}
            </div>
        </OpsPanel>
      </div>
    )

    // ── Безопасность ──
    if (section === "Безопасность") return (
      <div className="flex flex-col gap-4">
        <OpsPanel title="Аутентификация" contentClassName="divide-y px-5">
            <SettingRow label="Двухфакторная аутентификация (MFA)"
              description="TOTP / Authenticator app для всех администраторов">
              <FormToggle checked={mfa} onChange={setMfa} />
            </SettingRow>
            <SettingRow label="Тайм-аут сессии (мин)" description="Автоматический выход при бездействии">
              <Input className="w-20 h-8 text-sm" value={sessMin} onChange={e => setSessMin(e.target.value)} />
            </SettingRow>
      </OpsPanel>
        <OpsPanel
          title="Allowlist IP-адресов"
          description="Доступ разрешён только с этих сетей. Одна запись на строку."
          contentClassName="px-4 pb-4"
        >
            <textarea value={ipAllow} onChange={e => setIpAllow(e.target.value)} rows={4}
              className="w-full rounded-md border bg-muted px-3 py-2 text-xs font-mono resize-none outline-none focus:ring-1 focus:ring-ring" />
        </OpsPanel>
        <OpsPanel title="Аудит" contentClassName="divide-y px-5">
            <SettingRow label="Расширенный журнал аудита"
              description="Записывать все изменения конфигурации с указанием пользователя и IP">
              <FormToggle checked={auditLog} onChange={setAuditLog} />
            </SettingRow>
      </OpsPanel>
        <OpsPanel title={<span className="text-destructive">Опасная зона</span>} contentClassName="px-4 pb-4">
            <div className="flex items-center justify-between">
              <div>
                <p className="text-sm font-medium">Сбросить все настройки</p>
                <p className="text-xs text-muted-foreground">Удалит пользовательские параметры и вернёт заводские значения</p>
              </div>
              <Button variant="outline" size="sm" className="border-destructive text-destructive hover:bg-destructive hover:text-destructive-foreground">
                Сбросить
              </Button>
            </div>
        </OpsPanel>
      </div>
    )
  }

  return (
    <div className="flex flex-col h-full">
      <PageHeader
        crumbs={[{ label: "Система" }, { label: "Настройки" }]}
        actions={
          <Button
            size="sm"
            onClick={() => { void handleSave() }}
            disabled={
              section === "EvoBGP" &&
              (evoSaveBusy || mode !== "live" || backendStatus !== true)
            }
          >
            {section === "EvoBGP" && evoSaveBusy ? (
              <LoaderCircleIcon className="size-4 animate-spin" />
            ) : saved ? (
              <CheckIcon className="size-4" />
            ) : (
              <SaveIcon className="size-4" />
            )}
            {section === "EvoBGP" && evoSaveBusy
              ? "Сохранение…"
              : saved
                ? "Сохранено!"
                : "Сохранить"}
          </Button>
        }
      />

      <div className="flex-1 overflow-y-auto p-6">
        <div className="max-w-[960px] mx-auto grid grid-cols-[180px_1fr] gap-6">

          {/* nav */}
          <nav className="flex flex-col gap-1 self-start sticky top-0">
            {SECTIONS_NAV.map(s => (
              <button key={s} onClick={() => setSection(s)}
                className={cn(
                  "text-left px-3 py-2 rounded-md text-sm transition-colors",
                  s === section ? "bg-primary text-primary-foreground font-medium" : "hover:bg-muted text-muted-foreground hover:text-foreground",
                )}>
                {s}
              </button>
            ))}
          </nav>

          {/* content */}
          <div className="min-w-0">{renderContent()}</div>
        </div>
      </div>

      {/* delete confirm */}
      {dbRestoreFile && (
        <DatabaseRestoreConfirm
          open={!!dbRestoreFile}
          filename={dbRestoreFile.name}
          busy={dbRestoreBusy}
          onConfirm={() => { void handleSystemDatabaseRestoreConfirm() }}
          onCancel={() => {
            if (dbRestoreBusy) return
            setDbRestoreFile(null)
          }}
        />
      )}

      <NetflowPurgeConfirm
        open={dbPurgeOpen}
        busy={dbPurgeBusy}
        onConfirm={() => { void handleNetflowPurgeConfirm() }}
        onCancel={() => { if (!dbPurgeBusy) setDbPurgeOpen(false) }}
      />

      <FileImportDialog
        open={dbRestoreDialogOpen}
        onOpenChange={setDbRestoreDialogOpen}
        title="Восстановление базы данных"
        description="Выберите файл SQLite (.db) — текущая база будет полностью заменена"
        accept=".db,.sqlite,.sqlite3,application/octet-stream"
        onImport={async (files) => {
          const file = files[0]
          if (!file) return
          setDbRestoreFile(file)
        }}
      />
    </div>
  )
}

