import { useEffect, useMemo, useState } from 'react'
import { toast } from 'sonner'
import {
  ExternalLink,
  FileSpreadsheet,
  FileText,
  LayoutPanelTop,
  ListTree,
  MoreHorizontal,
  Pencil,
  Plus,
  Presentation,
  Search,
  Trash2,
  Upload,
  X
} from 'lucide-react'

import { DashboardFooter } from '@renderer/components/dashboard/DashboardFooter'
import {
  AlertDialog,
  AlertDialogAction,
  AlertDialogCancel,
  AlertDialogContent,
  AlertDialogDescription,
  AlertDialogFooter,
  AlertDialogHeader,
  AlertDialogTitle
} from '@renderer/components/ui/alert-dialog'
import { Badge } from '@renderer/components/ui/badge'
import { Button } from '@renderer/components/ui/button'
import {
  Card,
  CardContent,
  CardDescription,
  CardHeader,
  CardTitle
} from '@renderer/components/ui/card'
import {
  Dialog,
  DialogContent,
  DialogDescription,
  DialogFooter,
  DialogHeader,
  DialogTitle
} from '@renderer/components/ui/dialog'
import {
  Drawer,
  DrawerContent,
  DrawerDescription,
  DrawerHeader,
  DrawerTitle
} from '@renderer/components/ui/drawer'
import {
  DropdownMenu,
  DropdownMenuContent,
  DropdownMenuItem,
  DropdownMenuLabel,
  DropdownMenuSeparator,
  DropdownMenuTrigger
} from '@renderer/components/ui/dropdown-menu'
import { Input } from '@renderer/components/ui/input'
import { ScrollArea } from '@renderer/components/ui/scroll-area'
import { Select } from '@renderer/components/ui/select'
import type {
  TemplateFieldMapping,
  TemplateItem,
  TemplatePlaceholder,
  TemplateSemester,
  TemplateType
} from '@renderer/types/app'

type TemplateForm = {
  id?: string
  name: string
  grade: string
  semester: TemplateSemester
  type: TemplateType
  sourceFilePath: string
  fileName: string
  fieldMappings: TemplateFieldMapping
}

const emptyTemplateForm: TemplateForm = {
  name: '',
  grade: '',
  semester: 'first',
  type: 'portrait',
  sourceFilePath: '',
  fileName: '',
  fieldMappings: {}
}

type MappingDialogState = {
  open: boolean
  template: TemplateItem | null
  placeholders: TemplatePlaceholder[]
  fieldMappings: TemplateFieldMapping
}

const emptyMappingDialog: MappingDialogState = {
  open: false,
  template: null,
  placeholders: [],
  fieldMappings: {}
}

const templateTypeOptions: Array<{ value: TemplateType; label: string }> = [
  { value: 'portrait', label: '竖版' },
  { value: 'landscape', label: '横版' }
]

const templateSemesterOptions: Array<{ value: TemplateSemester; label: string }> = [
  { value: 'first', label: '上学期' },
  { value: 'second', label: '下学期' }
]

const studentFieldOptions: Array<{ key: string; label: string; kind: 'text' | 'image' }> = [
  { key: 'name', label: '中文姓名', kind: 'text' },
  { key: 'englishName', label: '英文名', kind: 'text' },
  { key: 'gender', label: '性别', kind: 'text' },
  { key: 'birthday', label: '生日', kind: 'text' },
  { key: 'zodiac', label: '生肖', kind: 'text' },
  { key: 'friends', label: '好朋友', kind: 'text' },
  { key: 'hobbies', label: '兴趣爱好', kind: 'text' },
  { key: 'favoriteGames', label: '喜欢的游戏', kind: 'text' },
  { key: 'favoriteFoods', label: '喜欢的食物', kind: 'text' },
  { key: 'traits', label: '表现特征', kind: 'text' },
  { key: 'comment', label: '成长评语', kind: 'text' },
  { key: 'className', label: '班级名称', kind: 'text' },
  { key: 'familyPhoto', label: '班级图片', kind: 'image' }
]

function getTemplateTypeLabel(type: TemplateType): string {
  return type === 'landscape' ? '横版' : '竖版'
}

function getTemplateSemesterLabel(semester: TemplateSemester): string {
  return semester === 'second' ? '下学期' : '上学期'
}

