import { useCallback, useEffect, useMemo, useRef, useState } from "react"
import {
  Alert,
  Button,
  Card,
  Col,
  Empty,
  Form,
  Input,
  InputNumber,
  Modal,
  Row,
  Select,
  Space,
  Statistic,
  Table,
  Tag,
  Typography,
  type TableColumnsType,
} from "antd"
import {
  CloudDownloadOutlined,
  ClockCircleOutlined,
  DatabaseOutlined,
  DeleteOutlined,
  FileSearchOutlined,
  PlusOutlined,
  ReloadOutlined,
  RollbackOutlined,
} from "@ant-design/icons"
import {
  createInstanceSnapshot,
  createInstanceCronSnapshot,
  deleteInstanceSnapshot,
  deleteInstanceCronSnapshot,
  listInstanceCronSnapshots,
  listInstanceSnapshots,
  restoreInstanceSnapshot,
  type InstanceCronSnapshotItem,
  type InstanceCronSnapshotCreatePayload,
  type InstanceSnapshotItem,
  type InstanceSnapshotMeta,
  type InstanceSnapshotQuery,
  type ServerDetail,
} from "../../api/servers"
import { toast } from "../../utils/toast"
import { UNSUPPORTED_OPERATION_MESSAGE } from "./helpers"

const { Text } = Typography

type Props = {
  server: ServerDetail
  onTaskSubmitted?: () => void
}

type SnapshotTypeFilter = "all" | "snap" | "backup"
type SnapshotOrderby = NonNullable<InstanceSnapshotQuery["orderby"]>
type SnapshotSort = NonNullable<InstanceSnapshotQuery["sort"]>
type SnapshotCreateType = "snap" | "backup"
type CronSnapshotCycle = InstanceCronSnapshotCreatePayload["cycle"]

interface SnapshotFormValues {
  disk_id: number | string
  type: SnapshotCreateType
  remarks?: string
}

interface CronSnapshotFormValues {
  type: SnapshotCreateType
  frequency: number
  cycle: CronSnapshotCycle
  disk: Array<number | string>
  point?: string
}

const DEFAULT_META: InstanceSnapshotMeta = {
  total: 0,
  total_page: 0,
  page: 1,
  per_page: 20,
}

const CRON_CYCLE_LABEL: Record<CronSnapshotCycle, string> = {
  hour: "小时",
  day: "天",
  week: "周",
  month: "月",
}

const CRON_CYCLE_OPTIONS = [
  { value: "hour", label: "小时" },
  { value: "day", label: "天" },
  { value: "week", label: "周" },
  { value: "month", label: "月" },
] satisfies Array<{ value: CronSnapshotCycle; label: string }>

const SNAPSHOT_TYPE_META: Record<string, { label: string; color: string }> = {
  snap: { label: "快照", color: "blue" },
  backup: { label: "备份", color: "purple" },
}

const SNAPSHOT_STATUS_META: Record<string, { label: string; color: string }> = {
  "0": { label: "创建中", color: "processing" },
  "1": { label: "创建完成", color: "success" },
}

const SNAPSHOT_LIMIT_KEYS: Record<SnapshotCreateType, string[]> = {
  snap: [
    "snap_limit",
    "snapshot_limit",
    "snap_num_limit",
    "snapshot_num_limit",
    "max_snap",
    "max_snapshot",
    "max_snap_num",
    "max_snapshot_num",
  ],
  backup: ["backup_limit", "backup_num_limit", "max_backup", "max_backup_num"],
}

const SNAPSHOT_COUNT_KEYS: Record<SnapshotCreateType, string[]> = {
  snap: ["snap_num", "snapshot_num", "snap_count", "snapshot_count"],
  backup: ["backup_num", "backup_count"],
}

function toNumber(value: unknown): number | null {
  if (value === null || value === undefined || value === "") return null
  const n = Number(value)
  return Number.isFinite(n) ? n : null
}

function formatSizeGb(value: unknown) {
  const n = toNumber(value)
  return n === null ? "-" : `${n} GB`
}

function formatNullable(value: unknown) {
  if (value === null || value === undefined || value === "") return "-"
  return String(value)
}

