import { Download, RefreshCcw, Upload } from 'lucide-react'
import { useMemo, useState } from 'react'
import { toast } from 'sonner'

import { Badge } from '@/components/ui/badge'
import { Button } from '@/components/ui/button'
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'
import { Input } from '@/components/ui/input'
import { Select } from '@/components/ui/select'
import { adminApi, ApiError } from '@/lib/api'
import type { SiteBackupDocument, SiteBackupImportResponse } from '@/lib/types'

function downloadJson(filename: string, payload: unknown) {
  const blob = new Blob([JSON.stringify(payload, null, 2)], { type: 'application/json' })
  const url = URL.createObjectURL(blob)
  const link = document.createElement('a')
  link.href = url
  link.download = filename
  document.body.appendChild(link)
  link.click()
  link.remove()
  URL.revokeObjectURL(url)
}

export function BackupsPage() {
  const [exporting, setExporting] = useState(false)
  const [importing, setImporting] = useState(false)
  const [importMode, setImportMode] = useState<'merge' | 'replace'>('merge')
  const [selectedFile, setSelectedFile] = useState<File | null>(null)
  const [selectedBackup, setSelectedBackup] = useState<SiteBackupDocument | null>(null)
  const [lastImportResult, setLastImportResult] = useState<SiteBackupImportResponse | null>(null)

  const backupStats = useMemo(() => {
    if (!selectedBackup) {
      return null
    }

    return {
      posts: selectedBackup.posts.length,
      categories: selectedBackup.categories.length,
      tags: selectedBackup.tags.length,
      reviews: selectedBackup.reviews.length,
      friendLinks: selectedBackup.friend_links.length,
      mediaAssets: selectedBackup.media_assets.length,
      storageManifest: selectedBackup.storage_manifest?.length ?? 0,
    }
  }, [selectedBackup])

  return (
    <div className="space-y-6">
      <div className="flex flex-col gap-4 xl:flex-row xl:items-end xl:justify-between">
        <div className="space-y-3">
          <Badge variant="secondary">备份 / 恢复</Badge>
          <div>
            <h2 className="text-3xl font-semibold tracking-tight">全站内容备份</h2>
            <p className="mt-2 max-w-3xl text-sm leading-7 text-muted-foreground">
              导出站点内容、配置、分类标签元数据和媒体元数据。当前不包含对象存储二进制文件，只会附带对象清单。
            </p>
          </div>
        </div>
      </div>

      <div className="grid gap-6 xl:grid-cols-[minmax(0,0.9fr)_minmax(0,1.1fr)]">
        <Card>
          <CardHeader>
            <CardTitle>导出备份</CardTitle>
            <CardDescription>
              一键导出当前内容与配置，建议定期下载到本地或同步到私有仓库 / 对象存储归档目录。
            </CardDescription>
          </CardHeader>
          <CardContent className="space-y-4">
            <div className="rounded-3xl border border-border/70 bg-background/50 p-4 text-sm leading-7 text-muted-foreground">
              <p>导出内容包含：</p>
              <ul className="mt-2 list-disc space-y-1 pl-5">
                <li>站点设置与运行时开关</li>
                <li>Markdown 文章源文件</li>
                <li>分类 / 标签扩展元数据</li>
                <li>评测、友链、媒体元数据</li>
                <li>对象存储文件清单（不含二进制）</li>
              </ul>
            </div>

            <Button
              disabled={exporting}
              onClick={async () => {
                try {
                  setExporting(true)
                  const backup = await adminApi.exportSiteBackup()
                  const exportedAt = backup.exported_at.replaceAll(':', '-').replaceAll('.', '-')
                  downloadJson(`termi-backup-${exportedAt}.json`, backup)
                  toast.success('备份已导出到本地。')
                } catch (error) {
                  toast.error(error instanceof ApiError ? error.message : '导出备份失败。')
                } finally {
                  setExporting(false)
                }
              }}
            >
              <Download className="h-4 w-4" />
              {exporting ? '导出中...' : '下载备份 JSON'}
            </Button>
          </CardContent>
        </Card>

        <Card>
          <CardHeader>
            <CardTitle>导入恢复</CardTitle>
            <CardDescription>
              支持 merge / replace 两种模式；replace 会覆盖当前 markdown 内容与对应元数据，请先确认当前环境是否允许回滚。
            </CardDescription>
          </CardHeader>
          <CardContent className="space-y-5">
            <div className="grid gap-4 md:grid-cols-[220px_minmax(0,1fr)]">
              <Select
                value={importMode}
                onChange={(event) => setImportMode(event.target.value as 'merge' | 'replace')}
              >
                <option value="merge">merge（推荐）</option>
                <option value="replace">replace（覆盖当前内容）</option>
              </Select>
              <Input
                type="file"
                accept="application/json"
                onChange={async (event) => {
                  const file = event.target.files?.item(0) ?? null
                  setSelectedFile(file)
                  setLastImportResult(null)
                  if (!file) {
                    setSelectedBackup(null)
                    return
                  }

                  try {
                    const parsed = JSON.parse(await file.text()) as SiteBackupDocument
                    setSelectedBackup(parsed)
                  } catch {
                    setSelectedBackup(null)
                    toast.error('备份文件不是合法的 JSON。')
                  }
                }}
              />
            </div>

            <div className="rounded-3xl border border-dashed border-border/70 bg-background/40 p-4 text-sm text-muted-foreground">
              <p className="font-medium text-foreground">导入风险提示</p>
              <ul className="mt-2 list-disc space-y-1 pl-5 leading-6">
                <li>replace 会覆盖当前 markdown 源文件，并重建分类 / 标签 / 媒体元数据。</li>
                <li>备份不会恢复对象存储二进制文件，请确保原桶仍可访问，或另行回传图片。</li>
                <li>建议先导出当前环境，再执行恢复操作。</li>
              </ul>
            </div>

            {selectedBackup ? (
              <div className="rounded-3xl border border-border/70 bg-background/50 p-4">
                <div className="flex flex-wrap items-center gap-2">
                  <Badge variant="outline">版本 {selectedBackup.version}</Badge>
                  <Badge variant="outline">导出时间 {selectedBackup.exported_at}</Badge>
                  <Badge variant="secondary">{selectedBackup.includes_storage_binaries ? '包含二进制' : '仅对象清单'}</Badge>
                </div>
                <div className="mt-4 grid gap-3 sm:grid-cols-2 xl:grid-cols-3 text-sm text-muted-foreground">
                  <div>文章：{backupStats?.posts ?? 0}</div>
                  <div>分类：{backupStats?.categories ?? 0}</div>
                  <div>标签：{backupStats?.tags ?? 0}</div>
                  <div>评测：{backupStats?.reviews ?? 0}</div>
                  <div>友链：{backupStats?.friendLinks ?? 0}</div>
                  <div>媒体元数据：{backupStats?.mediaAssets ?? 0}</div>
                  <div>对象清单：{backupStats?.storageManifest ?? 0}</div>
                </div>
                <p className="mt-4 text-sm leading-6 text-muted-foreground">{selectedBackup.warning}</p>
              </div>
            ) : (
              <div className="rounded-3xl border border-dashed border-border/70 bg-background/40 px-5 py-8 text-center text-sm text-muted-foreground">
                {selectedFile ? '当前文件未通过 JSON 校验。' : '选择一个备份 JSON 后，这里会显示导入概览。'}
              </div>
            )}

            <div className="flex flex-wrap items-center gap-3">
              <Button
                disabled={!selectedBackup || importing}
                variant={importMode === 'replace' ? 'danger' : 'default'}
                onClick={async () => {
                  if (!selectedBackup) {
                    return
                  }

                  if (
                    importMode === 'replace' &&
                    !window.confirm('replace 会覆盖当前内容，确认继续吗？')
                  ) {
                    return
                  }

                  try {
                    setImporting(true)
                    const result = await adminApi.importSiteBackup({
                      backup: selectedBackup,
                      mode: importMode,
                    })
                    setLastImportResult(result)
                    toast.success('备份已导入。')
                  } catch (error) {
                    toast.error(error instanceof ApiError ? error.message : '导入备份失败。')
                  } finally {
                    setImporting(false)
                  }
                }}
              >
                <Upload className="h-4 w-4" />
                {importing ? '导入中...' : importMode === 'replace' ? '执行覆盖恢复' : '执行合并恢复'}
              </Button>
              <Button
                variant="outline"
                onClick={() => {
                  setSelectedFile(null)
                  setSelectedBackup(null)
                  setLastImportResult(null)
                }}
              >
                <RefreshCcw className="h-4 w-4" />
                清空选择
              </Button>
            </div>
          </CardContent>
        </Card>
      </div>

      {lastImportResult ? (
        <Card>
          <CardHeader>
            <CardTitle>最近一次恢复结果</CardTitle>
            <CardDescription>模式：{lastImportResult.mode}</CardDescription>
          </CardHeader>
          <CardContent className="grid gap-3 sm:grid-cols-2 xl:grid-cols-4 text-sm text-muted-foreground">
            <div>站点设置：{lastImportResult.site_settings_restored ? '已恢复' : '未恢复'}</div>
            <div>文章写入：{lastImportResult.posts_written}</div>
            <div>分类更新：{lastImportResult.categories_upserted}</div>
            <div>标签更新：{lastImportResult.tags_upserted}</div>
            <div>评测更新：{lastImportResult.reviews_upserted}</div>
            <div>友链更新：{lastImportResult.friend_links_upserted}</div>
            <div>媒体元数据：{lastImportResult.media_assets_upserted}</div>
            <div>对象清单：{lastImportResult.storage_manifest_items}</div>
            <div className="sm:col-span-2 xl:col-span-4">{lastImportResult.warning}</div>
          </CardContent>
        </Card>
      ) : null}
    </div>
  )
}
