"use client"

import { useCallback, useEffect, useMemo, useState } from "react"
import { PageHeader } from "@/components/page-header"
import { FormField, FormToggle, SectionTitle } from "@/components/form-kit"
import { FileImportDialog } from "@/components/file-import-dialog"
import { routerCertificates, servers as mockServers } from "@/lib/data"
import type { CertStatus, Server } from "@/lib/data"
import type { CertificateDto } from "@mmapp/contracts/certificates"
import { Frame, FramePanel } from "@/components/reui/frame"
import { IconTile } from "@/components/reui/icon-tile"
import { OpsPanel } from "@/components/ops-panel"
import { DataPageCard } from "@/components/data-page-card"
import { DataPageToolbar } from "@/components/data-page-toolbar"
import { CertificatesDataGrid } from "@/components/data-grids/certificates-data-grid"
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 { cn } from "@/lib/utils"
import { useDataSource } from "@/lib/data-source"
import { listServers } from "@/shared/api/servers"
import { toFrontendServer } from "@/entities/server/model/mappers"
import {
  createCertificateIssueJob,
  getAcmeSettings,
  getCertificateIssueJob,
  listCertificates,
  putAcmeSettings,
  refreshCertificates,
  testAcmeSettings,
} from "@/shared/api/certificates"
import { toast } from "sonner"
import {
  ShieldCheckIcon,
  BadgeCheckIcon,
  AlertTriangleIcon,
  AlertCircleIcon,
  PlusIcon,
  RefreshCwIcon,
  UploadIcon,
} from "lucide-react"
import {
  Stepper,
  StepperContent,
  StepperIndicator,
  StepperItem,
  StepperNav,
  StepperPanel,
  StepperSeparator,
  StepperTitle,
  StepperTrigger,
} from "@/components/reui/stepper"

function mockToDto(cert: (typeof routerCertificates)[number]): CertificateDto {
  return {
    id: cert.id,
    name: cert.name,
    serverId: cert.serverId,
    commonName: cert.commonName,
    sans: cert.sans,
    issuedBy: cert.issuedBy,
    validFrom: cert.validFrom,
    validUntil: cert.validUntil,
    daysLeft: cert.daysLeft,
    keySize: cert.keySize,
    usage: cert.usage,
    trusted: cert.trusted,
    status: cert.status,
  }
}

function CertPartAlertExpired({ expired }: { expired: CertificateDto[] }) {
  return (
    <div className="flex items-start gap-3 rounded-lg bg-red-500/5 border border-red-500/20 px-4 py-3 text-sm">
      <AlertCircleIcon className="size-5 text-red-500 shrink-0 mt-0.5" />
      <div>
        <p className="font-medium text-red-600 dark:text-red-400">
          {expired.length} {expired.length === 1 ? "истёкший сертификат" : "истёкших сертификата"}
        </p>
        <p className="text-muted-foreground text-xs mt-0.5">
          {expired.map((c) => c.name).join(", ")} — требуют обновления
        </p>
      </div>
    </div>
  )
}

function CertPartAlertExpiring({ expiring }: { expiring: CertificateDto[] }) {
  return (
    <div className="flex items-start gap-3 rounded-lg bg-amber-500/5 border border-amber-500/20 px-4 py-3 text-sm">
      <AlertTriangleIcon className="size-5 text-amber-500 shrink-0 mt-0.5" />
      <div>
        <p className="font-medium text-amber-600 dark:text-amber-400">
          {expiring.length} {expiring.length === 1 ? "сертификат истекает" : "сертификата истекают"} в течение 30 дней
        </p>
        <p className="text-muted-foreground text-xs mt-0.5">
          {expiring.map((c) => `${c.name} (${c.daysLeft}д)`).join(", ")}
        </p>
      </div>
    </div>
  )
}