function formatSnapshotDate(value: unknown) {
  if (!value) return "-"
  const date = new Date(String(value).replace(" ", "T"))
  if (Number.isNaN(date.getTime())) return String(value)
  return date.toLocaleString("zh-CN", {
    year: "numeric",
    month: "2-digit",
    day: "2-digit",
    hour: "2-digit",
    minute: "2-digit",
    hour12: false,
  })
}

function snapshotTypeTag(value: unknown) {
  const key = String(value ?? "")
  const meta = SNAPSHOT_TYPE_META[key] ?? { label: key || "未知", color: "default" }
  return <Tag color={meta.color}>{meta.label}</Tag>
}

function snapshotStatusTag(value: unknown) {
  const key = String(value ?? "")
  const meta = SNAPSHOT_STATUS_META[key] ?? { label: key || "未知", color: "default" }
  return <Tag color={meta.color}>{meta.label}</Tag>
}

function snapshotActionLabel(record: InstanceSnapshotItem) {
  return record.type === "backup" ? "备份" : "快照"
}

function isSnapshotReady(record: InstanceSnapshotItem) {
  const status = String(record.status ?? "")
  return ["1", "2", "success", "finished", "done"].includes(status)
}

function readNumberFromRecord(source: unknown, keys: string[]) {
  if (!source || typeof source !== "object") return null
  const record = source as Record<string, unknown>
  for (const key of keys) {
    const value = toNumber(record[key])
    if (value !== null) return value
  }
  return null
}

function snapshotLimitFromUpstream(upstream: ServerDetail["upstream"], type: SnapshotCreateType) {
  if (!upstream) return null
  const candidates: unknown[] = [upstream]
  for (const key of ["limits", "limit", "quota", "quotas", "snapshot", "snapshots", "backup"]) {
    const nested = upstream[key]
    if (nested && typeof nested === "object") candidates.push(nested)
  }
  for (const candidate of candidates) {
    const value = readNumberFromRecord(candidate, SNAPSHOT_LIMIT_KEYS[type])
    if (value !== null) return value
  }
  return null
}

function snapshotCountFromDisk(disk: unknown, type: SnapshotCreateType) {
  return readNumberFromRecord(disk, SNAPSHOT_COUNT_KEYS[type])
}

function diskLabel(disk: Record<string, unknown>) {
  const type = String(disk.type ?? "") === "system" || String(disk.dev ?? "").toLowerCase() === "vda"
    ? "系统盘"
    : "数据盘"
  const name = formatNullable(disk.remarks || disk.name || disk.dev || disk.id)
  const size = formatSizeGb(disk.size)
  return `${type} / ${name} / ${size}`
}

function formatCronFrequency(item: InstanceCronSnapshotItem) {
  const frequency = toNumber(item.frequency) ?? 1
  const cycle = String(item.cycle ?? "day") as CronSnapshotCycle
  return `每 ${frequency} ${CRON_CYCLE_LABEL[cycle] ?? cycle}`
}

function cronDiskNames(value: InstanceCronSnapshotItem["disk"]) {
  if (!Array.isArray(value) || value.length === 0) return <Text type="secondary">-</Text>
  return (
    <Space direction="vertical" size={2}>
      {value.map((disk, index) => (
        <Text key={`${disk.id ?? index}-${disk.name ?? ""}`} style={{ fontSize: 12 }}>
          {formatNullable(disk.name || `磁盘 ${index + 1}`)}
        </Text>
      ))}
    </Space>
  )
}

function nextSnapshotNames(value: InstanceSnapshotItem["next"]) {
  if (!Array.isArray(value) || value.length === 0) return <Text type="secondary">-</Text>
  return (
    <Space direction="vertical" size={2}>
      {value.map((item, index) => (
        <Text key={`${item.id ?? index}-${item.name ?? ""}`} style={{ fontSize: 12 }}>
          {formatNullable(item.name)}
        </Text>
      ))}
    </Space>
  )
}

