<script setup>
import { ref, reactive, computed, onMounted } from 'vue'
import { toast } from 'vue-sonner'
import { Icon } from '@iconify/vue'
import dayjs from 'dayjs'
import BasicLayout from '@/layouts/components/BasicLayout.vue'
import { MaterialService } from '@/api/material'
import { VoiceService } from '@/api/voice'
import { useUpload } from '@/composables/useUpload'
import useVoiceText from '@/hooks/web/useVoiceText'

import { Button } from '@/components/ui/button'
import { Input } from '@/components/ui/input'
import { Textarea } from '@/components/ui/textarea'
import { Label } from '@/components/ui/label'
import { Badge } from '@/components/ui/badge'
import { Spinner } from '@/components/ui/spinner'
import {
  Table,
  TableBody,
  TableCell,
  TableHead,
  TableHeader,
  TableRow,
} from '@/components/ui/table'
import {
  Dialog,
  DialogContent,
  DialogFooter,
  DialogHeader,
  DialogTitle,
} from '@/components/ui/dialog'
import {
  AlertDialog,
  AlertDialogAction,
  AlertDialogCancel,
  AlertDialogContent,
  AlertDialogDescription,
  AlertDialogFooter,
  AlertDialogHeader,
  AlertDialogTitle,
} from '@/components/ui/alert-dialog'
import { Tooltip, TooltipContent, TooltipTrigger } from '@/components/ui/tooltip'
import { Progress } from '@/components/ui/progress'

// ========== 常量 ==========
const MAX_FILE_SIZE = 5 * 1024 * 1024
const VALID_AUDIO_TYPES = ['audio/mpeg', 'audio/wav', 'audio/aac', 'audio/mp4', 'audio/flac', 'audio/ogg']
const VALID_AUDIO_EXTENSIONS = ['.mp3', '.wav', '.aac', '.m4a', '.flac', '.ogg']

const DEFAULT_FORM_DATA = {
  id: null,
  name: '',
  fileId: null,
  autoTranscribe: true,
  language: 'zh-CN',
  gender: 'female',
  note: '',
  text: '',
  fileUrl: ''
}

// ========== 响应式数据 ==========
const loading = ref(false)
const submitting = ref(false)
const voiceList = ref([])
const modalVisible = ref(false)
const deleteDialogVisible = ref(false)
const deleteTarget = ref(null)
const formMode = ref('create')
const audioPlayer = ref(null)
const fileList = ref([])
const extractingText = ref(false)
const fileInputRef = ref(null)
const isDragging = ref(false)

const searchParams = reactive({
  name: '',
  pageNo: 1,
  pageSize: 10
})

const pagination = reactive({
  current: 1,
  pageSize: 10,
  total: 0,
  showSizeChanger: true,
})

const formData = reactive({ ...DEFAULT_FORM_DATA })

// ========== Hooks ==========
const { state: uploadState, upload } = useUpload()
const { getVoiceText } = useVoiceText()

// ========== 计算属性 ==========
const isCreateMode = computed(() => formMode.value === 'create')

const isSubmitDisabled = computed(() => {
  if (!isCreateMode.value) return false
  if (extractingText.value) return true
  if (formData.fileId && !formData.text) return true
  return false
})

// ========== 工具函数 ==========
const formatDateTime = (value) => {
  if (!value) return '-'
  return dayjs(value).format('YYYY-MM-DD HH:mm:ss')
}

// ========== 数据加载 ==========
const loadVoiceList = async () => {
  loading.value = true
  try {
    const res = await VoiceService.getPage({
      pageNo: pagination.current,
      pageSize: pagination.pageSize,
      name: searchParams.name || undefined
    })
    if (res.code !== 0) {
      toast.error(res.msg || '加载失败')
      return
    }
    voiceList.value = res.data.list || []
    pagination.total = res.data.total || 0
  } catch (error) {
    console.error('加载配音列表失败:', error)
    toast.error('加载失败，请稍后重试')
  } finally {
    loading.value = false
  }
}

// ========== 搜索和分页 ==========
function handleSearch() {
  pagination.current = 1
  loadVoiceList()
}