function CertPartKpi({
  displayCerts,
  expiring,
  expired,
}: {
  displayCerts: CertificateDto[]
  expiring: CertificateDto[]
  expired: CertificateDto[]
}) {
  return (
    <div className="grid grid-cols-2 lg:grid-cols-4 gap-4">
      {[
        {
          label: "Всего",
          value: displayCerts.length,
          icon: <ShieldCheckIcon className="size-4 text-muted-foreground" />,
        },
        {
          label: "Действующих",
          value: displayCerts.filter((c) => c.status === "valid").length,
          icon: <BadgeCheckIcon className="size-4 text-emerald-500" />,
        },
        {
          label: "Истекают",
          value: expiring.length,
          icon: <AlertTriangleIcon className="size-4 text-amber-500" />,
        },
        {
          label: "Истёкших",
          value: expired.length,
          icon: <AlertCircleIcon className="size-4 text-red-500" />,
        },
      ].map((s) => (
        <Frame key={s.label} className="h-full">
          <FramePanel className="relative isolate flex h-full items-start gap-3">
            <IconTile variant="elevated" aria-hidden="true" className="size-10.5 text-muted-foreground">
              {s.icon}
            </IconTile>
            <div className="flex min-w-0 flex-1 flex-col gap-0.5">
              <p className="text-muted-foreground text-sm font-medium">{s.label}</p>
              <p className="text-2xl leading-none font-bold tabular-nums">{s.value}</p>
            </div>
          </FramePanel>
        </Frame>
      ))}
    </div>
  )
}

function CertPartAcmeSettings({
  acmeDirectoryUrl,
  setAcmeDirectoryUrl,
  acmeZoneId,
  setAcmeZoneId,
  acmeTokenDraft,
  setAcmeTokenDraft,
  acmeTokenConfigured,
  acmeSaveBusy,
  onTest,
  onSave,
}: {
  acmeDirectoryUrl: string
  setAcmeDirectoryUrl: (v: string) => void
  acmeZoneId: string
  setAcmeZoneId: (v: string) => void
  acmeTokenDraft: string
  setAcmeTokenDraft: (v: string) => void
  acmeTokenConfigured: boolean
  acmeSaveBusy: boolean
  onTest: () => void
  onSave: () => void
}) {
  return (
    <OpsPanel
      title="ACME · Cloudflare DNS-01"
      description="Публичные Let's Encrypt для зон в Cloudflare выпускаются на backend и импортируются на RouterOS 7.22+."
      contentClassName="px-5 py-4 flex flex-col gap-3"
    >
        <form
          className="grid gap-3 md:grid-cols-2"
          autoComplete="off"
          onSubmit={(event) => event.preventDefault()}
        >
          <div className="flex flex-col gap-1.5">
            <label className="text-sm font-medium">ACME directory URL</label>
            <Input
              autoComplete="off"
              name="acme-directory-url"
              value={acmeDirectoryUrl}
              onChange={(e) => setAcmeDirectoryUrl(e.target.value)}
            />
          </div>
          <div className="flex flex-col gap-1.5">
            <label className="text-sm font-medium">Cloudflare zone id (опционально)</label>
            <Input
              autoComplete="off"
              name="cloudflare-zone-id"
              value={acmeZoneId}
              onChange={(e) => setAcmeZoneId(e.target.value)}
              placeholder="Авто по домену"
            />
          </div>
          <div className="flex flex-col gap-1.5 md:col-span-2">
            <label className="text-sm font-medium">Cloudflare API token</label>
            <Input
              type="password"
              autoComplete="new-password"
              name="cloudflare-api-token"
              value={acmeTokenDraft}
              onChange={(e) => setAcmeTokenDraft(e.target.value)}
              placeholder={
                acmeTokenConfigured
                  ? "Токен сохранён — введите новый для замены"
                  : "API token с правом DNS"
              }
            />
          </div>
        </form>
        <div className="flex flex-wrap gap-2">
          <Button variant="outline" size="sm" disabled={acmeSaveBusy} onClick={onTest}>
            Проверить Cloudflare
          </Button>
          <Button size="sm" disabled={acmeSaveBusy} onClick={onSave}>
            Сохранить настройки
          </Button>
        </div>
    </OpsPanel>
  )
}

