<script setup lang="ts">
import axios from 'axios'
import OSS from 'ali-oss'
import '@/lib/aliyun-upload-sdk/aliyun-upload-sdk-1.5.3.min.js'
import { ElMessage } from 'element-plus'
import type { CaseLibraryItem } from '../types'
import { createBook, createCase, createVideo, getUploadVideoAuth, updateUploadVideoAuth } from '../api'
import { upload } from '@/utils/upload'
import { useCaseLibrary } from '../composables/useCaseLibrary'
import { useGetCourseList } from '../composables/useGetCourseList'
import { useGetExperimentList } from '../composables/useGetExperimentList'

;(window as any).OSS = OSS

const { list, loading, fetchList } = useCaseLibrary()
const { courses, updateCourses } = useGetCourseList()
const { experiments, updateExperiments } = useGetExperimentList()

updateCourses()
fetchList()

const selectedCourse = ref('')
const selectedExperiment = ref('')
const selectedCaseId = ref('')
const fileSelections = ref<Record<'case' | 'book' | 'video', string[]>>({
  case: [],
  book: [],
  video: [],
})
const submitting = ref(false)
const progress = ref(0)
const progressText = ref('')
const caseFileTypeMap = {
  case: '1',
  book: '3',
  video: '4',
} as const

watch(selectedCourse, (val) => {
  selectedExperiment.value = ''
  updateExperiments(val)
})

function getFilesByType(files: CaseLibraryItem['files'], type: string) {
  return files.filter((f) => f.type === type)
}

function getMimeType(fileName: string) {
  const ext = fileName.split('.').pop()?.toLowerCase() || ''
  const mimeMap: Record<string, string> = {
    doc: 'application/msword',
    docx: 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
    pdf: 'application/pdf',
    ppt: 'application/vnd.ms-powerpoint',
    pptx: 'application/vnd.openxmlformats-officedocument.presentationml.presentation',
    xls: 'application/vnd.ms-excel',
    xlsx: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
    csv: 'text/csv',
    mp4: 'video/mp4',
  }
  return mimeMap[ext] || 'application/octet-stream'
}

const selectedCase = computed(() => {
  return list.value.find((item) => item.id === selectedCaseId.value) || null
})

const groupedFiles = computed(() => {
  const files = selectedCase.value?.files || []
  return {
    case: getFilesByType(files, caseFileTypeMap.case),
    book: getFilesByType(files, caseFileTypeMap.book),
    video: getFilesByType(files, caseFileTypeMap.video),
  }
})

const selectedCount = computed(() => {
  return fileSelections.value.case.length + fileSelections.value.book.length + fileSelections.value.video.length
})

const selectedItems = computed(() => {
  if (!selectedCase.value) return []
  return (Object.keys(caseFileTypeMap) as Array<'case' | 'book' | 'video'>).flatMap((type) => {
    return fileSelections.value[type]
      .map((key) => {
        const file = selectedCase.value?.files.find(
          (item) => `${item.type}-${item.url}` === key && item.type === caseFileTypeMap[type],
        )
        return file ? { row: selectedCase.value, type, file } : null
      })
      .filter(Boolean)
  })
})

watch(selectedCaseId, () => {
  fileSelections.value = { case: [], book: [], video: [] }
})

function handleCaseChange(caseId: string) {
  selectedCaseId.value = caseId
}

function getFileKey(file: CaseLibraryItem['files'][number]) {
  return `${file.type}-${file.url}`
}

function getAllChecked(type: 'case' | 'book' | 'video') {
  const files = groupedFiles.value[type]
  return !!files.length && fileSelections.value[type].length === files.length
}

function handleToggleAll(type: 'case' | 'book' | 'video', checked: boolean) {
  fileSelections.value[type] = checked ? groupedFiles.value[type].map((file) => getFileKey(file)) : []
}

function setStepProgress(index: number, total: number, ratio: number, label: string) {
  const safeRatio = Math.max(0, Math.min(1, ratio))
  progress.value = Math.round(((index + safeRatio) / total) * 100)
  progressText.value = label
}

