"use client"

import { useCallback, useEffect, useMemo, useState } from "react"
import { PageHeader } from "@/components/page-header"
import { DataPageToolbar } from "@/components/data-page-toolbar"
import { BackupsDataGrid } from "@/components/data-grids/backups-data-grid"
import { FileImportDialog } from "@/components/file-import-dialog"
import { FormField, FormToggle, SegmentedControl } from "@/components/form-kit"
import { StatusBadge } from "@/components/status-badge"
import type { Backup, Server } from "@/lib/data"
import { KpiStatGrid } from "@/components/reui-kit/kpi-stat-grid"
import { OpsPanel } from "@/components/ops-panel"
import { DataPageCard } from "@/components/data-page-card"
import { Button } from "@/components/ui/button"
import { Input } from "@/components/ui/input"
import {
  Sheet, SheetContent, SheetHeader, SheetTitle,
  SheetDescription, SheetFooter, SheetClose,
} from "@/components/ui/sheet"
import {
  RefreshCwIcon, PlusIcon, DownloadIcon, Trash2Icon,
  HardDriveIcon, ClockIcon, ServerIcon,
  FolderIcon,
} from "lucide-react"
import { cn } from "@/lib/utils"
import { useDataSource } from "@/lib/data-source"
import { listServers } from "@/shared/api/servers"
import { toFrontendServer } from "@/entities/server/model/mappers"
import { createBackupsAsync, deleteBackup, getBackupJob, getBackupScheduleSettings, listBackups, putBackupScheduleSettings, type BackupItem } from "@/shared/api/backups"
import { requestBlob } from "@/shared/api/http-client"
import { toast } from "sonner"
import {
  Stepper,
  StepperContent,
  StepperIndicator,
  StepperItem,
  StepperNav,
  StepperPanel,
  StepperSeparator,
  StepperTitle,
  StepperTrigger,
} from "@/components/reui/stepper"

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

type PageTab = "history" | "settings"
type KindFilter = "all" | "auto" | "manual"
type BackupFreq = "daily" | "weekly" | "monthly"
type StorageType = "local" | "ftp" | "scp" | "smb"
type BackupFormat = "rsc" | "backup"

const WEEK_DAYS = ["Пн", "Вт", "Ср", "Чт", "Пт", "Сб", "Вс"]

// ─── defaults ─────────────────────────────────────────────────────────────────

const defaultSchedule = {
  enabled: true,
  frequency: "daily" as BackupFreq,
  hour: 3,
  minute: 0,
  weekDay: 0,
  monthDay: 1,
  keepCount: 7,
  format: "rsc" as BackupFormat,
}

const defaultStorage = {
  type: "local" as StorageType,
  localPath: "/var/backup/mikrotik",
  host: "",
  port: "",
  username: "",
  password: "",
  remotePath: "/mikrotik-backups",
  share: "backups",
  showPassword: false,
}