function handleReset() {
  searchParams.name = ''
  pagination.current = 1
  loadVoiceList()
}

function handlePageChange(page) {
  pagination.current = page
  loadVoiceList()
}

// ========== CRUD 操作 ==========
function handleCreate() {
  formMode.value = 'create'
  resetForm()
  modalVisible.value = true
}

async function handleEdit(record) {
  formMode.value = 'edit'
  try {
    const res = await VoiceService.get(record.id)
    if (res.code === 0 && res.data) {
      Object.assign(formData, {
        id: res.data.id || null,
        name: res.data.name || '',
        fileId: res.data.fileId || null,
        language: res.data.language || 'zh-CN',
        gender: res.data.gender || 'female',
        note: res.data.note || ''
      })
    }
  } catch (error) {
    console.error('获取配音详情失败:', error)
    Object.assign(formData, {
      id: record.id,
      name: record.name || '',
      fileId: record.fileId || null,
      note: record.note || ''
    })
  }
  modalVisible.value = true
}

function handleDelete(record) {
  deleteTarget.value = record
  deleteDialogVisible.value = true
}

async function confirmDelete() {
  if (!deleteTarget.value) return
  try {
    const res = await VoiceService.delete(deleteTarget.value.id)
    if (res.code !== 0) {
      toast.error(res.msg || '删除失败')
      return
    }
    toast.success('删除成功')
    loadVoiceList()
  } catch (error) {
    console.error('删除失败:', error)
    toast.error('删除失败，请稍后重试')
  } finally {
    deleteDialogVisible.value = false
    deleteTarget.value = null
  }
}

// ========== 音频播放 ==========
function handlePlayAudio(record) {
  if (record.fileUrl && audioPlayer.value) {
    audioPlayer.value.src = record.fileUrl
    audioPlayer.value.play()
  } else {
    toast.warning('音频文件不存在')
  }
}

// ========== 文件上传 ==========
function triggerFileInput() {
  fileInputRef.value?.click()
}

function handleFileSelect(event) {
  const file = event.target.files?.[0]
  if (file) processFile(file)
}

function handleDragOver(e) {
  e.preventDefault()
  isDragging.value = true
}

function handleDragLeave() {
  isDragging.value = false
}

function handleDrop(event) {
  event.preventDefault()
  isDragging.value = false
  const file = event.dataTransfer?.files?.[0]
  if (file) processFile(file)
}

function validateFile(file) {
  if (file.size > MAX_FILE_SIZE) {
    toast.error('文件大小不能超过 5MB')
    return false
  }
  const fileName = file.name.toLowerCase()
  const isValid = VALID_AUDIO_TYPES.some(t => file.type.includes(t)) ||
    VALID_AUDIO_EXTENSIONS.some(ext => fileName.endsWith(ext))
  if (!isValid) {
    toast.error('请上传音频文件（MP3、WAV、AAC 等）')
    return false
  }
  return true
}

async function processFile(file) {
  if (!validateFile(file)) return

  try {
    await upload(file, {
      fileCategory: 'voice',
      groupId: null,
      onSuccess: async (id, fileUrl) => {
        formData.fileId = id
        formData.fileUrl = fileUrl
        fileList.value = [file]
        await fetchAudioTextById(id)
      },
      onError: (error) => {
        toast.error(error.message || '上传失败')
      }
    })
  } catch (error) {
    console.error('上传失败:', error)
  }
}

async function fetchAudioTextById(fileId) {
  if (!fileId) return
  extractingText.value = true
  try {
    const res = await MaterialService.getAudioPlayUrl(fileId)
    if (res.code === 0 && res.data) {
      const results = await getVoiceText([{ audio_url: res.data }])
      if (results?.length > 0) {
        formData.text = results[0].value || ''
      }
    }
  } catch (error) {
    console.error('获取音频文本失败:', error)
    toast.error('语音识别失败')
  } finally {
    extractingText.value = false
  }
}

function handleRemoveFile() {
  formData.fileId = null
  formData.text = ''
  formData.fileUrl = ''
  fileList.value = []
}