async function downloadRemoteFile(url: string, fileName: string, index: number, total: number) {
  const response = await axios.get(url, {
    responseType: 'blob',
    onDownloadProgress: (event) => {
      if (!event.total) {
        setStepProgress(index, total, 0.15, `正在下载：${fileName}`)
        return
      }
      const ratio = event.loaded / event.total
      setStepProgress(index, total, ratio * 0.45, `正在下载 ${Math.round(ratio * 100)}%：${fileName}`)
    },
  })
  return new File([response.data], fileName, {
    type: response.data.type || getMimeType(fileName),
  })
}

async function uploadDocumentFromCase(url: string, fileName: string, index: number, total: number) {
  const file = await downloadRemoteFile(url, fileName, index, total)
  setStepProgress(index, total, 0.55, `正在上传文件：${fileName}`)
  const uploadedUrl = await upload(file)
  return {
    url: uploadedUrl,
    size: (file.size / 1024 / 1024).toString(),
  }
}

async function uploadVideoFromCase(url: string, fileName: string, index: number, total: number) {
  const file = await downloadRemoteFile(url, fileName, index, total)
  return new Promise<string>((resolve, reject) => {
    const uploader = new (window as any).AliyunUpload.Vod({
      userId: '1303984639806000',
      region: 'cn-shanghai',
      partSize: 1048576,
      parallel: 5,
      retryCount: 3,
      retryDuration: 2,
      onUploadstarted(uploadInfo: any) {
        setStepProgress(index, total, 0.55, `正在上传视频：${uploadInfo.file.name}`)
        getUploadVideoAuth({ title: uploadInfo.file.name, file_name: uploadInfo.file.name })
          .then((res) => {
            uploader.setUploadAuthAndAddress(
              uploadInfo,
              res.data.upload_auth,
              res.data.upload_address,
              res.data.source_id,
            )
          })
          .catch(reject)
      },
      onUploadSucceed(uploadInfo: any) {
        setStepProgress(index, total, 0.95, `视频上传完成：${uploadInfo.file.name}`)
        resolve(uploadInfo.videoId)
      },
      onUploadFailed(_uploadInfo: any, code: number, message: string) {
        reject(new Error(message || `视频上传失败: ${code}`))
      },
      onUploadProgress(_uploadInfo: any, _totalSize: number, loadedPercent: number) {
        setStepProgress(
          index,
          total,
          0.55 + loadedPercent * 0.4,
          `正在上传视频 ${Math.round(loadedPercent * 100)}%：${fileName}`,
        )
      },
      onUploadTokenExpired(uploadInfo: any) {
        updateUploadVideoAuth({ source_id: uploadInfo.videoId })
          .then((res) => {
            uploader.resumeUploadWithAuth(res.data.UploadAuth)
          })
          .catch(reject)
      },
      onUploadEnd() {},
    })

    uploader.addFile(file, null, null, null, '{"Vod":{}}')
    uploader.startUpload()
  })
}