function CertPartReference() {
  return (
    <OpsPanel
      title="RouterOS 7 · /certificate — справка CLI"
      description="RouterOS 7.22+ · публичные LE для Cloudflare через backend DNS-01, не через /certificate add-acme на устройстве."
      contentClassName="px-5 py-4"
    >
        <div className="grid grid-cols-1 sm:grid-cols-3 gap-4 text-xs font-mono">
          {[
            {
              title: "Создать CA",
              lines: [
                "/certificate add \\",
                "  name=my-ca \\",
                "  common-name=MyCA \\",
                "  key-size=4096 \\",
                "  days-valid=3650 \\",
                "  key-usage=key-cert-sign,crl-sign",
                "/certificate sign my-ca",
              ],
            },
            {
              title: "Импорт LE",
              lines: [
                "/certificate import \\",
                "  file-name=router.crt \\",
                "  name=router-cert \\",
                "  trusted=yes \\",
                "  trust-store=www,api",
              ],
            },
            {
              title: "Статус",
              lines: ["/certificate print detail", "/certificate export-certificate router-cert"],
            },
          ].map((b) => (
            <div key={b.title}>
              <p className="font-sans font-semibold text-foreground/80 mb-1.5 text-[11px] uppercase tracking-wide">
                {b.title}
              </p>
              <pre className="bg-zinc-950 rounded-md p-2.5 text-zinc-300 text-[11px] leading-relaxed overflow-x-auto whitespace-pre">
                {b.lines.join("\n")}
              </pre>
            </div>
          ))}
        </div>
    </OpsPanel>
  )
}

function CertPartIssueForm({
  serverList,
  issueServerId,
  setIssueServerId,
  issueCertName,
  setIssueCertName,
  issueCommonName,
  setIssueCommonName,
  issueSans,
  setIssueSans,
  issueTrustWww,
  setIssueTrustWww,
  issueTrustApi,
  setIssueTrustApi,
  step,
}: {
  serverList: Server[]
  issueServerId: string
  setIssueServerId: (v: string) => void
  issueCertName: string
  setIssueCertName: (v: string) => void
  issueCommonName: string
  setIssueCommonName: (v: string) => void
  issueSans: string
  setIssueSans: (v: string) => void
  issueTrustWww: boolean
  setIssueTrustWww: (v: boolean) => void
  issueTrustApi: boolean
  setIssueTrustApi: (v: boolean) => void
  step?: 1 | 2 | 3 | 4
}) {
  const showAll = step == null
  return (
    <div className="flex flex-col gap-5">
      {(showAll || step === 1) && (
      <div className="flex flex-col gap-4">
        <SectionTitle>Основные</SectionTitle>
        <FormField label="Сервер" required hint="RouterOS 7.22+, куда импортируется сертификат">
          <select
            value={issueServerId}
            onChange={(e) => setIssueServerId(e.target.value)}
            className="h-8 w-full rounded-lg border border-input bg-background px-2.5 text-sm text-foreground outline-none focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring/50"
          >
            <option value="" disabled>
              Выбрать сервер…
            </option>
            {serverList.map((s) => (
              <option key={s.id} value={s.id}>
                {s.name} ({s.site})
              </option>
            ))}
          </select>
        </FormField>
        <FormField label="Имя сертификата на роутере" required hint="Имя объекта /certificate на устройстве">
          <Input
            className="font-mono"
            value={issueCertName}
            onChange={(e) => setIssueCertName(e.target.value)}
            placeholder="router-le"
          />
        </FormField>
      </div>
      )}

      {(showAll || step === 2) && (
      <div className="flex flex-col gap-4">
        <SectionTitle>Домены</SectionTitle>
        <FormField label="Common Name" required hint="Основное имя в сертификате">
          <Input
            className="font-mono"
            value={issueCommonName}
            onChange={(e) => setIssueCommonName(e.target.value)}
            placeholder="vpn.example.com"
          />
        </FormField>
        <FormField label="SAN" hint="По одному имени в строке">
          <textarea
            className="min-h-24 w-full rounded-lg border border-input bg-background px-2.5 py-2 text-sm font-mono text-foreground outline-none focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring/50"
            value={issueSans}
            onChange={(e) => setIssueSans(e.target.value)}
            placeholder="www.example.com"
          />
        </FormField>
        <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">Let&apos;s Encrypt · DNS-01 (Cloudflare)</p>
          <p>TXT-запись создаётся в Cloudflare, сертификат импортируется на выбранный RouterOS.</p>
        </div>
      </div>
      )}

      {(showAll || step === 3) && (
      <div className="flex flex-col gap-4">
        <SectionTitle>Импорт на RouterOS</SectionTitle>
        <div className="flex items-center justify-between">
          <div>
            <p className="text-sm font-medium">Trust store · www</p>
            <p className="text-xs text-muted-foreground">Веб-интерфейс и HTTPS-сервисы</p>
          </div>
          <FormToggle checked={issueTrustWww} onChange={setIssueTrustWww} />
        </div>
        <div className="flex items-center justify-between">
          <div>
            <p className="text-sm font-medium">Trust store · api</p>
            <p className="text-xs text-muted-foreground">REST API и управление</p>
          </div>
          <FormToggle checked={issueTrustApi} onChange={setIssueTrustApi} />
        </div>
      </div>
      )}

      {(showAll || step === 4) && (
        <div className="rounded-lg border border-border bg-muted/20 px-4 py-3 text-sm">
          <p className="font-medium mb-2">Проверьте параметры</p>
          <ul className="text-xs text-muted-foreground space-y-1">
            <li>Сервер: {serverList.find((s) => s.id === issueServerId)?.name ?? "—"}</li>
            <li>Имя: {issueCertName || "—"}</li>
            <li>CN: {issueCommonName || "—"}</li>
            <li>Trust www: {issueTrustWww ? "да" : "нет"} · api: {issueTrustApi ? "да" : "нет"}</li>
          </ul>
        </div>
      )}
    </div>
  )
}