export default function BackupTab({ server, onTaskSubmitted }: Props) {
  const [form] = Form.useForm<SnapshotFormValues>()
  const [cronForm] = Form.useForm<CronSnapshotFormValues>()
  const [items, setItems] = useState<InstanceSnapshotItem[]>([])
  const [cronItems, setCronItems] = useState<InstanceCronSnapshotItem[]>([])
  const [snapshotSupportMessage, setSnapshotSupportMessage] = useState<string | null>(null)
  const [cronSupportMessage, setCronSupportMessage] = useState<string | null>(null)
  const [meta, setMeta] = useState<InstanceSnapshotMeta>(DEFAULT_META)
  const [loading, setLoading] = useState(false)
  const [cronLoading, setCronLoading] = useState(false)
  const [createOpen, setCreateOpen] = useState(false)
  const [cronOpen, setCronOpen] = useState(false)
  const [creating, setCreating] = useState(false)
  const [cronSaving, setCronSaving] = useState(false)
  const [snapshotActionLoading, setSnapshotActionLoading] = useState<string | null>(null)
  const [cronActionLoading, setCronActionLoading] = useState<string | null>(null)
  const autoRefreshTimerRef = useRef<number | null>(null)
  const autoRefreshUntilRef = useRef(0)
  const [page, setPage] = useState(1)
  const [perPage, setPerPage] = useState(20)
  const [orderby, setOrderby] = useState<SnapshotOrderby>("id")
  const [sort, setSort] = useState<SnapshotSort>("desc")
  const [typeFilter, setTypeFilter] = useState<SnapshotTypeFilter>("all")
  const [searchInput, setSearchInput] = useState("")
  const [search, setSearch] = useState("")
  const disks = Array.isArray(server.upstream?.disk)
    ? server.upstream.disk
      .filter((disk) => disk.id !== null && disk.id !== undefined)
      .map((disk) => disk as Record<string, unknown>)
    : []
  const selectedType = (Form.useWatch("type", form) ?? "snap") as SnapshotCreateType
  const selectedCronType = (Form.useWatch("type", cronForm) ?? "snap") as SnapshotCreateType
  const selectedDiskId = Form.useWatch("disk_id", form)
  const selectedDisk = disks.find((disk) => String(disk.id) === String(selectedDiskId))
  const selectedLimit = snapshotLimitFromUpstream(server.upstream, selectedType)
  const selectedCount = selectedDisk ? snapshotCountFromDisk(selectedDisk, selectedType) : null
  const selectedLimitReached = selectedLimit !== null && selectedCount !== null && selectedCount >= selectedLimit
  const snapLimit = snapshotLimitFromUpstream(server.upstream, "snap")
  const backupLimit = snapshotLimitFromUpstream(server.upstream, "backup")
  const snapUsed = disks.reduce((sum, disk) => sum + (snapshotCountFromDisk(disk, "snap") ?? 0), 0)

  const loadSnapshots = useCallback(async (silent = false) => {
    if (!server.upstream_id) {
      setItems([])
      setMeta(DEFAULT_META)
      setSnapshotSupportMessage(null)
      return
    }

    if (!silent) setLoading(true)
    try {
      const params: InstanceSnapshotQuery = {
        page,
        per_page: perPage,
        orderby,
        sort,
      }
      const keyword = search.trim()
      if (keyword) params.search = keyword
      if (typeFilter !== "all") params.type = typeFilter

      const res = await listInstanceSnapshots(server.id, params)
      const data = res.data.data
      if (data?.supported === false) {
        setItems([])
        setMeta(data.meta ?? DEFAULT_META)
        setSnapshotSupportMessage(data.message || UNSUPPORTED_OPERATION_MESSAGE)
        return
      }
      setSnapshotSupportMessage(null)
      setItems(data?.list ?? [])
      setMeta(data?.meta ?? DEFAULT_META)
    } catch (error: unknown) {
      const detail = (error as { response?: { data?: { detail?: string } } })?.response?.data?.detail
      if (!silent) toast.error(detail || "备份列表加载失败")
    } finally {
      if (!silent) setLoading(false)
    }
  }, [orderby, page, perPage, search, server.id, server.upstream_id, sort, typeFilter])

  const loadCronSnapshots = useCallback(async (silent = false) => {
    if (!server.upstream_id) {
      setCronItems([])
      setCronSupportMessage(null)
      return
    }

    if (!silent) setCronLoading(true)
    try {
      const res = await listInstanceCronSnapshots(server.id)
      const data = res.data.data
      if (Array.isArray(data)) {
        setCronSupportMessage(null)
        setCronItems(data)
        return
      }
      if (data?.supported === false) {
        setCronSupportMessage(data.message || UNSUPPORTED_OPERATION_MESSAGE)
        setCronItems(data.list ?? [])
        return
      }
      setCronSupportMessage(null)
      setCronItems(data?.list ?? [])
    } catch (error: unknown) {
      const detail = (error as { response?: { data?: { detail?: string } } })?.response?.data?.detail
      if (!silent) toast.error(detail || "定时任务加载失败")
    } finally {
      if (!silent) setCronLoading(false)
    }
  }, [server.id, server.upstream_id])

  useEffect(() => {
    const timer = window.setTimeout(() => {
      void loadSnapshots()
      void loadCronSnapshots()
    }, 0)
    return () => window.clearTimeout(timer)
  }, [loadCronSnapshots, loadSnapshots])

  const stopAutoRefresh = useCallback(() => {
    if (autoRefreshTimerRef.current !== null) {
      window.clearTimeout(autoRefreshTimerRef.current)
      autoRefreshTimerRef.current = null
    }
  }, [])

  const scheduleAutoRefresh = useCallback(() => {
    stopAutoRefresh()
    const tick = () => {
      void loadSnapshots(true)
      void loadCronSnapshots(true)
      onTaskSubmitted?.()
      if (Date.now() < autoRefreshUntilRef.current) {
        autoRefreshTimerRef.current = window.setTimeout(tick, 4000)
      } else {
        autoRefreshTimerRef.current = null
      }
    }
    autoRefreshTimerRef.current = window.setTimeout(tick, 1500)
  }, [loadCronSnapshots, loadSnapshots, onTaskSubmitted, stopAutoRefresh])

  const startAutoRefresh = useCallback(() => {
    autoRefreshUntilRef.current = Date.now() + 60000
    void loadSnapshots(true)
    void loadCronSnapshots(true)
    onTaskSubmitted?.()
    scheduleAutoRefresh()
  }, [loadCronSnapshots, loadSnapshots, onTaskSubmitted, scheduleAutoRefresh])

  useEffect(() => {
    return () => stopAutoRefresh()
  }, [stopAutoRefresh])

  const openCreateModal = (type: SnapshotCreateType) => {
    const firstDiskId = disks[0]?.id
    form.setFieldsValue({
      type,
      remarks: "",
      ...(firstDiskId !== undefined ? { disk_id: firstDiskId as number | string } : {}),
    })
    setCreateOpen(true)
  }

  const openCronModal = (type: SnapshotCreateType) => {
    const firstDiskId = disks[0]?.id
    cronForm.setFieldsValue({
      type,
      frequency: 1,
      cycle: "day",
      point: "00:00",
      disk: firstDiskId !== undefined ? [firstDiskId as number | string] : [],
    })
    setCronOpen(true)
  }

  const submitCreateSnapshot = async () => {
    try {
      const values = await form.validateFields()
      setCreating(true)
      const remarks = values.remarks?.trim()
      await createInstanceSnapshot(server.id, values.disk_id, {
        type: values.type,
        ...(remarks ? { remarks, name: remarks } : {}),
      })
      toast.success(`${values.type === "snap" ? "快照" : "备份"}创建任务已下发`)
      setCreateOpen(false)
      startAutoRefresh()
    } catch (error: unknown) {
      if ((error as { errorFields?: unknown[] })?.errorFields) return
      const detail = (error as { response?: { data?: { detail?: string } } })?.response?.data?.detail
      toast.error(detail || "创建备份/快照失败")
    } finally {
      setCreating(false)
    }
  }

  const confirmRestoreSnapshot = useCallback((record: InstanceSnapshotItem) => {
    const snapshotId = record.id
    const label = snapshotActionLabel(record)
    Modal.confirm({
      title: `还原${label}`,
      content: `还原会把关联磁盘恢复到该${label}创建时的状态，当前磁盘数据可能被覆盖。请确认重要数据已经保存。`,
      okText: "确认还原",
      cancelText: "取消",
      okButtonProps: { danger: true },
      onOk: async () => {
        const loadingKey = `restore-${snapshotId}`
        setSnapshotActionLoading(loadingKey)
        try {
          await restoreInstanceSnapshot(server.id, snapshotId)
          toast.success(`${label}还原任务已下发`)
          startAutoRefresh()
        } catch (error: unknown) {
          const detail = (error as { response?: { data?: { detail?: string } } })?.response?.data?.detail
          toast.error(detail || `${label}还原失败`)
          throw error
        } finally {
          setSnapshotActionLoading(null)
        }
      },
    })
  }, [server.id, startAutoRefresh])

  const confirmDeleteSnapshot = useCallback((record: InstanceSnapshotItem) => {
    const snapshotId = record.id
    const label = snapshotActionLabel(record)
    Modal.confirm({
      title: `删除${label}`,
      content: `删除后将无法继续使用该${label}进行还原。该操作只删除上游备份/快照记录，不会删除当前云服务器。`,
      okText: "确认删除",
      cancelText: "取消",
      okButtonProps: { danger: true },
      onOk: async () => {
        const loadingKey = `delete-${snapshotId}`
        setSnapshotActionLoading(loadingKey)
        try {
          await deleteInstanceSnapshot(server.id, snapshotId)
          toast.success(`${label}删除任务已下发`)
          startAutoRefresh()
        } catch (error: unknown) {
          const detail = (error as { response?: { data?: { detail?: string } } })?.response?.data?.detail
          toast.error(detail || `${label}删除失败`)
          throw error
        } finally {
          setSnapshotActionLoading(null)
        }
      },
    })
  }, [server.id, startAutoRefresh])

  const submitCronSnapshot = async () => {
    try {
      const values = await cronForm.validateFields()
      setCronSaving(true)
      await createInstanceCronSnapshot(server.id, {
        type: values.type,
        frequency: values.frequency,
        cycle: values.cycle,
        disk: values.disk.map((id) => Number(id)).filter((id) => Number.isFinite(id)),
        ...(values.point ? { point: values.point } : {}),
      })
      toast.success(`${values.type === "snap" ? "定时快照" : "定时备份"}已添加`)
      setCronOpen(false)
      startAutoRefresh()
    } catch (error: unknown) {
      if ((error as { errorFields?: unknown[] })?.errorFields) return
      const detail = (error as { response?: { data?: { detail?: string } } })?.response?.data?.detail
      toast.error(detail || "添加定时任务失败")
    } finally {
      setCronSaving(false)
    }
  }

  const confirmDeleteCronSnapshot = useCallback((record: InstanceCronSnapshotItem) => {
    const cronId = record.id
    const label = record.type === "backup" ? "定时备份" : "定时快照"
    Modal.confirm({
      title: `删除${label}`,
      content: `删除后将不再按该规则自动创建${record.type === "backup" ? "备份" : "快照"}。已有备份/快照不会被删除。`,
      okText: "确认删除",
      cancelText: "取消",
      okButtonProps: { danger: true },
      onOk: async () => {
        setCronActionLoading(`delete-${cronId}`)
        try {
          await deleteInstanceCronSnapshot(server.id, cronId)
          toast.success(`${label}已删除`)
          startAutoRefresh()
        } catch (error: unknown) {
          const detail = (error as { response?: { data?: { detail?: string } } })?.response?.data?.detail
          toast.error(detail || `${label}删除失败`)
          throw error
        } finally {
          setCronActionLoading(null)
        }
      },
    })
  }, [server.id, startAutoRefresh])

  const columns = useMemo<TableColumnsType<InstanceSnapshotItem>>(() => [
    {
      title: "类型",
      dataIndex: "type",
      width: 90,
      render: snapshotTypeTag,
    },
    {
      title: "大小",
      dataIndex: "size",
      width: 110,
      render: formatSizeGb,
    },
    {
      title: "文件名",
      dataIndex: "name",
      width: 220,
      ellipsis: true,
      render: formatNullable,
    },
    {
      title: "状态",
      dataIndex: "status",
      width: 110,
      render: snapshotStatusTag,
    },
    {
      title: "创建时间",
      dataIndex: "create_time",
      width: 180,
      render: formatSnapshotDate,
    },
    {
      title: "备注名",
      dataIndex: "remarks",
      width: 150,
      ellipsis: true,
      render: formatNullable,
    },
    {
      title: "关联磁盘文件",
      dataIndex: "disk_name",
      width: 180,
      ellipsis: true,
      render: formatNullable,
    },
    {
      title: "关联磁盘备注",
      dataIndex: "disk_remarks",
      width: 160,
      ellipsis: true,
      render: formatNullable,
    },
    {
      title: "下级快照文件名",
      dataIndex: "next",
      width: 220,
      ellipsis: true,
      render: nextSnapshotNames,
    },
    {
      title: "操作",
      key: "actions",
      width: 170,
      fixed: "right",
      render: (_, record) => {
        const snapshotId = record.id
        const actionDisabled = snapshotActionLoading !== null
        return (
          <Space size={8}>
            <Button
              size="small"
              icon={<RollbackOutlined />}
              disabled={actionDisabled || !isSnapshotReady(record)}
              loading={snapshotActionLoading === `restore-${snapshotId}`}
              onClick={() => confirmRestoreSnapshot(record)}
            >
              还原
            </Button>
            <Button
              size="small"
              danger
              icon={<DeleteOutlined />}
              disabled={actionDisabled}
              loading={snapshotActionLoading === `delete-${snapshotId}`}
              onClick={() => confirmDeleteSnapshot(record)}
            >
              删除
            </Button>
          </Space>
        )
      },
    },
  ], [confirmDeleteSnapshot, confirmRestoreSnapshot, snapshotActionLoading])

  const cronColumns = useMemo<TableColumnsType<InstanceCronSnapshotItem>>(() => [
    {
      title: "类型",
      dataIndex: "type",
      width: 100,
      render: snapshotTypeTag,
    },
    {
      title: "频率",
      key: "frequency",
      width: 130,
      render: (_, record) => formatCronFrequency(record),
    },
    {
      title: "时间点",
      dataIndex: "point",
      width: 110,
      render: formatNullable,
    },
    {
      title: "关联磁盘",
      dataIndex: "disk",
      width: 240,
      render: cronDiskNames,
    },
    {
      title: "创建时间",
      dataIndex: "create_time",
      width: 180,
      render: formatSnapshotDate,
    },
    {
      title: "操作",
      key: "actions",
      width: 100,
      fixed: "right",
      render: (_, record) => (
        <Button
          size="small"
          danger
          icon={<DeleteOutlined />}
          disabled={cronActionLoading !== null}
          loading={cronActionLoading === `delete-${record.id}`}
          onClick={() => confirmDeleteCronSnapshot(record)}
        >
          删除
        </Button>
      ),
    },
  ], [confirmDeleteCronSnapshot, cronActionLoading])

  if (!server.upstream_id) {
    return <Empty image={Empty.PRESENTED_IMAGE_SIMPLE} description="实例尚未开通，暂无备份信息" />
  }

  if (snapshotSupportMessage && cronSupportMessage) {
    return <Empty image={Empty.PRESENTED_IMAGE_SIMPLE} description={snapshotSupportMessage || cronSupportMessage} />
  }

  return (
    <Space direction="vertical" size={16} style={{ width: "100%" }}>
      <Row gutter={[16, 16]}>
        <Col xs={24} sm={12} xl={5}>
          <Card variant="outlined">
            <Statistic title="总条数" value={meta.total} prefix={<FileSearchOutlined />} />
          </Card>
        </Col>
        <Col xs={24} sm={12} xl={5}>
          <Card variant="outlined">
            <Statistic title="总页数" value={meta.total_page} prefix={<DatabaseOutlined />} />
          </Card>
        </Col>
        <Col xs={24} sm={12} xl={4}>
          <Card variant="outlined">
            <Statistic title="当前页" value={meta.page} prefix={<CloudDownloadOutlined />} />
          </Card>
        </Col>
        <Col xs={24} sm={12} xl={5}>
          <Card variant="outlined">
            <Statistic title="快照数量 / 上限" value={`${snapUsed} / ${snapLimit ?? "无限"}`} />
          </Card>
        </Col>
        <Col xs={24} sm={12} xl={5}>
          <Card variant="outlined">
            <Statistic title="备份上限" value={backupLimit ?? "无限"} />
          </Card>
        </Col>
      </Row>

      <Card
        variant="outlined"
        styles={{ body: { display: "flex", flexDirection: "column", gap: 16 } }}
      >
        <Alert
          showIcon
          type="info"
          message="快照和备份不会存到当前云服务器系统盘内；快照适合短期快速回滚，备份适合长期保留，实际存储位置由上游云平台配置决定。"
        />

        <Row gutter={[12, 12]} align="middle" justify="space-between">
          <Col flex="auto">
            <Space direction="vertical" size={2}>
              <Text strong>实例备份列表</Text>
              <Text type="secondary">查看当前实例的快照与备份，支持按文件名、备注和类型筛选。</Text>
            </Space>
          </Col>
          <Col>
            <Space>
              <Button type="primary" icon={<PlusOutlined />} disabled={disks.length === 0 || Boolean(snapshotSupportMessage)} onClick={() => openCreateModal("snap")}>
                创建快照
              </Button>
              <Button icon={<CloudDownloadOutlined />} disabled={disks.length === 0 || Boolean(snapshotSupportMessage)} onClick={() => openCreateModal("backup")}>
                创建备份
              </Button>
              <Button icon={<ReloadOutlined />} loading={loading} onClick={() => void loadSnapshots()}>
                刷新
              </Button>
            </Space>
          </Col>
        </Row>

        <Row gutter={[12, 12]} align="middle">
          <Col xs={24} md={8}>
            <Input.Search
              allowClear
              value={searchInput}
              placeholder="搜索文件名或备注"
              enterButton="搜索"
              onChange={(event) => {
                const value = event.target.value
                setSearchInput(value)
                if (!value) {
                  setPage(1)
                  setSearch("")
                }
              }}
              onSearch={(value) => {
                setPage(1)
                setSearch(value)
              }}
            />
          </Col>
          <Col xs={12} md={4}>
            <Select
              value={typeFilter}
              style={{ width: "100%" }}
              options={[
                { value: "all", label: "全部类型" },
                { value: "snap", label: "快照" },
                { value: "backup", label: "备份" },
              ]}
              onChange={(value) => {
                setPage(1)
                setTypeFilter(value)
              }}
            />
          </Col>
          <Col xs={12} md={4}>
            <Select
              value={orderby}
              style={{ width: "100%" }}
              options={[
                { value: "id", label: "按 ID" },
                { value: "size", label: "按大小" },
                { value: "name", label: "按文件名" },
                { value: "type", label: "按类型" },
              ]}
              onChange={(value) => {
                setPage(1)
                setOrderby(value)
              }}
            />
          </Col>
          <Col xs={12} md={4}>
            <Select
              value={sort}
              style={{ width: "100%" }}
              options={[
                { value: "desc", label: "降序" },
                { value: "asc", label: "升序" },
              ]}
              onChange={(value) => {
                setPage(1)
                setSort(value)
              }}
            />
          </Col>
        </Row>

        <Table
          rowKey={(record) => String(record.id)}
          columns={columns}
          dataSource={items}
          loading={loading}
          locale={{ emptyText: <Empty image={Empty.PRESENTED_IMAGE_SIMPLE} description={snapshotSupportMessage || "暂无备份/快照记录"} /> }}
          pagination={{
            current: meta.page || page,
            pageSize: meta.per_page || perPage,
            total: meta.total,
            showSizeChanger: true,
            showTotal: (total) => `共 ${total} 条`,
            pageSizeOptions: [10, 20, 50, 100],
            onChange: (nextPage, nextPageSize) => {
              setPage(nextPage)
              setPerPage(nextPageSize)
            },
          }}
          scroll={{ x: 1490 }}
        />
      </Card>

      <Card
        variant="outlined"
        styles={{ body: { display: "flex", flexDirection: "column", gap: 16 } }}
      >
        <Row gutter={[12, 12]} align="middle" justify="space-between">
          <Col flex="auto">
            <Space direction="vertical" size={2}>
              <Text strong>定时备份/快照</Text>
              <Text type="secondary">按固定周期自动创建快照或备份，适合长期保护关键磁盘。</Text>
            </Space>
          </Col>
          <Col>
            <Space>
              <Button type="primary" icon={<ClockCircleOutlined />} disabled={disks.length === 0 || Boolean(cronSupportMessage)} onClick={() => openCronModal("snap")}>
                添加定时快照
              </Button>
              <Button icon={<CloudDownloadOutlined />} disabled={disks.length === 0 || Boolean(cronSupportMessage)} onClick={() => openCronModal("backup")}>
                添加定时备份
              </Button>
              <Button icon={<ReloadOutlined />} loading={cronLoading} onClick={() => void loadCronSnapshots()}>
                刷新
              </Button>
            </Space>
          </Col>
        </Row>

        <Table
          rowKey={(record) => String(record.id)}
          columns={cronColumns}
          dataSource={cronItems}
          loading={cronLoading}
          pagination={false}
          locale={{ emptyText: <Empty image={Empty.PRESENTED_IMAGE_SIMPLE} description={cronSupportMessage || "暂无定时备份/快照任务"} /> }}
          scroll={{ x: 860 }}
        />
      </Card>

      <Modal
        title={selectedType === "snap" ? "创建快照" : "创建备份"}
        open={createOpen}
        onCancel={() => setCreateOpen(false)}
        onOk={submitCreateSnapshot}
        confirmLoading={creating}
        okButtonProps={{ disabled: selectedLimitReached }}
        okText={selectedType === "snap" ? "创建快照" : "创建备份"}
        cancelText="取消"
      >
        <Form form={form} layout="vertical" requiredMark="optional">
          <Form.Item name="type" hidden rules={[{ required: true }]}>
            <Input />
          </Form.Item>
          <Form.Item
            label="关联磁盘"
            name="disk_id"
            rules={[{ required: true, message: "请选择磁盘" }]}
          >
            <Select
              showSearch
              optionFilterProp="label"
              options={disks.map((disk) => ({
                value: disk.id as number | string,
                label: diskLabel(disk),
              }))}
            />
          </Form.Item>
          <Form.Item
            label="备注"
            name="remarks"
            rules={[{ max: 128, message: "备注名不能超过 128 个字符" }]}
          >
            <Input placeholder="用于识别该备份或快照" maxLength={128} showCount />
          </Form.Item>
          <Card size="small" variant="outlined">
            <Space direction="vertical" size={4} style={{ width: "100%" }}>
              <Text type="secondary">
                当前磁盘：{selectedDisk ? diskLabel(selectedDisk) : "-"}
              </Text>
              <Text type={selectedLimitReached ? "danger" : "secondary"}>
                {selectedType === "snap" ? "快照" : "备份"}数量：
                {selectedCount === null ? "上游未返回当前数量" : selectedCount}
                {" / "}
                {selectedLimit === null ? "无限" : selectedLimit}
              </Text>
              <Text type="secondary">
                快照和备份均由上游云平台存储，不会占用当前云服务器系统盘空间；快照偏向快速回滚，备份偏向长期恢复。
              </Text>
            </Space>
          </Card>
        </Form>
      </Modal>

      <Modal
        title={selectedCronType === "snap" ? "添加定时快照" : "添加定时备份"}
        open={cronOpen}
        onCancel={() => setCronOpen(false)}
        onOk={submitCronSnapshot}
        confirmLoading={cronSaving}
        okText="保存"
        cancelText="取消"
      >
        <Form form={cronForm} layout="vertical" requiredMark="optional">
          <Form.Item name="type" hidden rules={[{ required: true }]}>
            <Input />
          </Form.Item>
          <Form.Item
            label="关联磁盘"
            name="disk"
            rules={[{ required: true, message: "请选择至少一块磁盘" }]}
          >
            <Select
              mode="multiple"
              showSearch
              optionFilterProp="label"
              placeholder="请选择需要定时保护的磁盘"
              options={disks.map((disk) => ({
                value: disk.id as number | string,
                label: diskLabel(disk),
              }))}
            />
          </Form.Item>
          <Row gutter={12}>
            <Col span={10}>
              <Form.Item
                label="频率"
                name="frequency"
                rules={[{ required: true, message: "请输入频率" }]}
              >
                <InputNumber min={1} max={999} precision={0} style={{ width: "100%" }} />
              </Form.Item>
            </Col>
            <Col span={14}>
              <Form.Item
                label="周期"
                name="cycle"
                rules={[{ required: true, message: "请选择周期" }]}
              >
                <Select options={CRON_CYCLE_OPTIONS} />
              </Form.Item>
            </Col>
          </Row>
          <Form.Item
            label="时间点"
            name="point"
            rules={[{ pattern: /^([01]\d|2[0-3]):[0-5]\d$/, message: "时间格式应为 00:00-23:59" }]}
          >
            <Input placeholder="例如 03:30" />
          </Form.Item>
          <Alert
            showIcon
            type="info"
            message="定时任务由上游云平台执行；这里保存的是自动创建快照或备份的规则，不会立即生成新的备份。"
          />
        </Form>
      </Modal>
    </Space>
  )
}