// ========== 表单操作 ==========
async function handleSubmit() {
  if (!formData.name.trim()) {
    toast.warning('请输入配音名称')
    return
  }
  if (isCreateMode.value && !formData.fileId) {
    toast.warning('请上传音频文件')
    return
  }

  submitting.value = true
  const params = isCreateMode.value
    ? {
        name: formData.name,
        fileId: formData.fileId,
        autoTranscribe: formData.autoTranscribe,
        language: formData.language,
        gender: formData.gender,
        note: formData.note,
        text: formData.text
      }
    : {
        id: formData.id,
        name: formData.name,
        language: formData.language,
        gender: formData.gender,
        note: formData.note
      }

  try {
    const res = isCreateMode.value
      ? await VoiceService.create(params)
      : await VoiceService.update(params)
    if (res.code !== 0) {
      toast.error(res.msg || '操作失败')
      return
    }
    toast.success(isCreateMode.value ? '创建成功' : '更新成功')
    modalVisible.value = false
    loadVoiceList()
  } catch (error) {
    console.error('提交失败:', error)
    toast.error('操作失败，请稍后重试')
  } finally {
    submitting.value = false
  }
}

function handleCancel() {
  modalVisible.value = false
  resetForm()
}

function resetForm() {
  Object.assign(formData, { ...DEFAULT_FORM_DATA })
  fileList.value = []
}

// ========== 生命周期 ==========
onMounted(() => loadVoiceList())
</script>