// ════════════════════════════════════════════════════════════════════════════
export default function BackupsPage() {
  const { backendUrl } = useDataSource()
  // ── state ──────────────────────────────────────────────────────────────
  const [tab,        setTab]        = useState<PageTab>("history")
  const [backupList, setBackupList] = useState<Backup[]>([])
  const [kindFilter, setKindFilter] = useState<KindFilter>("all")
  const [liveServers, setLiveServers] = useState<Server[]>([])
  const [loading, setLoading] = useState(false)
  const [opBusy, setOpBusy] = useState(false)
  const [opError, setOpError] = useState<string | null>(null)
  const [backupJobId, setBackupJobId] = useState<string | null>(null)
  const [scheduleSaveBusy, setScheduleSaveBusy] = useState(false)

  // Schedule
  const [schedule, setSchedule] = useState(defaultSchedule)
  const setSched = <K extends keyof typeof defaultSchedule>(k: K, v: (typeof defaultSchedule)[K]) =>
    setSchedule((s) => ({ ...s, [k]: v }))

  // Storage
  const [storage, setStorage] = useState(defaultStorage)
  const setStore = <K extends keyof typeof defaultStorage>(k: K, v: (typeof defaultStorage)[K]) =>
    setStorage((s) => ({ ...s, [k]: v }))

  // Server selection (all enabled by default)
  const [selectedServers, setSelectedServers] = useState<Set<string>>(
    new Set()
  )
  function toggleServer(id: string) {
    setSelectedServers((prev) => {
      const next = new Set(prev)
      if (next.has(id)) next.delete(id); else next.add(id)
      return next
    })
  }

  function handleSave() {
    if (scheduleSaveBusy) return
    setScheduleSaveBusy(true)
    setOpError(null)
    void (async () => {
      try {
        await putBackupScheduleSettings(backendUrl, {
          enabled: schedule.enabled,
          frequency: schedule.frequency,
          hour: schedule.hour,
          minute: schedule.minute,
          weekDay: schedule.weekDay,
          monthDay: schedule.monthDay,
          keepCount: schedule.keepCount,
          format: schedule.format,
          serverIds: [...selectedServers],
        })
        toast.success("Настройки сохранены")
      } catch (e) {
        setOpError(e instanceof Error ? e.message : "Ошибка сохранения расписания")
      } finally {
        setScheduleSaveBusy(false)
      }
    })()
  }

  // Manual backup sheet
  const [manualOpen,    setManualOpen]    = useState(false)
  const [manualStep,    setManualStep]    = useState(1)
  const [manualServers, setManualServers] = useState<Set<string>>(new Set())
  const [manualNotes,   setManualNotes]   = useState("")
  const [restoreOpen, setRestoreOpen] = useState(false)
  const [restoreTarget, setRestoreTarget] = useState<Backup | null>(null)
  function toggleManualServer(id: string) {
    setManualServers((prev) => {
      const next = new Set(prev)
      if (next.has(id)) next.delete(id); else next.add(id)
      return next
    })
  }
  const mapApiBackupToUi = useCallback((b: BackupItem): Backup => {
    const kb = Math.max(1, Math.round(b.sizeBytes / 1024))
    return {
      id: b.id,
      server: b.serverName,
      filename: b.filename,
      size: `${kb} КБ`,
      created: new Date(b.createdAt).toLocaleString("ru-RU"),
      kind: b.kind,
      notes: b.notes ?? "",
    }
  }, [])

  const loadLive = useCallback(async () => {
    setLoading(true)
    setOpError(null)
    try {
      const [serversRows, backupsRows, scheduleRow] = await Promise.all([
        listServers(backendUrl),
        listBackups(backendUrl),
        getBackupScheduleSettings(backendUrl),
      ])
      const mappedServers = serversRows.map((s) => toFrontendServer(s))
      setLiveServers(mappedServers)
      setBackupList(backupsRows.map(mapApiBackupToUi))
      setSchedule({
        enabled: scheduleRow.enabled,
        frequency: scheduleRow.frequency,
        hour: scheduleRow.hour,
        minute: scheduleRow.minute,
        weekDay: scheduleRow.weekDay,
        monthDay: scheduleRow.monthDay,
        keepCount: scheduleRow.keepCount,
        format: scheduleRow.format,
      })
      const serverIds = scheduleRow.serverIds.length > 0
        ? scheduleRow.serverIds
        : mappedServers.map((s) => s.id)
      setSelectedServers(new Set(serverIds.filter((id) => mappedServers.some((s) => s.id === id))))
    } catch (e) {
      setOpError(e instanceof Error ? e.message : "Ошибка загрузки данных")
    } finally {
      setLoading(false)
    }
  }, [backendUrl, mapApiBackupToUi])

  useEffect(() => {
    void loadLive()
  }, [loadLive])

  async function handleManualBackup() {
    if (manualServers.size === 0 || opBusy) return
    setOpError(null)
    try {
      const res = await createBackupsAsync(backendUrl, {
        serverIds: [...manualServers],
        notes: manualNotes || undefined,
      })
      setBackupJobId(res.jobId)
      setManualOpen(false)
      setManualServers(new Set())
      setManualNotes("")
      setTab("history")
    } catch (e) {
      setOpError(e instanceof Error ? e.message : "Ошибка создания бэкапа")
    }
  }

  useEffect(() => {
    if (!backupJobId) return
    toast.info("Бэкап выполняется в фоне...")
    let cancelled = false
    const timer = setInterval(() => {
      void (async () => {
        try {
          const job = await getBackupJob(backendUrl, backupJobId)
          if (cancelled) return
          if (job.status === "done") {
            clearInterval(timer)
            setBackupJobId(null)
            if (job.failures.length > 0) {
              setOpError(`Часть бэкапов не создалась: ${job.failures.map((f) => `${f.serverId}: ${f.error}`).join("; ")}`)
            } else {
              toast.success("Бэкап успешно завершён")
            }
            await loadLive()
          } else if (job.status === "failed") {
            clearInterval(timer)
            setBackupJobId(null)
            setOpError(job.failures.map((f) => `${f.serverId}: ${f.error}`).join("; ") || "Ошибка фоновой задачи бэкапа")
            await loadLive()
          }
        } catch (e) {
          clearInterval(timer)
          setBackupJobId(null)
          setOpError(e instanceof Error ? e.message : "Ошибка опроса задачи бэкапа")
        }
      })()
    }, 1200)
    return () => {
      cancelled = true
      clearInterval(timer)
    }
  }, [backendUrl, backupJobId, loadLive])

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

  // Delete backup
  async function handleDelete(id: string) {
    setOpBusy(true)
    setOpError(null)
    try {
      await deleteBackup(backendUrl, id)
      await loadLive()
    } catch (e) {
      setOpError(e instanceof Error ? e.message : "Ошибка удаления бэкапа")
    } finally {
      setOpBusy(false)
    }
  }

  async function handleDownload(id: string, fallbackFilename: string) {
    const res = await requestBlob(backendUrl, `/api/backups/${id}/download`)
    const blob = await res.blob()
    const url = URL.createObjectURL(blob)
    const a = document.createElement("a")
    a.href = url
    a.download = fallbackFilename
    a.click()
    URL.revokeObjectURL(url)
  }

  // ── derived ────────────────────────────────────────────────────────────
  const filtered = useMemo(() =>
    backupList.filter((b) => kindFilter === "all" || b.kind === kindFilter),
    [backupList, kindFilter]
  )

  const autoCount   = backupList.filter((b) => b.kind === "auto").length
  const manualCount = backupList.filter((b) => b.kind === "manual").length
  const serverCount = new Set(backupList.map((b) => b.server)).size

  // Schedule summary string
  const schedSummary = (() => {
    if (!schedule.enabled) return "Отключено"
    const t = `${String(schedule.hour).padStart(2,"0")}:${String(schedule.minute).padStart(2,"0")}`
    if (schedule.frequency === "daily")   return `Каждый день в ${t}`
    if (schedule.frequency === "weekly")  return `Каждую неделю (${WEEK_DAYS[schedule.weekDay]}) в ${t}`
    return `${schedule.monthDay}-го числа каждого месяца в ${t}`
  })()

  // Storage path summary
  const pathSummary = storage.type === "local"
    ? storage.localPath || "/var/backup/mikrotik"
    : `${storage.type.toUpperCase()}://${storage.host || "host"}${storage.remotePath || "/"}`

  // ── render ─────────────────────────────────────────────────────────────
  return (
    <div className="flex flex-col h-full">
      <PageHeader
        crumbs={[{ label: "Управление" }, { label: "Бэкапы" }]}
        actions={
          <>
            <Button
              variant="outline"
              size="sm"
              onClick={() => {
                setManualServers(new Set(liveServers.map((s) => s.id)))
                setManualOpen(true)
              }}
              disabled={loading}
            >
              <RefreshCwIcon className="size-4" />Снять со всех
            </Button>
            <Button
              size="sm"
              onClick={() => { setManualServers(new Set()); setManualNotes(""); setManualStep(1); setManualOpen(true) }}
              disabled={loading}
            >
              <PlusIcon className="size-4" />Новый бэкап
            </Button>
          </>
        }
      />

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

          <KpiStatGrid
            aria-label="Сводка бэкапов"
            items={[
              {
                id: "all",
                label: "Всего бэкапов",
                value: backupList.length,
                icon: <HardDriveIcon className="size-4" />,
                iconClassName: "text-muted-foreground",
              },
              {
                id: "auto",
                label: "Авто",
                value: autoCount,
                icon: <ClockIcon className="size-4" />,
                iconClassName: "text-info",
              },
              {
                id: "manual",
                label: "Вручную",
                value: manualCount,
                icon: <PlusIcon className="size-4" />,
                iconClassName: "text-muted-foreground",
              },
              {
                id: "servers",
                label: "Серверов охвачено",
                value: serverCount,
                icon: <ServerIcon className="size-4" />,
                iconClassName: "text-primary",
              },
            ]}
          />

          {/* Info bar */}
          <div className="flex items-center gap-4 text-xs text-muted-foreground px-1">
            <span className="flex items-center gap-1.5">
              <ClockIcon className="size-3" />
              {schedSummary}
            </span>
            <span className="h-3 w-px bg-border" />
            <span className="flex items-center gap-1.5">
              <FolderIcon className="size-3" />
              {pathSummary}
            </span>
            <span className="h-3 w-px bg-border" />
            <span className="flex items-center gap-1.5">
              <ServerIcon className="size-3" />
              {selectedServers.size} из {liveServers.length} серверов
            </span>
            <button onClick={() => setTab("settings")}
              className="ml-auto text-xs text-primary hover:underline">
              Изменить настройки →
            </button>
          </div>
          {/* Tabs */}
          <div className="flex items-center gap-1 border-b border-border">
            {([
              { id: "history",  label: "История бэкапов" },
              { id: "settings", label: "Настройки" },
            ] as { id: PageTab; label: string }[]).map((t) => (
              <button key={t.id} onClick={() => setTab(t.id)}
                className={`px-4 py-2.5 text-sm font-medium border-b-2 -mb-px transition-colors ${tab === t.id ? "border-primary text-foreground" : "border-transparent text-muted-foreground hover:text-foreground"}`}>
                {t.label}
              </button>
            ))}
          </div>

          {/* ── История ──────────────────────────────────────────────────── */}
          {tab === "history" && (
            <DataPageCard>
              <DataPageToolbar
                segmented={{
                  value: kindFilter,
                  onChange: setKindFilter,
                  options: [
                    { value: "all", label: "Все", count: backupList.length },
                    { value: "auto", label: "Авто", count: autoCount },
                    { value: "manual", label: "Вручную", count: manualCount },
                  ],
                }}
                countLabel={`${filtered.length} бэкапов`}
              />
              <BackupsDataGrid
                backups={filtered}
                onDownload={handleDownload}
                onRestore={(b) => {
                  setRestoreTarget(b)
                  setRestoreOpen(true)
                }}
                onDelete={handleDelete}
              />
            </DataPageCard>
          )}

          {/* ── Настройки ────────────────────────────────────────────────── */}
          {tab === "settings" && (
            <div className="grid grid-cols-1 lg:grid-cols-2 gap-5">

              {/* Расписание */}
              <OpsPanel title="Расписание" contentClassName="px-5 py-5 flex flex-col gap-5">

                  <div className="flex items-center justify-between">
                    <div>
                      <p className="text-sm font-medium">Автоматический бэкап</p>
                      <p className="text-xs text-muted-foreground mt-0.5">Создавать бэкапы по расписанию</p>
                    </div>
                    <FormToggle checked={schedule.enabled} onChange={(v) => setSched("enabled", v)} />
                  </div>

                  <div className={cn("flex flex-col gap-4 transition-opacity", !schedule.enabled && "opacity-40 pointer-events-none")}>
                    <FormField label="Частота">
                      <SegmentedControl
                        value={schedule.frequency}
                        onChange={(v) => setSched("frequency", v)}
                        options={[
                          { value: "daily",   label: "Ежедневно" },
                          { value: "weekly",  label: "Еженедельно" },
                          { value: "monthly", label: "Ежемесячно" },
                        ]}
                      />
                    </FormField>

                    {schedule.frequency === "weekly" && (
                      <FormField label="День недели">
                        <div className="flex gap-1">
                          {WEEK_DAYS.map((d, i) => (
                            <button key={i} type="button" onClick={() => setSched("weekDay", i)}
                              className={cn(
                                "w-9 h-9 rounded text-sm font-medium border transition-colors",
                                schedule.weekDay === i
                                  ? "bg-primary text-primary-foreground border-primary"
                                  : "border-border text-muted-foreground hover:text-foreground"
                              )}>
                              {d}
                            </button>
                          ))}
                        </div>
                      </FormField>
                    )}

                    {schedule.frequency === "monthly" && (
                      <FormField label="День месяца" hint="1–28">
                        <Input type="number" min={1} max={28} className="font-mono w-24"
                          value={schedule.monthDay}
                          onChange={(e) => setSched("monthDay", Math.min(28, Math.max(1, Number(e.target.value))))} />
                      </FormField>
                    )}

                    <FormField label="Время запуска">
                      <div className="flex items-center gap-2">
                        <div className="relative">
                          <Input type="number" min={0} max={23} className="font-mono w-20 text-center"
                            value={String(schedule.hour).padStart(2, "0")}
                            onChange={(e) => setSched("hour", Math.min(23, Math.max(0, Number(e.target.value))))} />
                        </div>
                        <span className="text-muted-foreground font-mono text-lg">:</span>
                        <div className="flex gap-1">
                          {[0, 15, 30, 45].map((m) => (
                            <button key={m} type="button" onClick={() => setSched("minute", m)}
                              className={cn(
                                "px-2.5 py-1.5 rounded text-xs font-mono border transition-colors",
                                schedule.minute === m
                                  ? "bg-primary text-primary-foreground border-primary"
                                  : "border-border text-muted-foreground hover:text-foreground"
                              )}>
                              {String(m).padStart(2, "0")}
                            </button>
                          ))}
                        </div>
                      </div>
                    </FormField>

                    <div className="grid grid-cols-2 gap-4">
                      <FormField label="Хранить бэкапов" hint="На каждый сервер">
                        <Input type="number" min={1} max={90} className="font-mono"
                          value={schedule.keepCount}
                          onChange={(e) => setSched("keepCount", Math.max(1, Number(e.target.value)))} />
                      </FormField>
                      <FormField label="Формат файла">
                        <SegmentedControl
                          value={schedule.format}
                          onChange={(v) => setSched("format", v)}
                          options={[
                            { value: "rsc",    label: ".rsc" },
                            { value: "backup", label: ".backup" },
                          ]}
                        />
                      </FormField>
                    </div>
                  </div>
              </OpsPanel>

              {/* Хранилище */}
              <OpsPanel title="Хранилище" contentClassName="px-5 py-5 flex flex-col gap-5">

                  <FormField label="Тип хранилища">
                    <SegmentedControl
                      value={storage.type}
                      onChange={(v) => setStore("type", v)}
                      options={[
                        { value: "local", label: "Локально" },
                        { value: "ftp",   label: "FTP" },
                        { value: "scp",   label: "SCP" },
                        { value: "smb",   label: "SMB" },
                      ]}
                    />
                  </FormField>

                  {storage.type === "local" && (
                    <FormField label="Путь сохранения" hint="Директория на сервере приложения">
                      <Input className="font-mono" placeholder="/var/backup/mikrotik"
                        value={storage.localPath}
                        onChange={(e) => setStore("localPath", e.target.value)} />
                    </FormField>
                  )}

                  {storage.type !== "local" && (
                    <>
                      <div className="grid grid-cols-3 gap-3">
                        <div className="col-span-2">
                          <FormField label="Хост">
                            <Input className="font-mono" placeholder="192.168.1.100"
                              value={storage.host} onChange={(e) => setStore("host", e.target.value)} />
                          </FormField>
                        </div>
                        <FormField label="Порт">
                          <Input className="font-mono"
                            placeholder={storage.type === "ftp" ? "21" : storage.type === "scp" ? "22" : "445"}
                            value={storage.port} onChange={(e) => setStore("port", e.target.value)} />
                        </FormField>
                      </div>

                      {storage.type === "smb" && (
                        <FormField label="Общая папка (Share)">
                          <Input className="font-mono" placeholder="backups"
                            value={storage.share} onChange={(e) => setStore("share", e.target.value)} />
                        </FormField>
                      )}

                      <div className="grid grid-cols-2 gap-3">
                        <FormField label="Пользователь">
                          <Input className="font-mono" placeholder="backup-user"
                            value={storage.username} onChange={(e) => setStore("username", e.target.value)} />
                        </FormField>
                        <FormField label={storage.type === "scp" ? "Пароль / ключ" : "Пароль"}>
                          <div className="relative">
                            <Input
                              type={storage.showPassword ? "text" : "password"}
                              className="font-mono pr-8"
                              placeholder="••••••••"
                              value={storage.password}
                              onChange={(e) => setStore("password", e.target.value)}
                            />
                            <button type="button"
                              onClick={() => setStore("showPassword", !storage.showPassword)}
                              className="absolute right-2.5 top-1/2 -translate-y-1/2 text-muted-foreground hover:text-foreground text-xs">
                              {storage.showPassword ? "скрыть" : "показ"}
                            </button>
                          </div>
                        </FormField>
                      </div>

                      <FormField label="Удалённый путь">
                        <Input className="font-mono" placeholder="/mikrotik-backups"
                          value={storage.remotePath} onChange={(e) => setStore("remotePath", e.target.value)} />
                      </FormField>
                    </>
                  )}

                  {/* Preview */}
                  <div className="rounded-lg border border-border bg-muted/20 px-4 py-3 text-xs text-muted-foreground">
                    <p className="font-medium text-foreground mb-1">Путь сохранения</p>
                    <p className="font-mono text-foreground break-all">{pathSummary}</p>
                    {storage.type !== "local" && (
                      <p className="mt-1">Файлы: <span className="font-mono text-foreground">{pathSummary}/{"<server-name>"}_{"{date}"}.{schedule.format}</span></p>
                    )}
                    {storage.type === "local" && (
                      <p className="mt-1">Файлы: <span className="font-mono text-foreground">{storage.localPath || "/var/backup/mikrotik"}/{"<server-name>"}_{"{date}"}.{schedule.format}</span></p>
                    )}
                  </div>
              </OpsPanel>

              {/* Серверы */}
              <OpsPanel
                className="lg:col-span-2"
                title="Серверы для бэкапа"
                headerRight={
                    <div className="flex items-center gap-2 shrink-0">
                      <button type="button" onClick={() => setSelectedServers(new Set(liveServers.map(s => s.id)))}
                        className="text-xs text-primary hover:underline">Выбрать все</button>
                      <span className="text-border">·</span>
                      <button type="button" onClick={() => setSelectedServers(new Set())}
                        className="text-xs text-muted-foreground hover:text-foreground hover:underline">Сбросить</button>
                    </div>
                }
                contentClassName="px-5 py-5 flex flex-col gap-4"
              >

                  <div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 gap-2">
                    {liveServers.map((s) => {
                      const checked = selectedServers.has(s.id)
                      return (
                        <button key={s.id} type="button" onClick={() => toggleServer(s.id)}
                          className={cn(
                            "flex items-center gap-3 rounded-lg border p-3 text-left transition-colors",
                            checked
                              ? "border-primary/40 bg-primary/5"
                              : "border-border hover:border-border/80 hover:bg-muted/40"
                          )}>
                          <div className={cn(
                            "flex size-4 shrink-0 items-center justify-center rounded border transition-colors",
                            checked ? "bg-primary border-primary" : "border-border"
                          )}>
                            {checked && <svg width="10" height="8" viewBox="0 0 10 8" fill="none"><path d="M1 4l2.5 2.5L9 1" stroke="white" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round"/></svg>}
                          </div>
                          <div className="flex-1 min-w-0">
                            <p className="text-sm font-medium truncate">{s.name}</p>
                            <div className="flex items-center gap-1.5 mt-0.5">
                              <span className="text-xs text-muted-foreground">{s.site}</span>
                              <StatusBadge status={s.status} />
                            </div>
                          </div>
                        </button>
                      )
                    })}
                  </div>

                  <p className="text-xs text-muted-foreground">
                    Выбрано {selectedServers.size} из {liveServers.length} серверов
                  </p>
              </OpsPanel>

              {/* Save button */}
              <div className="lg:col-span-2 flex items-center gap-3">
                <Button onClick={handleSave} className="gap-2" disabled={scheduleSaveBusy}>
                  Сохранить настройки
                </Button>
              </div>
            </div>
          )}

        </div>
      </div>

      {/* ══ Sheet: Manual backup ══════════════════════════════════════════════ */}
      <Sheet open={manualOpen} onOpenChange={(v) => { setManualOpen(v); if (!v) setManualStep(1) }}>
        <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>Новый бэкап</SheetTitle>
            <SheetDescription>Снять конфигурацию вручную с выбранных серверов</SheetDescription>
          </SheetHeader>

          <Stepper value={manualStep} onValueChange={setManualStep} className="flex-1 flex flex-col min-h-0 px-6 py-5">
            <StepperNav className="mb-5">
              <StepperItem step={1}>
                <StepperTrigger>
                  <StepperIndicator>1</StepperIndicator>
                  <StepperTitle className="sr-only">Серверы</StepperTitle>
                </StepperTrigger>
                <StepperSeparator />
              </StepperItem>
              <StepperItem step={2}>
                <StepperTrigger>
                  <StepperIndicator>2</StepperIndicator>
                  <StepperTitle className="sr-only">Заметка</StepperTitle>
                </StepperTrigger>
                <StepperSeparator />
              </StepperItem>
              <StepperItem step={3}>
                <StepperTrigger>
                  <StepperIndicator>3</StepperIndicator>
                  <StepperTitle className="sr-only">Подтверждение</StepperTitle>
                </StepperTrigger>
              </StepperItem>
            </StepperNav>
            <StepperPanel className="flex-1 overflow-y-auto">
              <StepperContent value={1} className="flex flex-col gap-3">
                <div className="flex items-center justify-between mb-1">
                  <p className="text-sm font-medium">Выберите серверы</p>
                  <div className="flex items-center gap-2">
                    <button type="button" onClick={() => setManualServers(new Set(liveServers.map((s) => s.id)))}
                      className="text-xs text-primary hover:underline">Все</button>
                    <span className="text-border">·</span>
                    <button type="button" onClick={() => setManualServers(new Set())}
                      className="text-xs text-muted-foreground hover:text-foreground hover:underline">Сбросить</button>
                  </div>
                </div>
                {liveServers.map((s) => {
                  const checked = manualServers.has(s.id)
                  return (
                    <button key={s.id} type="button" onClick={() => toggleManualServer(s.id)}
                      className={cn(
                        "flex items-center gap-3 rounded-lg border p-3 text-left transition-colors",
                        checked ? "border-primary/40 bg-primary/5" : "border-border hover:bg-muted/40"
                      )}>
                      <div className={cn(
                        "flex size-4 shrink-0 items-center justify-center rounded border transition-colors",
                        checked ? "bg-primary border-primary" : "border-border"
                      )}>
                        {checked && <svg width="10" height="8" viewBox="0 0 10 8" fill="none"><path d="M1 4l2.5 2.5L9 1" stroke="white" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round"/></svg>}
                      </div>
                      <div className="flex-1 min-w-0">
                        <p className="text-sm font-medium">{s.name}</p>
                        <div className="flex items-center gap-1.5 mt-0.5">
                          <span className="text-xs font-mono text-muted-foreground">{s.host}</span>
                          <StatusBadge status={s.status} />
                        </div>
                      </div>
                      {s.status === "offline" && (
                        <span className="text-xs text-muted-foreground">недоступен</span>
                      )}
                    </button>
                  )
                })}
              </StepperContent>
              <StepperContent value={2} className="flex flex-col gap-4">
                <FormField label="Заметка">
                  <Input placeholder="Например: перед обновлением BGP"
                    value={manualNotes} onChange={(e) => setManualNotes(e.target.value)} />
                </FormField>
              </StepperContent>
              <StepperContent value={3} className="flex flex-col gap-3 text-sm">
                <p className="text-muted-foreground">
                  Будет создан бэкап для <strong className="text-foreground">{manualServers.size}</strong> серверов.
                </p>
                {manualNotes && (
                  <p className="text-muted-foreground">Заметка: {manualNotes}</p>
                )}
              </StepperContent>
            </StepperPanel>
          </Stepper>

          <SheetFooter className="px-6 py-4 border-t shrink-0 flex-row gap-2">
            <SheetClose render={<Button variant="outline" className="flex-1" />}>Отмена</SheetClose>
            {manualStep > 1 && (
              <Button variant="outline" className="flex-1" onClick={() => setManualStep((s) => s - 1)}>
                Назад
              </Button>
            )}
            {manualStep < 3 ? (
              <Button
                className="flex-1"
                disabled={manualStep === 1 && manualServers.size === 0}
                onClick={() => setManualStep((s) => s + 1)}
              >
                Далее
              </Button>
            ) : (
              <Button className="flex-1"
                disabled={manualServers.size === 0 || backupJobId !== null}
                onClick={handleManualBackup}>
                Снять бэкап ({manualServers.size})
              </Button>
            )}
          </SheetFooter>
        </SheetContent>
      </Sheet>

      <FileImportDialog
        open={restoreOpen}
        onOpenChange={setRestoreOpen}
        title={restoreTarget ? `Восстановление: ${restoreTarget.filename}` : "Восстановление бэкапа"}
        description="Выберите файл конфигурации для загрузки на роутер"
        accept=".backup,.rsc,.zip"
        onImport={async (files) => {
          toast.success(`Файл ${files[0]?.name} подготовлен к восстановлению на ${restoreTarget?.server ?? "сервер"}`)
        }}
      />
    </div>
  )
}