function isSupportedTemplateFile(fileName: string): boolean {
  return /\.(ppt|pptx|doc|docx|xls|xlsx)$/i.test(fileName)
}

function getFileKind(extension: string): string {
  if (['.ppt', '.pptx'].includes(extension)) {
    return 'PPT'
  }

  if (['.doc', '.docx'].includes(extension)) {
    return 'Word'
  }

  if (['.xls', '.xlsx'].includes(extension)) {
    return 'Excel'
  }

  return '文件'
}

function getFileIcon(extension: string): typeof Presentation {
  if (['.ppt', '.pptx'].includes(extension)) {
    return Presentation
  }

  if (['.xls', '.xlsx'].includes(extension)) {
    return FileSpreadsheet
  }

  return FileText
}

function getStudentFieldLabel(fieldKey?: string): string {
  return studentFieldOptions.find((field) => field.key === fieldKey)?.label ?? ''
}

function getCompatibleStudentFields(kind: TemplatePlaceholder['kind']): typeof studentFieldOptions {
  return studentFieldOptions.filter((field) => field.kind === kind)
}

function formatDateTime(value: string): string {
  const date = new Date(value)

  if (Number.isNaN(date.getTime())) {
    return value
  }

  return date.toLocaleString('zh-CN', {
    year: 'numeric',
    month: '2-digit',
    day: '2-digit',
    hour: '2-digit',
    minute: '2-digit'
  })
}