<template>
  <BasicLayout :show-back="false" :show-title="false">
    <!-- 工具栏 -->
    <div class="toolbar">
      <div class="toolbar-left">
        <Button @click="handleCreate">
          <Icon icon="lucide:plus" class="mr-1.5 size-4" />
          新建配音
        </Button>
      </div>
      <div class="toolbar-right">
        <Input
          v-model="searchParams.name"
          placeholder="搜索配音名称..."
          class="w-64"
          @keypress.enter="handleSearch"
        >
          <template #prefix>
            <Icon icon="lucide:search" class="size-4 text-muted-foreground" />
          </template>
        </Input>
        <Button variant="outline" @click="handleSearch">搜索</Button>
        <Button variant="ghost" @click="handleReset">重置</Button>
      </div>
    </div>

    <!-- 表格区域 -->
    <div class="table-wrapper">
      <Table>
        <TableHeader>
          <TableRow class="hover:bg-transparent">
            <TableHead class="w-44">配音名称</TableHead>
            <TableHead>备注</TableHead>
            <TableHead class="w-44">创建时间</TableHead>
            <TableHead class="w-40 text-center">操作</TableHead>
          </TableRow>
        </TableHeader>
        <TableBody>
          <!-- 加载状态 -->
          <TableRow v-if="loading">
            <TableCell :col-span="4" class="h-48 text-center">
              <Spinner class="mx-auto size-6" />
            </TableCell>
          </TableRow>

          <!-- 空状态 -->
          <TableRow v-else-if="voiceList.length === 0">
            <TableCell :col-span="4" class="h-48 text-center">
              <div class="flex flex-col items-center gap-3 text-muted-foreground">
                <Icon icon="lucide:mic-off" class="size-10 opacity-50" />
                <span>暂无配音数据</span>
              </div>
            </TableCell>
          </TableRow>

          <!-- 数据列表 -->
          <TableRow v-else v-for="record in voiceList" :key="record.id" class="group">
            <TableCell>
              <span class="font-medium">{{ record.name || '未命名' }}</span>
            </TableCell>
            <TableCell>
              <Tooltip v-if="record.note" :delay-duration="200">
                <TooltipTrigger as-child>
                  <span class="text-muted-foreground line-clamp-1">{{ record.note }}</span>
                </TooltipTrigger>
                <TooltipContent class="max-w-xs">
                  {{ record.note }}
                </TooltipContent>
              </Tooltip>
              <span v-else class="text-muted-foreground">-</span>
            </TableCell>
            <TableCell class="text-muted-foreground">
              {{ formatDateTime(record.createTime) }}
            </TableCell>
            <TableCell class="text-center">
              <div class="flex items-center justify-center gap-1 opacity-0 group-hover:opacity-100 transition-opacity">
                <Button variant="ghost" size="sm" class="h-8 px-2" @click="handlePlayAudio(record)">
                  <Icon icon="lucide:play" class="size-4 mr-1" />
                  播放
                </Button>
                <Button variant="ghost" size="sm" class="h-8 px-2" @click="handleEdit(record)">
                  <Icon icon="lucide:pencil" class="size-4 mr-1" />
                  编辑
                </Button>
                <Button variant="ghost" size="sm" class="h-8 px-2 text-destructive hover:text-destructive" @click="handleDelete(record)">
                  <Icon icon="lucide:trash-2" class="size-4 mr-1" />
                  删除
                </Button>
              </div>
            </TableCell>
          </TableRow>
        </TableBody>
      </Table>

      <!-- 分页 -->
      <div v-if="pagination.total > pagination.pageSize" class="pagination-bar">
        <span class="text-sm text-muted-foreground">
          共 {{ pagination.total }} 条
        </span>
        <div class="flex gap-1">
          <Button
            variant="outline"
            size="sm"
            :disabled="pagination.current === 1"
            @click="handlePageChange(pagination.current - 1)"
          >
            上一页
          </Button>
          <Button
            variant="outline"
            size="sm"
            :disabled="pagination.current * pagination.pageSize >= pagination.total"
            @click="handlePageChange(pagination.current + 1)"
          >
            下一页
          </Button>
        </div>
      </div>
    </div>

    <!-- 新建/编辑弹窗 -->
    <Dialog :open="modalVisible" @update:open="(v) => modalVisible = v">
      <DialogContent class="sm:max-w-lg">
        <DialogHeader>
          <DialogTitle>{{ isCreateMode ? '新建配音' : '编辑配音' }}</DialogTitle>
        </DialogHeader>

        <div class="space-y-5 py-2">
          <!-- 名称 -->
          <div class="space-y-2">
            <Label for="name">配音名称 <span class="text-destructive">*</span></Label>
            <Input id="name" v-model="formData.name" placeholder="请输入配音名称" />
          </div>

          <!-- 上传区域 -->
          <div v-if="isCreateMode" class="space-y-2">
            <Label>音频文件 <span class="text-destructive">*</span></Label>

            <!-- 未上传状态 -->
            <div
              v-if="fileList.length === 0 && !uploadState.uploading && !extractingText"
              class="upload-zone"
              :class="{ 'upload-zone--dragging': isDragging }"
              @click="triggerFileInput"
              @dragover="handleDragOver"
              @dragleave="handleDragLeave"
              @drop="handleDrop"
            >
              <div class="upload-zone__icon">
                <Icon icon="lucide:cloud-upload" />
              </div>
              <p class="upload-zone__title">点击或拖拽音频文件到此区域</p>
              <p class="upload-zone__hint">支持 MP3、WAV、AAC、M4A、FLAC、OGG，最大 5MB</p>
            </div>

            <!-- 上传中 -->
            <div v-else-if="uploadState.uploading" class="upload-status">
              <Progress :value="50" class="w-16" />
              <p class="mt-3 text-sm text-muted-foreground">正在上传...</p>
            </div>

            <!-- 识别中 -->
            <div v-else-if="extractingText" class="upload-status">
              <Progress :value="50" class="w-16" />
              <p class="mt-3 text-sm text-muted-foreground">正在识别语音...</p>
            </div>

            <!-- 已上传 -->
            <div v-else class="upload-preview">
              <div class="upload-preview__icon">
                <Icon icon="lucide:file-audio" />
              </div>
              <div class="upload-preview__info">
                <span class="upload-preview__name">{{ fileList[0]?.name || '音频文件' }}</span>
                <Badge v-if="formData.text" variant="secondary" class="gap-1">
                  <Icon icon="lucide:check-circle" class="size-3" />
                  已识别语音
                </Badge>
                <Badge v-else variant="outline" class="gap-1 text-amber-600">
                  <Icon icon="lucide:alert-circle" class="size-3" />
                  未识别到语音
                </Badge>
              </div>
              <Button variant="ghost" size="sm" class="text-destructive" @click="handleRemoveFile">
                <Icon icon="lucide:x" class="size-4" />
              </Button>
            </div>

            <input
              ref="fileInputRef"
              type="file"
              accept="audio/*,.mp3,.wav,.aac,.m4a,.flac,.ogg"
              class="hidden"
              @change="handleFileSelect"
            />
          </div>

          <!-- 备注 -->
          <div class="space-y-2">
            <Label for="note">备注</Label>
            <Textarea
              id="note"
              v-model="formData.note"
              placeholder="备注信息（选填）"
              :rows="2"
            />
          </div>
        </div>

        <DialogFooter>
          <Button variant="outline" @click="handleCancel">取消</Button>
          <Button :disabled="isSubmitDisabled" :loading="submitting" @click="handleSubmit">
            保存
          </Button>
        </DialogFooter>
      </DialogContent>
    </Dialog>

    <!-- 删除确认 -->
    <AlertDialog :open="deleteDialogVisible" @update:open="(v) => deleteDialogVisible = v">
      <AlertDialogContent>
        <AlertDialogHeader>
          <AlertDialogTitle>确认删除</AlertDialogTitle>
          <AlertDialogDescription>
            确定要删除配音「{{ deleteTarget?.name }}」吗？此操作不可恢复。
          </AlertDialogDescription>
        </AlertDialogHeader>
        <AlertDialogFooter>
          <AlertDialogCancel>取消</AlertDialogCancel>
          <AlertDialogAction
            class="bg-destructive text-destructive-foreground hover:bg-destructive/90"
            @click="confirmDelete"
          >
            删除
          </AlertDialogAction>
        </AlertDialogFooter>
      </AlertDialogContent>
    </AlertDialog>

    <audio ref="audioPlayer" class="hidden" />
  </BasicLayout>