async function handleBatchAuthorize() {
  if (!selectedExperiment.value) {
    ElMessage.warning('请选择实验')
    return
  }
  if (!selectedItems.value.length) {
    ElMessage.warning('请勾选需要授权的案例文件')
    return
  }

  const successStats = { case: 0, book: 0, video: 0 }
  const total = selectedItems.value.length

  submitting.value = true
  progress.value = 0
  progressText.value = `准备处理 0/${total}`
  try {
    for (let index = 0; index < selectedItems.value.length; index += 1) {
      const item = selectedItems.value[index]
      if (!item) continue
      const { type, file } = item
      setStepProgress(index, total, 0.05, `准备处理 ${index + 1}/${total}：${file.name}`)

      if (type === 'case') {
        const uploadedFile = await uploadDocumentFromCase(file.url, file.name, index, total)
        setStepProgress(index, total, 0.95, `正在创建案例原文：${file.name}`)
        await createCase({
          experiment_id: selectedExperiment.value,
          name: file.name.replace(/\.[^.]+$/, ''),
          type: getMimeType(file.name),
          url: uploadedFile.url,
          size: uploadedFile.size,
          status: '1',
        })
        successStats.case += 1
        progress.value = Math.round(((index + 1) / total) * 100)
        continue
      }

      if (type === 'book') {
        const uploadedFile = await uploadDocumentFromCase(file.url, file.name, index, total)
        setStepProgress(index, total, 0.95, `正在创建案例指导书：${file.name}`)
        await createBook({
          experiment_id: selectedExperiment.value,
          name: file.name.replace(/\.[^.]+$/, ''),
          type: getMimeType(file.name),
          url: uploadedFile.url,
          size: uploadedFile.size,
          status: '1',
        })
        successStats.book += 1
        progress.value = Math.round(((index + 1) / total) * 100)
        continue
      }

      const sourceId = await uploadVideoFromCase(file.url, file.name, index, total)
      setStepProgress(index, total, 0.98, `正在创建案例操作视频：${file.name}`)

      await createVideo({
        experiment_id: selectedExperiment.value,
        name: file.name.replace(/\.[^.]+$/, ''),
        source_id: sourceId,
        status: '1',
      })
      successStats.video += 1
      progress.value = Math.round(((index + 1) / total) * 100)
    }

    if (!successStats.case && !successStats.book && !successStats.video) {
      ElMessage.warning('没有可授权的文件，案例视频缺少 source_id 时无法直接授权')
      return
    }

    ElMessage.success(
      `授权完成：案例原文 ${successStats.case} 个，指导书 ${successStats.book} 个，视频 ${successStats.video} 个`,
    )
    progressText.value = `授权完成 ${total}/${total}`
  } catch {
    ElMessage.error('批量授权失败')
    progressText.value = '授权失败'
  } finally {
    submitting.value = false
  }
}
</script>

<template>
  <AppCard title="案例授权">
    <div class="case-auth-toolbar">
      <el-select v-model="selectedCourse" placeholder="请选择课程" filterable clearable style="width: 200px">
        <el-option v-for="item in courses" :key="item.id" :label="item.name" :value="item.id" />
      </el-select>
      <el-select v-model="selectedExperiment" placeholder="请选择实验" filterable clearable style="width: 200px">
        <el-option v-for="item in experiments" :key="item.id" :label="item.name" :value="item.id" />
      </el-select>
      <el-button type="primary" :loading="submitting" @click="handleBatchAuthorize" :disabled="!selectedCount"
        >批量授权到实验</el-button
      >
    </div>
    <div v-if="selectedCount || submitting || progress" class="case-auth-progress">
      <div class="case-auth-progress__header">
        <span>{{ progressText || `准备处理 0/${selectedCount}` }}</span>
        <span>{{ progress }}%</span>
      </div>
      <el-progress :percentage="progress" :show-text="false" />
    </div>
    <div v-loading="loading" class="case-list">
      <div
        v-for="item in list"
        :key="item.id"
        class="case-card"
        :class="{ 'case-card--active': selectedCaseId === item.id }"
        @click="handleCaseChange(item.id)">
        <div class="case-card__top">
          <div>
            <div class="case-card__name">{{ item.name }}</div>
            <div class="case-card__id">{{ item.id }}</div>
          </div>
          <el-radio :model-value="selectedCaseId === item.id" :label="true">选择</el-radio>
        </div>
        <div class="case-card__desc">{{ item.description || '暂无案例简介' }}</div>
        <div class="case-card__meta">
          <el-tag size="small" :type="getFilesByType(item.files, caseFileTypeMap.case).length ? 'success' : 'info'"
            >原文 {{ getFilesByType(item.files, caseFileTypeMap.case).length }}</el-tag
          >
          <el-tag size="small" :type="getFilesByType(item.files, caseFileTypeMap.book).length ? 'success' : 'info'"
            >指导书 {{ getFilesByType(item.files, caseFileTypeMap.book).length }}</el-tag
          >
          <el-tag size="small" :type="getFilesByType(item.files, caseFileTypeMap.video).length ? 'success' : 'info'"
            >视频 {{ getFilesByType(item.files, caseFileTypeMap.video).length }}</el-tag
          >
        </div>
      </div>
    </div>

    <div v-if="selectedCase" class="case-auth-sections">
      <div class="case-auth-section">
        <div class="case-auth-section__header">
          <div class="case-auth-section__title">案例原文</div>
          <el-checkbox
            :model-value="getAllChecked('case')"
            :disabled="!groupedFiles.case.length"
            @update:model-value="(value) => handleToggleAll('case', !!value)">
            全选
          </el-checkbox>
        </div>
        <el-checkbox-group v-model="fileSelections.case" class="case-auth-options">
          <el-checkbox v-for="file in groupedFiles.case" :key="getFileKey(file)" :label="getFileKey(file)">
            {{ file.name }}
          </el-checkbox>
        </el-checkbox-group>
        <el-empty v-if="!groupedFiles.case.length" description="暂无案例原文" :image-size="72" />
      </div>

      <div class="case-auth-section">
        <div class="case-auth-section__header">
          <div class="case-auth-section__title">案例指导书</div>
          <el-checkbox
            :model-value="getAllChecked('book')"
            :disabled="!groupedFiles.book.length"
            @update:model-value="(value) => handleToggleAll('book', !!value)">
            全选
          </el-checkbox>
        </div>
        <el-checkbox-group v-model="fileSelections.book" class="case-auth-options">
          <el-checkbox v-for="file in groupedFiles.book" :key="getFileKey(file)" :label="getFileKey(file)">
            {{ file.name }}
          </el-checkbox>
        </el-checkbox-group>
        <el-empty v-if="!groupedFiles.book.length" description="暂无案例指导书" :image-size="72" />
      </div>

      <div class="case-auth-section">
        <div class="case-auth-section__header">
          <div class="case-auth-section__title">案例操作视频</div>
          <el-checkbox
            :model-value="getAllChecked('video')"
            :disabled="!groupedFiles.video.length"
            @update:model-value="(value) => handleToggleAll('video', !!value)">
            全选
          </el-checkbox>
        </div>
        <el-checkbox-group v-model="fileSelections.video" class="case-auth-options">
          <el-checkbox v-for="file in groupedFiles.video" :key="getFileKey(file)" :label="getFileKey(file)">
            {{ file.name }}
          </el-checkbox>
        </el-checkbox-group>
        <el-empty v-if="!groupedFiles.video.length" description="暂无案例操作视频" :image-size="72" />
      </div>
    </div>
  </AppCard>