export function TemplatePage(): React.JSX.Element {
  const [templates, setTemplates] = useState<TemplateItem[]>([])
  const [query, setQuery] = useState('')
  const [drawerOpen, setDrawerOpen] = useState(false)
  const [templateForm, setTemplateForm] = useState<TemplateForm>(emptyTemplateForm)
  const [mappingDialog, setMappingDialog] = useState<MappingDialogState>(emptyMappingDialog)
  const [saving, setSaving] = useState(false)
  const [mappingSaving, setMappingSaving] = useState(false)
  const [openingId, setOpeningId] = useState('')
  const [deletingTemplate, setDeletingTemplate] = useState<TemplateItem | null>(null)
  const [isDragActive, setIsDragActive] = useState(false)

  useEffect(() => {
    async function loadTemplatesOnMount(): Promise<void> {
      const response = await window.api.loadTemplates()

      if (!response.ok) {
        showError('读取模板失败', response.message)
        return
      }

      setTemplates(response.templates)
    }

    loadTemplatesOnMount()
  }, [])

  const filteredTemplates = useMemo(() => {
    const keyword = query.trim().toLowerCase()

    if (!keyword) {
      return templates
    }

    return templates.filter((template) =>
      [
        template.name,
        template.grade,
        getTemplateSemesterLabel(template.semester),
        getTemplateTypeLabel(template.type),
        template.originalFileName,
        getFileKind(template.fileExtension)
      ]
        .join(' ')
        .toLowerCase()
        .includes(keyword)
    )
  }, [templates, query])

  function showSuccess(message: string, description?: string): void {
    toast.success(message, description ? { description } : undefined)
  }

  function showError(message: string, description?: string): void {
    toast.error(message, description ? { description } : undefined)
  }

  function openCreateDrawer(): void {
    setTemplateForm(emptyTemplateForm)
    setDrawerOpen(true)
  }

  function openEditDrawer(template: TemplateItem): void {
    setTemplateForm({
      id: template.id,
      name: template.name,
      grade: template.grade,
      semester: template.semester,
      type: template.type,
      sourceFilePath: template.filePath,
      fileName: template.originalFileName,
      fieldMappings: template.fieldMappings
    })
    setDrawerOpen(true)
  }

  async function openMappingDialog(template: TemplateItem): Promise<void> {
    setMappingDialog({
      open: true,
      template,
      placeholders: template.placeholders,
      fieldMappings: template.fieldMappings
    })

    const placeholders = await parsePlaceholdersForFile(template.filePath)

    setMappingDialog((currentDialog) => {
      if (currentDialog.template?.id !== template.id) {
        return currentDialog
      }

      return {
        ...currentDialog,
        placeholders,
        fieldMappings: mergeFieldMappings(placeholders, currentDialog.fieldMappings)
      }
    })
  }

  async function parsePlaceholdersForFile(filePath: string): Promise<TemplatePlaceholder[]> {
    const response = await window.api.parseTemplatePlaceholders(filePath)

    if (!response.ok) {
      showError('解析占位符失败', response.message)
      return []
    }

    return response.placeholders
  }

  function mergeFieldMappings(
    placeholders: TemplatePlaceholder[],
    currentMappings: TemplateFieldMapping
  ): TemplateFieldMapping {
    const placeholderNames = new Set(placeholders.map((placeholder) => placeholder.name))

    return Object.fromEntries(
      Object.entries(currentMappings).filter(
        ([placeholder, fieldKey]) => placeholderNames.has(placeholder) && Boolean(fieldKey)
      )
    )
  }

  async function handleSelectFile(): Promise<void> {
    const response = await window.api.selectTemplateFile()

    if (!response.ok) {
      if (!response.canceled) {
        showError('选择模板失败', response.message)
      }
      return
    }

    const placeholders = await parsePlaceholdersForFile(response.filePath)

    setTemplateForm((currentForm) => ({
      ...currentForm,
      sourceFilePath: response.filePath,
      fileName: response.fileName,
      fieldMappings: mergeFieldMappings(placeholders, currentForm.fieldMappings)
    }))
  }

  async function selectDroppedTemplateFile(file?: File): Promise<void> {
    if (!file) {
      return
    }

    if (!isSupportedTemplateFile(file.name)) {
      showError('请选择 PPT、Word、Excel 模板文件')
      return
    }

    const filePath = window.api.getDroppedFilePath(file)

    if (!filePath) {
      showError('无法读取拖拽文件路径')
      return
    }

    const placeholders = await parsePlaceholdersForFile(filePath)

    setTemplateForm((currentForm) => ({
      ...currentForm,
      sourceFilePath: filePath,
      fileName: file.name,
      fieldMappings: mergeFieldMappings(placeholders, currentForm.fieldMappings)
    }))
  }

  function handleDrop(event: React.DragEvent<HTMLButtonElement>): void {
    event.preventDefault()
    setIsDragActive(false)
    void selectDroppedTemplateFile(event.dataTransfer.files[0])
  }

  function updateFieldMapping(placeholder: string, fieldKey: string): void {
    setMappingDialog((currentDialog) => {
      const fieldMappings = { ...currentDialog.fieldMappings }

      if (fieldKey) {
        fieldMappings[placeholder] = fieldKey
      } else {
        delete fieldMappings[placeholder]
      }

      return {
        ...currentDialog,
        fieldMappings
      }
    })
  }

  async function handleSaveFieldMappings(): Promise<void> {
    const template = mappingDialog.template

    if (!template) {
      return
    }

    setMappingSaving(true)

    try {
      const response = await window.api.saveTemplate({
        id: template.id,
        name: template.name,
        grade: template.grade,
        semester: template.semester,
        type: template.type,
        sourceFilePath: template.filePath,
        fieldMappings: mappingDialog.fieldMappings
      })

      if (!response.ok) {
        showError('保存字段关联失败', response.message)
        return
      }

      setTemplates((currentTemplates) =>
        currentTemplates.map((currentTemplate) =>
          currentTemplate.id === response.template.id ? response.template : currentTemplate
        )
      )
      setMappingDialog(emptyMappingDialog)
      showSuccess('字段关联已保存')
    } finally {
      setMappingSaving(false)
    }
  }

  function handleFieldDragStart(event: React.DragEvent<HTMLButtonElement>, fieldKey: string): void {
    event.dataTransfer.setData('application/x-student-field', fieldKey)
    event.dataTransfer.effectAllowed = 'copy'
  }

  function handlePlaceholderDrop(
    event: React.DragEvent<HTMLDivElement>,
    placeholder: TemplatePlaceholder
  ): void {
    event.preventDefault()
    const fieldKey = event.dataTransfer.getData('application/x-student-field')
    const field = studentFieldOptions.find((item) => item.key === fieldKey)

    if (!field) {
      return
    }

    if (field.kind !== placeholder.kind) {
      showError('字段类型不匹配')
      return
    }

    updateFieldMapping(placeholder.name, field.key)
  }

  async function handleSaveTemplate(): Promise<void> {
    const name = templateForm.name.trim()
    const grade = templateForm.grade.trim()

    if (!name) {
      showError('请输入模板名称')
      return
    }

    if (!grade) {
      showError('请输入适用年级')
      return
    }

    if (!templateForm.sourceFilePath) {
      showError('请选择模板文件')
      return
    }

    setSaving(true)

    try {
      const response = await window.api.saveTemplate({
        id: templateForm.id,
        name,
        grade,
        semester: templateForm.semester,
        type: templateForm.type,
        sourceFilePath: templateForm.sourceFilePath,
        fieldMappings: templateForm.fieldMappings
      })

      if (!response.ok) {
        showError('保存模板失败', response.message)
        return
      }

      setTemplates((currentTemplates) => {
        const exists = currentTemplates.some((template) => template.id === response.template.id)
        const nextTemplates = exists
          ? currentTemplates.map((template) =>
              template.id === response.template.id ? response.template : template
            )
          : [response.template, ...currentTemplates]

        return nextTemplates.sort((first, second) =>
          second.updatedAt.localeCompare(first.updatedAt)
        )
      })
      setDrawerOpen(false)
      setTemplateForm(emptyTemplateForm)
      showSuccess(templateForm.id ? '模板已更新' : '模板已添加')
    } finally {
      setSaving(false)
    }
  }

  async function handleOpenTemplate(template: TemplateItem): Promise<void> {
    setOpeningId(template.id)

    try {
      const response = await window.api.openTemplate(template.filePath)

      if (!response.ok) {
        showError('打开模板失败', response.message)
      }
    } finally {
      setOpeningId('')
    }
  }

  async function handleDeleteTemplate(template: TemplateItem): Promise<void> {
    const response = await window.api.deleteTemplate(template.id)

    if (!response.ok) {
      showError('删除模板失败', response.message)
      return
    }

    setTemplates((currentTemplates) =>
      currentTemplates.filter((currentTemplate) => currentTemplate.id !== template.id)
    )
    setDeletingTemplate(null)
    showSuccess(`已删除模板「${template.name}」`)
  }

  return (
    <>
      <section className="space-y-4 bg-white px-4 py-4">
        <div className="flex flex-col gap-3 md:flex-row md:items-center md:justify-between">
          <div>
            <CardTitle>
              <div className="flex items-center gap-2">
                <LayoutPanelTop className="size-5 text-primary" />
                模板管理
              </div>
            </CardTitle>
            <CardDescription className="mt-1">
              管理报告模板文件，支持 PPT、Word、Excel。
            </CardDescription>
          </div>
          <Button onClick={openCreateDrawer}>
            <Plus />
            添加模板
          </Button>
        </div>

        <div className="grid gap-3 rounded-md border border-dashed bg-muted/30 p-4">
          <div className="relative min-w-0">
            <Search className="pointer-events-none absolute left-3 top-1/2 size-4 -translate-y-1/2 text-muted-foreground" />
            <Input
              className="pl-9"
              value={query}
              onChange={(event) => setQuery(event.target.value)}
              placeholder="搜索模板名称、年级、学期、类型或文件名"
            />
          </div>
        </div>

        <ScrollArea className="h-140">
          <div className="grid gap-3 pr-3 md:grid-cols-2 xl:grid-cols-3">
            {filteredTemplates.map((template) => {
              const FileIcon = getFileIcon(template.fileExtension)
              const fileKind = getFileKind(template.fileExtension)

              return (
                <Card key={template.id} className="shadow-none">
                  <CardHeader className="space-y-4">
                    <div className="flex items-start justify-between gap-3">
                      <div className="grid size-11 shrink-0 place-items-center rounded-md bg-muted text-primary">
                        <FileIcon className="size-5" />
                      </div>
                      <div className="flex flex-wrap justify-end gap-2">
                        <Badge variant="secondary">{fileKind}</Badge>
                        <Badge variant={template.type === 'landscape' ? 'warning' : 'outline'}>
                          {getTemplateTypeLabel(template.type)}
                        </Badge>
                        <Badge variant="outline">
                          {getTemplateSemesterLabel(template.semester)}
                        </Badge>
                      </div>
                    </div>
                    <div className="min-w-0">
                      <CardTitle className="truncate text-base">{template.name}</CardTitle>
                      <CardDescription className="mt-2 truncate">
                        {template.originalFileName}
                      </CardDescription>
                    </div>
                  </CardHeader>
                  <CardContent className="space-y-4">
                    <div className="grid grid-cols-2 gap-3">
                      <div className="rounded-md bg-muted px-3 py-2">
                        <p className="text-xs text-muted-foreground">适用年级</p>
                        <p className="mt-1 truncate font-medium">{template.grade}</p>
                      </div>
                      <div className="rounded-md bg-muted px-3 py-2">
                        <p className="text-xs text-muted-foreground">学期</p>
                        <p className="mt-1 truncate font-medium">
                          {getTemplateSemesterLabel(template.semester)}
                        </p>
                      </div>
                      <div className="rounded-md bg-muted px-3 py-2">
                        <p className="text-xs text-muted-foreground">更新时间</p>
                        <p className="mt-1 truncate text-sm">
                          {formatDateTime(template.updatedAt)}
                        </p>
                      </div>
                      <div className="rounded-md bg-muted px-3 py-2">
                        <p className="text-xs text-muted-foreground">字段对照</p>
                        <p className="mt-1 truncate text-sm">
                          {Object.keys(template.fieldMappings).length}/
                          {template.placeholders.length}
                        </p>
                      </div>
                    </div>

                    <div className="flex items-center justify-end gap-2">
                      <Button
                        size="sm"
                        disabled={openingId === template.id}
                        onClick={() => handleOpenTemplate(template)}
                      >
                        <ExternalLink />
                        {openingId === template.id ? '打开中' : '打开模板'}
                      </Button>
                      <DropdownMenu>
                        <DropdownMenuTrigger asChild>
                          <Button size="sm" variant="outline">
                            <MoreHorizontal />
                            操作
                          </Button>
                        </DropdownMenuTrigger>
                        <DropdownMenuContent>
                          <DropdownMenuLabel>模板操作</DropdownMenuLabel>
                          <DropdownMenuSeparator />
                          <DropdownMenuItem onSelect={() => openEditDrawer(template)}>
                            <Pencil />
                            编辑信息
                          </DropdownMenuItem>
                          <DropdownMenuItem
                            disabled={template.fileExtension !== '.pptx'}
                            onSelect={() => void openMappingDialog(template)}
                          >
                            <ListTree />
                            字段关联
                          </DropdownMenuItem>
                          <DropdownMenuItem
                            className="text-destructive"
                            onSelect={() => setDeletingTemplate(template)}
                          >
                            <Trash2 />
                            删除
                          </DropdownMenuItem>
                        </DropdownMenuContent>
                      </DropdownMenu>
                    </div>
                  </CardContent>
                </Card>
              )
            })}

            {filteredTemplates.length === 0 && (
              <div className="col-span-full grid h-32 place-items-center rounded-md border text-sm text-muted-foreground">
                暂无模板，请添加模板文件
              </div>
            )}
          </div>
        </ScrollArea>
      </section>

      <Drawer direction="right" open={drawerOpen} onOpenChange={setDrawerOpen}>
        <DrawerContent>
          <DrawerHeader>
            <div className="flex items-start justify-between gap-3">
              <div>
                <DrawerTitle>{templateForm.id ? '编辑模板' : '添加模板'}</DrawerTitle>
                <DrawerDescription className="mt-1">
                  填写模板名称、适用年级、学期、版式类型，并上传 Office 模板文件。
                </DrawerDescription>
              </div>
              <Button
                size="icon"
                variant="ghost"
                aria-label="关闭模板表单"
                onClick={() => setDrawerOpen(false)}
              >
                <X />
              </Button>
            </div>
          </DrawerHeader>
          <div className="min-h-0 flex-1 space-y-5 overflow-y-auto p-6">
            <div className="grid gap-2">
              <label className="text-sm font-medium" htmlFor="template-name">
                模板名称
              </label>
              <Input
                id="template-name"
                value={templateForm.name}
                onChange={(event) =>
                  setTemplateForm({
                    ...templateForm,
                    name: event.target.value
                  })
                }
                placeholder="例如：大班成长报告横版模板"
              />
            </div>

            <div className="grid gap-2">
              <label className="text-sm font-medium" htmlFor="template-grade">
                适用年级
              </label>
              <Input
                id="template-grade"
                value={templateForm.grade}
                onChange={(event) =>
                  setTemplateForm({
                    ...templateForm,
                    grade: event.target.value
                  })
                }
                placeholder="例如：小班、中班、大班"
              />
            </div>

            <div className="grid gap-2">
              <label className="text-sm font-medium" htmlFor="template-semester">
                学期
              </label>
              <Select
                id="template-semester"
                value={templateForm.semester}
                onChange={(event) =>
                  setTemplateForm({
                    ...templateForm,
                    semester: event.target.value as TemplateSemester
                  })
                }
              >
                {templateSemesterOptions.map((option) => (
                  <option key={option.value} value={option.value}>
                    {option.label}
                  </option>
                ))}
              </Select>
            </div>

            <div className="grid gap-2">
              <label className="text-sm font-medium" htmlFor="template-type">
                模板类型
              </label>
              <Select
                id="template-type"
                value={templateForm.type}
                onChange={(event) =>
                  setTemplateForm({
                    ...templateForm,
                    type: event.target.value as TemplateType
                  })
                }
              >
                {templateTypeOptions.map((option) => (
                  <option key={option.value} value={option.value}>
                    {option.label}
                  </option>
                ))}
              </Select>
            </div>

            <div className="grid gap-2">
              <p className="text-sm font-medium">模板文件</p>
              <button
                type="button"
                className={[
                  'grid min-h-[150px] place-items-center rounded-md border border-dashed bg-muted/30 p-5 text-center transition-colors',
                  isDragActive
                    ? 'border-primary bg-accent'
                    : 'hover:border-primary hover:bg-muted/50'
                ].join(' ')}
                onClick={handleSelectFile}
                onDragEnter={(event) => {
                  event.preventDefault()
                  setIsDragActive(true)
                }}
                onDragOver={(event) => event.preventDefault()}
                onDragLeave={() => setIsDragActive(false)}
                onDrop={handleDrop}
              >
                <div className="space-y-3">
                  <div className="mx-auto grid size-12 place-items-center rounded-full bg-primary text-primary-foreground">
                    <Upload className="size-5" />
                  </div>
                  <div>
                    <p className="font-medium">
                      {templateForm.fileName || '点击或拖拽 PPT、Word、Excel 文件到这里'}
                    </p>
                    <p className="mt-1 text-sm text-muted-foreground">
                      支持 .ppt、.pptx、.doc、.docx、.xls、.xlsx
                    </p>
                  </div>
                </div>
              </button>
            </div>

            <div className="sticky bottom-0 -mx-6 flex justify-end gap-2 border-t bg-card px-6 py-4">
              <Button variant="outline" onClick={() => setDrawerOpen(false)}>
                取消
              </Button>
              <Button disabled={saving} onClick={handleSaveTemplate}>
                <Upload />
                {saving ? '保存中' : '保存模板'}
              </Button>
            </div>
          </div>
        </DrawerContent>
      </Drawer>

      <Dialog
        open={mappingDialog.open}
        onOpenChange={(open) => {
          if (!open) {
            setMappingDialog(emptyMappingDialog)
          }
        }}
      >
        <DialogContent>
          <DialogHeader>
            <DialogTitle>字段关联</DialogTitle>
            <DialogDescription>
              {mappingDialog.template
                ? `为「${mappingDialog.template.name}」选择模板占位符和学生字段的对应关系。`
                : '选择模板占位符和学生字段的对应关系。'}
            </DialogDescription>
          </DialogHeader>

          <div className="min-h-0 flex-1 overflow-hidden rounded-md border">
            <div className="grid grid-cols-[minmax(0,1fr)_220px] border-b bg-muted/30 px-4 py-2 text-xs font-medium text-muted-foreground">
              <span>模板占位符</span>
              <span>绑定学生字段</span>
            </div>
            <div className="max-h-[42vh] overflow-y-auto">
              {mappingDialog.placeholders.map((placeholder) => {
                const mappedField = mappingDialog.fieldMappings[placeholder.name] ?? ''

                return (
                  <div
                    key={placeholder.name}
                    className="grid grid-cols-[minmax(0,1fr)_220px] items-center gap-3 border-b px-4 py-3 last:border-b-0"
                    onDragOver={(event) => event.preventDefault()}
                    onDrop={(event) => handlePlaceholderDrop(event, placeholder)}
                  >
                    <div className="min-w-0">
                      <div className="flex items-center gap-2">
                        <Badge variant={placeholder.kind === 'image' ? 'warning' : 'outline'}>
                          {placeholder.kind === 'image' ? '图片' : '文本'}
                        </Badge>
                        <p className="truncate text-sm font-medium">{placeholder.name}</p>
                      </div>
                      <p className="mt-1 truncate text-xs text-muted-foreground">
                        {mappedField ? `已绑定：${getStudentFieldLabel(mappedField)}` : '未绑定'}
                      </p>
                    </div>
                    <div className="flex items-center gap-2">
                      <Select
                        value={mappedField}
                        onChange={(event) =>
                          updateFieldMapping(placeholder.name, event.target.value)
                        }
                      >
                        <option value="">选择字段</option>
                        {getCompatibleStudentFields(placeholder.kind).map((field) => (
                          <option key={field.key} value={field.key}>
                            {field.label}
                          </option>
                        ))}
                      </Select>
                      <Button
                        size="sm"
                        variant="ghost"
                        disabled={!mappedField}
                        onClick={() => updateFieldMapping(placeholder.name, '')}
                      >
                        清空
                      </Button>
                    </div>
                  </div>
                )
              })}

              {mappingDialog.placeholders.length === 0 && (
                <div className="grid min-h-40 place-items-center px-4 text-center text-sm text-muted-foreground">
                  当前模板没有解析到可关联的占位符
                </div>
              )}
            </div>
          </div>

          <div className="shrink-0 rounded-md border bg-muted/20 p-3">
            <div className="mb-2 flex items-center justify-between gap-3">
              <p className="text-xs font-medium text-muted-foreground">可拖拽的学生字段</p>
              <p className="text-xs text-muted-foreground">
                已匹配 {Object.keys(mappingDialog.fieldMappings).length}/
                {mappingDialog.placeholders.length}
              </p>
            </div>
            <div className="flex flex-wrap gap-2">
              {studentFieldOptions.map((field) => (
                <button
                  key={field.key}
                  type="button"
                  draggable
                  className="inline-flex h-8 items-center gap-2 rounded-md border bg-background px-2 text-xs transition-colors hover:border-primary hover:bg-accent"
                  onDragStart={(event) => handleFieldDragStart(event, field.key)}
                >
                  <span>{field.label}</span>
                  <Badge variant={field.kind === 'image' ? 'warning' : 'secondary'}>
                    {field.kind === 'image' ? '图' : '文'}
                  </Badge>
                </button>
              ))}
            </div>
          </div>

          <DialogFooter className="shrink-0">
            <Button variant="outline" onClick={() => setMappingDialog(emptyMappingDialog)}>
              取消
            </Button>
            <Button
              disabled={mappingSaving || !mappingDialog.template}
              onClick={handleSaveFieldMappings}
            >
              <Upload />
              {mappingSaving ? '保存中' : '保存关联'}
            </Button>
          </DialogFooter>
        </DialogContent>
      </Dialog>

      <AlertDialog open={Boolean(deletingTemplate)} onOpenChange={() => setDeletingTemplate(null)}>
        <AlertDialogContent>
          <AlertDialogHeader>
            <AlertDialogTitle>删除「{deletingTemplate?.name}」？</AlertDialogTitle>
            <AlertDialogDescription>
              删除后，这个模板记录和已导入的模板文件副本都会被移除。
            </AlertDialogDescription>
          </AlertDialogHeader>
          <AlertDialogFooter>
            <AlertDialogCancel>取消</AlertDialogCancel>
            <AlertDialogAction
              onClick={() => {
                if (deletingTemplate) {
                  void handleDeleteTemplate(deletingTemplate)
                }
              }}
            >
              确认删除
            </AlertDialogAction>
          </AlertDialogFooter>
        </AlertDialogContent>
      </AlertDialog>

      <DashboardFooter />
    </>
  )
}