</template>

<style scoped lang="less">
.toolbar {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: var(--space-4);
  background: var(--color-bg-card);
  border-radius: var(--radius-lg);
  margin-bottom: var(--space-4);
  box-shadow: var(--shadow-sm);
}

.toolbar-right {
  display: flex;
  align-items: center;
  gap: var(--space-2);
}

.table-wrapper {
  background: var(--color-bg-card);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-sm);
  overflow: hidden;
}

.pagination-bar {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: var(--space-3) var(--space-4);
  border-top: 1px solid var(--color-border);
  background: var(--color-bg-card);
}

// 上传区域
.upload-zone {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  padding: var(--space-8) var(--space-4);
  border: 2px dashed var(--color-border);
  border-radius: var(--radius-lg);
  background: var(--color-muted);
  cursor: pointer;
  transition: all 0.2s ease;

  &:hover {
    border-color: var(--color-primary);
    background: oklch(0.97 0.01 254.604);
  }

  &--dragging {
    border-color: var(--color-primary);
    background: oklch(0.95 0.02 254.604);
  }

  &__icon {
    font-size: 36px;
    color: var(--color-primary);
    margin-bottom: var(--space-3);
    opacity: 0.8;
  }

  &__title {
    font-size: var(--font-size-sm);
    font-weight: 500;
    color: var(--color-foreground);
    margin-bottom: var(--space-1);
  }

  &__hint {
    font-size: var(--font-size-xs);
    color: var(--color-muted-foreground);
  }
}

.upload-status {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  padding: var(--space-8) var(--space-4);
  border: 2px solid var(--color-border);
  border-radius: var(--radius-lg);
  background: var(--color-muted);
}

.upload-preview {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  padding: var(--space-4);
  border: 1px solid oklch(0.92 0.02 145);
  border-radius: var(--radius-lg);
  background: oklch(0.98 0.01 145);

  &__icon {
    font-size: 28px;
    color: var(--color-primary);
  }

  &__info {
    flex: 1;
    display: flex;
    flex-direction: column;
    gap: var(--space-1);
  }

  &__name {
    font-size: var(--font-size-sm);
    font-weight: 500;
    color: var(--color-foreground);
    max-width: 220px;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
  }
}

.hidden {
  display: none;
}
</style>