</template>

<style lang="scss" scoped>
.case-auth-toolbar {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 10px;
  margin-bottom: 16px;
}

.case-auth-progress {
  margin-bottom: 16px;
  padding: 12px 16px;
  background: var(--el-fill-color-light);
  border-radius: 8px;
}

.case-auth-progress__header {
  display: flex;
  justify-content: space-between;
  gap: 12px;
  margin-bottom: 8px;
  font-size: 14px;
  color: var(--el-text-color-regular);
}

.case-list {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 16px;
}

.case-card {
  padding: 16px;
  border: 1px solid var(--el-border-color);
  border-radius: 10px;
  background: #fff;
  cursor: pointer;
  transition:
    border-color 0.2s ease,
    box-shadow 0.2s ease;
}

.case-card:hover,
.case-card--active {
  border-color: var(--el-color-primary);
  box-shadow: 0 0 0 2px rgb(64 158 255 / 12%);
}

.case-card__top {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 12px;
  margin-bottom: 10px;
}

.case-card__name {
  font-size: 16px;
  font-weight: 600;
  color: var(--el-text-color-primary);
}

.case-card__id {
  margin-top: 4px;
  font-size: 12px;
  color: var(--el-text-color-secondary);
}

.case-card__desc {
  min-height: 44px;
  font-size: 14px;
  line-height: 1.6;
  color: var(--el-text-color-regular);
}

.case-card__meta {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  margin-top: 12px;
}

.case-auth-sections {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 16px;
  margin-top: 16px;
}

.case-auth-section {
  padding: 16px;
  background: var(--el-fill-color-light);
  border-radius: 8px;
  min-height: 180px;
}

.case-auth-section__header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  margin-bottom: 12px;
}

.case-auth-section__title {
  font-size: 15px;
  font-weight: 600;
  color: var(--el-text-color-primary);
}

.case-auth-options {
  display: flex;
  flex-direction: column;
  gap: 10px;
}

@media (max-width: 1200px) {
  .case-list {
    grid-template-columns: 1fr;
  }

  .case-auth-sections {
    grid-template-columns: 1fr;
  }
}
</style>