export default function CertificatesPage() {
  const { mode, backendUrl, prefsHydrated, backendStatus } = useDataSource()
  const isLive = prefsHydrated && mode === "live"
  const liveReady = isLive && backendStatus === true

  const [search, setSearch] = useState("")
  const [statusFilter, setStatusFilter] = useState<CertStatus | "all">("all")
  const [certificates, setCertificates] = useState<CertificateDto[]>([])
  const [loadState, setLoadState] = useState<"idle" | "loading" | "error">("idle")
  const [loadError, setLoadError] = useState<string | null>(null)
  const [serverList, setServerList] = useState<Server[]>([])

  const [issueOpen, setIssueOpen] = useState(false)
  const [issueStep, setIssueStep] = useState(1)
  const [importOpen, setImportOpen] = useState(false)
  const [issueBusy, setIssueBusy] = useState(false)
  const [issueServerId, setIssueServerId] = useState("")
  const [issueCertName, setIssueCertName] = useState("")
  const [issueCommonName, setIssueCommonName] = useState("")
  const [issueSans, setIssueSans] = useState("")
  const [issueTrustWww, setIssueTrustWww] = useState(true)
  const [issueTrustApi, setIssueTrustApi] = useState(true)

  const [acmeDirectoryUrl, setAcmeDirectoryUrl] = useState(
    "https://acme-v02.api.letsencrypt.org/directory",
  )
  const [acmeZoneId, setAcmeZoneId] = useState("")
  const [acmeTokenDraft, setAcmeTokenDraft] = useState("")
  const [acmeTokenConfigured, setAcmeTokenConfigured] = useState(false)
  const [acmeSaveBusy, setAcmeSaveBusy] = useState(false)

  const displayCerts = useMemo(() => {
    if (!prefsHydrated) return []
    if (isLive) return certificates
    return routerCertificates.map(mockToDto)
  }, [prefsHydrated, isLive, certificates])

  const serverById = useMemo(() => {
    const map = new Map<string, Server>()
    for (const s of isLive ? serverList : mockServers) map.set(s.id, s)
    return map
  }, [isLive, serverList])

  const loadLive = useCallback(
    async (silent = false) => {
      if (!isLive) return
      if (!silent) setLoadState("loading")
      setLoadError(null)
      try {
        const [certRes, serversRes] = await Promise.all([
          listCertificates(backendUrl),
          listServers(backendUrl),
        ])
        setCertificates(certRes.certificates)
        setServerList(serversRes.map(toFrontendServer))
        if (certRes.failures.length > 0 && certRes.certificates.length === 0) {
          setLoadError(
            certRes.failures.map((f) => `${f.serverName ?? f.serverId}: ${f.error}`).join("; "),
          )
        } else if (certRes.failures.length > 0) {
          toast.warning(`Часть серверов недоступна: ${certRes.failures.length}`)
        }
        setLoadState("idle")
      } catch (e) {
        setCertificates([])
        setLoadError(e instanceof Error ? e.message : "Не удалось загрузить сертификаты")
        setLoadState("error")
      }
    },
    [backendUrl, isLive],
  )

  const loadAcmeSettings = useCallback(async () => {
    if (!liveReady) return
    try {
      const s = await getAcmeSettings(backendUrl)
      setAcmeDirectoryUrl(s.directoryUrl)
      setAcmeZoneId(s.defaultZoneId ?? "")
      setAcmeTokenConfigured(s.tokenConfigured)
    } catch {
      /* ignore */
    }
  }, [backendUrl, liveReady])

  useEffect(() => {
    if (!isLive) {
      queueMicrotask(() => {
        setCertificates([])
        setServerList([])
        setLoadState("idle")
        setLoadError(null)
      })
      return
    }
    queueMicrotask(() => {
      void loadLive()
      void loadAcmeSettings()
    })
  }, [isLive, loadLive, loadAcmeSettings])

  const expiring = useMemo(
    () => displayCerts.filter((c) => c.status === "valid" && c.daysLeft >= 0 && c.daysLeft <= 30),
    [displayCerts],
  )
  const expired = useMemo(() => displayCerts.filter((c) => c.status === "expired"), [displayCerts])

  const filtered = useMemo(() => {
    return displayCerts.filter((c) => {
      if (statusFilter !== "all" && c.status !== statusFilter) return false
      if (!search) return true
      const q = search.toLowerCase()
      return (
        c.name.toLowerCase().includes(q) ||
        c.commonName.toLowerCase().includes(q) ||
        c.issuedBy.toLowerCase().includes(q) ||
        c.sans.some((s) => s.includes(q))
      )
    })
  }, [displayCerts, search, statusFilter])

  async function handleRefresh() {
    if (!liveReady) return
    try {
      const res = await refreshCertificates(backendUrl)
      setCertificates(res.certificates)
      toast.success("Список сертификатов обновлён")
    } catch (e) {
      toast.error(e instanceof Error ? e.message : "Ошибка обновления")
    }
  }

  async function pollIssueJob(jobId: string) {
    for (let i = 0; i < 120; i++) {
      await new Promise((r) => setTimeout(r, 2000))
      const job = await getCertificateIssueJob(backendUrl, jobId)
      if (job.status === "done") {
        toast.success("Сертификат выпущен и импортирован на роутер")
        setIssueOpen(false)
        await loadLive(true)
        return
      }
      if (job.status === "failed") {
        throw new Error(job.error ?? "Выпуск не удался")
      }
    }
    throw new Error("Таймаут ожидания выпуска сертификата")
  }

  async function handleIssue() {
    if (!liveReady) return
    const domains = [
      issueCommonName.trim(),
      ...issueSans.split(/[\n,;]+/).map((s) => s.trim()),
    ].filter(Boolean)
    if (!issueServerId || !issueCertName.trim() || domains.length === 0) {
      toast.error("Укажите сервер, имя сертификата и домены")
      return
    }
    setIssueBusy(true)
    try {
      const trustStore: Array<"www" | "api"> = []
      if (issueTrustWww) trustStore.push("www")
      if (issueTrustApi) trustStore.push("api")
      const { jobId } = await createCertificateIssueJob(backendUrl, {
        serverId: issueServerId,
        certName: issueCertName.trim(),
        domainNames: domains,
        trustStore,
      })
      toast.message("Выпуск сертификата запущен…")
      await pollIssueJob(jobId)
    } catch (e) {
      toast.error(e instanceof Error ? e.message : "Ошибка выпуска")
    } finally {
      setIssueBusy(false)
    }
  }

  async function handleSaveAcmeSettings() {
    if (!liveReady) return
    setAcmeSaveBusy(true)
    try {
      const saved = await putAcmeSettings(backendUrl, {
        directoryUrl: acmeDirectoryUrl.trim(),
        defaultZoneId: acmeZoneId.trim() || null,
        cloudflareApiToken: acmeTokenDraft.trim() ? acmeTokenDraft.trim() : undefined,
      })
      setAcmeTokenConfigured(saved.tokenConfigured)
      setAcmeTokenDraft("")
      toast.success("Настройки ACME сохранены")
    } catch (e) {
      toast.error(e instanceof Error ? e.message : "Не удалось сохранить настройки")
    } finally {
      setAcmeSaveBusy(false)
    }
  }

  async function handleTestAcme() {
    if (!liveReady) return
    try {
      const res = await testAcmeSettings(backendUrl, {
        cloudflareApiToken: acmeTokenDraft.trim() || undefined,
      })
      if (res.ok) toast.success(res.message ?? "Cloudflare API доступен")
      else toast.error(res.message ?? "Проверка не прошла")
    } catch (e) {
      toast.error(e instanceof Error ? e.message : "Проверка Cloudflare не удалась")
    }
  }

  return (
    <div className="flex flex-col h-full">
      <PageHeader
        crumbs={[{ label: "Управление" }, { label: "Сертификаты" }]}
        actions={
          <>
            <Button
              variant="outline"
              size="sm"
              disabled={!liveReady || loadState === "loading"}
              onClick={() => {
                void handleRefresh()
              }}
            >
              <RefreshCwIcon className={cn("size-4", loadState === "loading" && "animate-spin")} />
              Обновить
            </Button>
            <Button variant="outline" size="sm" onClick={() => setImportOpen(true)}>
              <UploadIcon className="size-4" />
              Импорт
            </Button>
            <Button size="sm" disabled={!liveReady || issueBusy} onClick={() => { setIssueStep(1); setIssueOpen(true) }}>
              <PlusIcon className="size-4" />
              Выпустить сертификат
            </Button>
          </>
        }
      />

      <div className="flex-1 overflow-y-auto p-6">
        <div className="flex flex-col gap-5">
          {isLive && backendStatus === false && (
            <div className="rounded-lg border border-amber-500/30 bg-amber-500/5 px-4 py-3 text-sm text-amber-700 dark:text-amber-300">
              Backend недоступен — live-операции отключены.
            </div>
          )}
          {loadError && isLive && (
            <div className="rounded-lg border border-destructive/30 bg-destructive/5 px-4 py-3 text-sm text-destructive">
              {loadError}
            </div>
          )}

          {(expiring.length > 0 || expired.length > 0) && (
            <div className="flex flex-col gap-2">
              {expired.length > 0 && <CertPartAlertExpired expired={expired} />}
              {expiring.length > 0 && <CertPartAlertExpiring expiring={expiring} />}
            </div>
          )}

          <CertPartKpi displayCerts={displayCerts} expiring={expiring} expired={expired} />

          {liveReady && (
            <CertPartAcmeSettings
              acmeDirectoryUrl={acmeDirectoryUrl}
              setAcmeDirectoryUrl={setAcmeDirectoryUrl}
              acmeZoneId={acmeZoneId}
              setAcmeZoneId={setAcmeZoneId}
              acmeTokenDraft={acmeTokenDraft}
              setAcmeTokenDraft={setAcmeTokenDraft}
              acmeTokenConfigured={acmeTokenConfigured}
              acmeSaveBusy={acmeSaveBusy}
              onTest={() => {
                void handleTestAcme()
              }}
              onSave={() => {
                void handleSaveAcmeSettings()
              }}
            />
          )}

          <DataPageCard>
            <DataPageToolbar
              search={search}
              onSearchChange={setSearch}
              searchPlaceholder="Поиск по имени, CN, эмитенту…"
              segmented={{
                value: statusFilter,
                onChange: setStatusFilter,
                options: [
                  { value: "all", label: "Все" },
                  { value: "valid", label: "Действующие" },
                  { value: "expired", label: "Истёкшие" },
                  { value: "revoked", label: "Отозванные" },
                ],
              }}
              countLabel={`${filtered.length} сертификатов`}
            />
            <CertificatesDataGrid
              certificates={filtered}
              serverMap={serverById}
              isLoading={prefsHydrated && isLive && loadState === "loading" && displayCerts.length === 0}
            />
          </DataPageCard>

          <CertPartReference />
        </div>
      </div>

      <Sheet open={issueOpen} onOpenChange={(v) => { setIssueOpen(v); if (!v) setIssueStep(1) }}>
        <SheetContent side="right" className="w-full sm:max-w-lg flex flex-col gap-0 p-0">
          <SheetHeader className="px-6 pt-6 pb-4 border-b shrink-0">
            <SheetTitle>Выпуск сертификата</SheetTitle>
            <SheetDescription>
              Let&apos;s Encrypt через DNS-01 (Cloudflare) и импорт на выбранный RouterOS.
            </SheetDescription>
          </SheetHeader>

          <Stepper value={issueStep} onValueChange={setIssueStep} className="flex-1 flex flex-col min-h-0 px-6 py-5">
            <StepperNav className="mb-5">
              {[
                { step: 1, title: "Основные" },
                { step: 2, title: "Домены" },
                { step: 3, title: "Импорт" },
                { step: 4, title: "Проверка" },
              ].map(({ step, title }, i, arr) => (
                <StepperItem key={step} step={step}>
                  <StepperTrigger>
                    <StepperIndicator>{step}</StepperIndicator>
                    <StepperTitle className="sr-only">{title}</StepperTitle>
                  </StepperTrigger>
                  {i < arr.length - 1 && <StepperSeparator />}
                </StepperItem>
              ))}
            </StepperNav>
            <StepperPanel className="flex-1 overflow-y-auto">
              {[1, 2, 3, 4].map((s) => (
                <StepperContent key={s} value={s}>
                  <CertPartIssueForm
                    step={s as 1 | 2 | 3 | 4}
                    serverList={serverList}
                    issueServerId={issueServerId}
                    setIssueServerId={setIssueServerId}
                    issueCertName={issueCertName}
                    setIssueCertName={setIssueCertName}
                    issueCommonName={issueCommonName}
                    setIssueCommonName={setIssueCommonName}
                    issueSans={issueSans}
                    setIssueSans={setIssueSans}
                    issueTrustWww={issueTrustWww}
                    setIssueTrustWww={setIssueTrustWww}
                    issueTrustApi={issueTrustApi}
                    setIssueTrustApi={setIssueTrustApi}
                  />
                </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" disabled={issueBusy} />}>
              Отмена
            </SheetClose>
            {issueStep > 1 && (
              <Button variant="outline" className="flex-1" disabled={issueBusy} onClick={() => setIssueStep((s) => s - 1)}>
                Назад
              </Button>
            )}
            {issueStep < 4 ? (
              <Button
                className="flex-1"
                disabled={issueStep === 1 && (!issueServerId || !issueCertName)}
                onClick={() => setIssueStep((s) => s + 1)}
              >
                Далее
              </Button>
            ) : (
              <Button
                className="flex-1"
                disabled={!liveReady || issueBusy}
                onClick={() => { void handleIssue() }}
              >
                {issueBusy ? "Выпуск…" : "Выпустить"}
              </Button>
            )}
          </SheetFooter>
        </SheetContent>
      </Sheet>

      <FileImportDialog
        open={importOpen}
        onOpenChange={setImportOpen}
        title="Импорт сертификата"
        description="Загрузите PEM, CRT или PKCS#12 для импорта на RouterOS"
        accept=".pem,.crt,.cer,.p12,.pfx"
        onImport={async (files) => {
          toast.success(`Файл ${files[0]?.name} готов к импорту на роутер`)
        }}
      />
    </div>
  )
}
