<template>
  <div class="group-course-management">
    <el-card>
      <template #header>
        <div class="card-header">
          <div class="search-section">
            <el-input
              v-model="searchKeyword"
              placeholder="搜索课程名称"
              clearable
              style="width: 240px"
              @clear="handleSearch"
              @keyup.enter="handleSearch"
            >
              <template #prefix>
                <el-icon><Search /></el-icon>
              </template>
            </el-input>
            <el-select
              v-model="searchStatus"
              placeholder="课程状态"
              clearable
              style="width: 140px"
              @change="handleSearch"
            >
              <el-option value="0" label="正常" />
              <el-option value="1" label="已取消" />
              <el-option value="2" label="已结束" />
              <el-option value="3" label="进行中" />
              <el-option value="5" label="教练缺席" />
              <el-option value="6" label="自动结束" />
              <el-option value="7" label="教练迟到" />
            </el-select>
            <el-button type="primary" @click="handleSearch">
              搜索
            </el-button>
            <el-button @click="fetchData" :loading="loading" circle>
              <el-icon><Refresh /></el-icon>
            </el-button>
          </div>
          <el-button type="primary" @click="handleAdd">
            新增团课
          </el-button>
        </div>
      </template>
      <el-table
        v-loading="loading"
        :data="dataSource"
        style="width: 100%"
        @sort-change="handleSortChange"
      >
        <el-table-column prop="id" label="ID" width="80" sortable="custom" />
        <el-table-column label="封面" width="100">
          <template #default="{ row }">
            <div class="cover-cell">
              <img
                v-if="coverCache[row.coverImage]"
                :src="coverCache[row.coverImage]"
                class="cover-thumb"
                @error="onCoverError(row)"
              />
              <span v-else class="cover-no-data">No data</span>
            </div>
          </template>
        </el-table-column>
        <el-table-column prop="courseName" label="课程名称" min-width="140" />
        <el-table-column label="课程类型" width="100">
          <template #default="{ row }">
            {{ getCourseTypeName(row.courseType) }}
          </template>
        </el-table-column>
        <el-table-column label="教练" width="100">
          <template #default="{ row }">
            {{ getCoachDisplayName(row.coachId) || row.coachName || '-' }}
          </template>
        </el-table-column>
        <el-table-column label="状态" width="90">
          <template #default="{ row }">
            <el-tag
              :type="statusMap[row.status]?.type || 'info'"
              effect="dark"
              style="font-weight: 500; font-size: 14px;"
            >
              {{ statusMap[row.status]?.label || row.status }}
            </el-tag>
          </template>
        </el-table-column>
        <el-table-column prop="startTime" label="开始时间" width="170">
          <template #default="{ row }">
            {{ formatDateTime(row.actualStartTime || row.startTime) }}
          </template>
        </el-table-column>
        <el-table-column prop="endTime" label="结束时间" width="170">
          <template #default="{ row }">
            {{ formatDateTime(row.actualEndTime || row.endTime) }}
          </template>
        </el-table-column>
        <el-table-column prop="location" label="上课地点" width="120" />
        <el-table-column label="人数" width="90">
          <template #default="{ row }">
            {{ realMemberCounts[row.id!] ?? row.currentMembers ?? 0 }}/{{ row.maxMembers || 0 }}
          </template>
        </el-table-column>
        <el-table-column label="操作" width="320" fixed="right">
          <template #default="{ row }">
            <el-button type="primary" link size="small" @click="handleEdit(row)">
              编辑
            </el-button>
            <el-button type="info" link size="small" @click="showDetail(row)">
              详情
            </el-button>
            <el-button type="success" link size="small" @click="showQrCode(row)">
              二维码
            </el-button>
            <el-button
              v-if="row.status === '0'"
              type="warning"
              link
              size="small"
              @click="handleCancel(row)"
            >
              取消
            </el-button>
            <el-button type="danger" link size="small" @click="handleDelete(row)">
              删除
            </el-button>
          </template>
        </el-table-column>
      </el-table>
      <el-pagination
        v-model:current-page="pagination.current"
        v-model:page-size="pagination.pageSize"
        :page-sizes="[10, 20, 50, 100]"
        :total="pagination.total"
        layout="total, sizes, prev, pager, next, jumper"
        style="margin-top: 16px; justify-content: flex-end"
        @current-change="handleTableChange"
        @size-change="handleSizeChange"
      />
    </el-card>

    <el-dialog
      v-model="modalVisible"
      :title="modalTitle"
      width="600px"
    >
      <el-form
        ref="formRef"
        :model="formState"
        :rules="formRules"
        label-width="120px"
      >
        <el-form-item label="课程名称" prop="courseName">
          <el-input v-model="formState.courseName" placeholder="请输入课程名称" />
        </el-form-item>
        <el-form-item label="课程类型" prop="courseType">
          <el-select v-model="formState.courseType" placeholder="请选择课程类型" style="width: 100%">
            <el-option
              v-for="type in courseTypes"
              :key="type.id"
              :value="type.id"
              :label="type.typeName"
            />
          </el-select>
        </el-form-item>
        <el-form-item label="教练" prop="coachId">
          <el-select
            v-model="formState.coachId"
            placeholder="请选择教练"
            clearable
            style="width: 100%"
          >
            <el-option
              v-for="coach in coaches"
              :key="coach.id"
              :value="coach.id"
              :label="`${coach.nickname || coach.username} (${coach.username})`"
            />
          </el-select>
          <div v-if="coachConflictWarning" style="margin-top: 6px; color: #e6a23c; font-size: 12px; line-height: 1.5;">
            <el-icon style="vertical-align: middle;"><WarningFilled /></el-icon>
            {{ coachConflictWarning }}
          </div>
          <div v-if="coachConflictChecking" style="margin-top: 4px; color: #909399; font-size: 12px;">
            正在检查教练排期...
          </div>
        </el-form-item>
        <el-form-item label="开始时间" prop="startTime">
          <el-date-picker
            v-model="formState.startTime"
            type="datetime"
            placeholder="选择开始时间"
            value-format="YYYY-MM-DDTHH:mm:ss"
            :disabled-date="notBeforeToday"
            style="width: 100%"
          />
        </el-form-item>
        <el-form-item label="结束时间" prop="endTime" v-if="false">
          <span />
        </el-form-item>
        <el-form-item label="持续时间(分钟)" prop="duration">
          <el-input-number
            v-model="formState.duration"
            :min="1"
            :max="1440"
            placeholder="请输入课程持续时间"
            style="width: 100%"
          />
        </el-form-item>
        <el-form-item label="最大人数" prop="maxMembers">
          <el-input-number v-model="formState.maxMembers" :min="1" :max="999" style="width: 100%" />
        </el-form-item>
        <el-form-item label="上课地点" prop="location">
          <el-input v-model="formState.location" placeholder="请输入上课地点" />
        </el-form-item>
        <el-form-item label="封面图片">
          <el-upload
            class="cover-upload"
            :http-request="uploadCover"
            :before-upload="beforeCoverUpload"
            :show-file-list="false"
            accept="image/*"
          >
            <img v-if="coverPreviewSrc" :src="coverPreviewSrc" class="cover-preview" />
            <el-icon v-else class="cover-upload-icon"><Plus /></el-icon>
          </el-upload>
        </el-form-item>
        <el-form-item v-if="formState.id" label="课程状态" prop="status">
          <el-select v-model="formState.status" placeholder="请选择状态" style="width: 100%">
            <el-option value="0" label="正常" />
            <el-option value="1" label="已取消" />
          </el-select>
        </el-form-item>
        <el-form-item label="储值卡额度">
          <el-input-number v-model="formState.storedValueAmount" :min="0" :precision="2" style="width: 100%" />
        </el-form-item>
        <el-form-item label="课程描述">
          <el-input
            v-model="formState.description"
            type="textarea"
            :rows="3"
            placeholder="请输入课程描述"
          />
        </el-form-item>
      </el-form>
      <template #footer>
        <el-button @click="modalVisible = false">取消</el-button>
        <el-button type="primary" @click="handleModalOk">确定</el-button>
      </template>
    </el-dialog>

    <!-- 签到二维码弹窗 -->
    <el-dialog
      v-model="qrDialogVisible"
      :title="'签到二维码 - ' + currentCourseName"
      width="420px"
      center
    >
      <div class="qr-dialog-body">
        <div v-loading="qrLoading" class="qr-image-wrapper">
          <img
            v-if="qrImageUrl"
            :src="qrImageUrl"
            class="qr-image"
            alt="签到二维码"
          />
          <el-empty v-else-if="!qrLoading" description="暂无二维码" />
        </div>
        <p class="qr-tip">扫码签到参加团课</p>
      </div>
      <template #footer>
        <el-button @click="qrDialogVisible = false">关闭</el-button>
        <el-button type="primary" :disabled="!qrImageUrl" @click="downloadQrCode">
          下载二维码
        </el-button>
      </template>
    </el-dialog>

    <!-- 团课详情弹窗 -->
    <el-dialog
      v-model="detailVisible"
      title="团课详情"
      width="640px"
    >
      <el-descriptions :column="2" border>
        <el-descriptions-item label="ID">{{ detailRow.id }}</el-descriptions-item>
        <el-descriptions-item label="课程名称">{{ detailRow.courseName }}</el-descriptions-item>
        <el-descriptions-item label="课程类型">{{ getCourseTypeName(detailRow.courseType) }}</el-descriptions-item>
        <el-descriptions-item label="教练">{{ getCoachDisplayName(detailRow.coachId) || detailRow.coachName || '-' }}</el-descriptions-item>
        <el-descriptions-item label="课程状态">
          <el-tag :type="statusMap[detailRow.status || '']?.type || 'info'" effect="dark" size="small">
            {{ statusMap[detailRow.status || '']?.label || detailRow.status }}
          </el-tag>
        </el-descriptions-item>
        <el-descriptions-item label="上课地点">{{ detailRow.location || '-' }}</el-descriptions-item>
        <el-descriptions-item label="计划开始时间">{{ formatDateTime(detailRow.startTime) }}</el-descriptions-item>
        <el-descriptions-item label="实际开课时间">{{ formatDateTime(detailRow.actualStartTime) || '-' }}</el-descriptions-item>
        <el-descriptions-item label="计划结束时间">{{ formatDateTime(detailRow.endTime) }}</el-descriptions-item>
        <el-descriptions-item label="实际结课时间">{{ formatDateTime(detailRow.actualEndTime) || '-' }}</el-descriptions-item>
        <el-descriptions-item label="人数">{{ realMemberCounts[detailRow.id!] ?? detailRow.currentMembers ?? 0 }} / {{ detailRow.maxMembers || 0 }}</el-descriptions-item>
        <el-descriptions-item label="储值卡额度">{{ detailRow.storedValueAmount ?? '-' }}</el-descriptions-item>
        <el-descriptions-item label="封面图片">
          <img v-if="detailRow.coverImage && coverCache[detailRow.coverImage]" :src="coverCache[detailRow.coverImage]" class="detail-cover" />
          <span v-else>-</span>
        </el-descriptions-item>
        <el-descriptions-item label="课程描述" :span="2">{{ detailRow.description || '-' }}</el-descriptions-item>
        <el-descriptions-item label="创建人">{{ detailRow.createBy || '-' }}</el-descriptions-item>
        <el-descriptions-item label="更新人">{{ detailRow.updateBy || '-' }}</el-descriptions-item>
        <el-descriptions-item label="创建时间">{{ formatDateTime(detailRow.createdAt) }}</el-descriptions-item>
        <el-descriptions-item label="更新时间">{{ formatDateTime(detailRow.updatedAt) }}</el-descriptions-item>
      </el-descriptions>
      <template #footer>
        <el-button @click="detailVisible = false">关闭</el-button>
      </template>
    </el-dialog>
  </div>
</template>

<script setup lang="ts">
import { ref, reactive, watch, onMounted, onUnmounted } from 'vue'
import { ElMessage, ElMessageBox } from 'element-plus'
import { Search, Plus, WarningFilled, Refresh } from '@element-plus/icons-vue'
import { groupCourseApi, groupCourseTypeApi, groupCourseBookingApi, type GroupCourse, type GroupCourseType } from '@/api/groupCourse.api'
import { coachApi, type Coach } from '@/api/coach.api'
import request from '@/utils/request'
import { formatDateTime } from '@/utils/dateFormat'

const loading = ref(false)
const dataSource = ref<GroupCourse[]>([])
const searchKeyword = ref('')
const searchStatus = ref('')
const courseTypes = ref<GroupCourseType[]>([])
const coaches = ref<Coach[]>([])
const realMemberCounts = reactive<Record<number, number>>({})

const statusMap: Record<string, { type: string; label: string }> = {
  '0': { type: 'success', label: '正常' },
  '1': { type: 'danger', label: '已取消' },
  '2': { type: 'info', label: '已结束' },
  '3': { type: '', label: '进行中' },
  '5': { type: 'danger', label: '教练缺席' },
  '6': { type: 'warning', label: '自动结束' },
  '7': { type: 'warning', label: '教练迟到' },
}

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

const sortInfo = reactive({
  sortBy: 'id',
  sortOrder: 'asc' as 'asc' | 'desc',
})

const modalVisible = ref(false)
const modalTitle = ref('')
const formRef = ref()
const coachConflictWarning = ref('')
const coachConflictChecking = ref(false)
const formState = reactive<GroupCourse>({
  courseName: '',
  courseType: undefined,
  coachId: undefined,
  startTime: '',
  endTime: '',
  maxMembers: 20,
  location: '',
  coverImage: '',
  description: '',
  storedValueAmount: 0,
  status: '0',
})

const duration = ref(60)
// @ts-expect-error duration is a frontend-only field
formState.duration = duration

const formRules = {
  courseName: [
    { required: true, message: '请输入课程名称', trigger: 'blur' },
    { min: 2, max: 100, message: '课程名称长度在 2 到 100 个字符', trigger: 'blur' },
  ],
  startTime: [{ required: true, message: '请选择开始时间', trigger: 'change' }],
  duration: [{ required: true, message: '请输入课程持续时间', trigger: 'blur' }],
  maxMembers: [{ required: true, message: '请输入最大人数', trigger: 'blur' }],
}

const coverCache = reactive<Record<string, string>>({})

// ===== 详情弹窗 =====
const detailVisible = ref(false)
const detailRow = ref<GroupCourse>({} as GroupCourse)

const showDetail = (row: GroupCourse) => {
  detailRow.value = row
  detailVisible.value = true
  if (row.coverImage) {
    loadRowCover(row.coverImage)
  }
}

const loadRowCover = async (coverImage: string | undefined) => {
  if (!coverImage) return
  // already loaded
  if (coverCache[coverImage]) return
  // Extract file ID from either /api/files/{id}/preview format or pure numeric string
  const match = coverImage.match(/\/api\/files\/(\d+)\/preview$/)
  const fileId = match ? match[1] : (/^\d+$/.test(coverImage) ? coverImage : null)
  if (fileId) {
    try {
      const blob: any = await request.get(`/files/${fileId}/preview`, { responseType: 'blob' })
      coverCache[coverImage] = URL.createObjectURL(blob)
    } catch {
      // leave empty, template will show "No data"
    }
  } else {
    // legacy URL — use directly, but may fail at load time
    coverCache[coverImage] = coverImage
  }
}

const onCoverError = (row: GroupCourse) => {
  if (row.coverImage) {
    delete coverCache[row.coverImage]
  }
}

const fetchCourseTypes = async () => {
  try {
    courseTypes.value = await groupCourseTypeApi.getAll()
  } catch {
    // ignore
  }
}

const fetchCoaches = async () => {
  try {
    coaches.value = await coachApi.getAll()
  } catch {
    // ignore
  }
}

const getCourseTypeName = (typeId: number | undefined): string => {
  if (typeId === undefined || typeId === null) return '-'
  const type = courseTypes.value.find(t => t.id === typeId)
  return type?.typeName || String(typeId)
}

const getCoachDisplayName = (coachId: number | string | undefined): string => {
  if (coachId === undefined || coachId === null) return ''
  const coach = coaches.value.find(c => String(c.id) === String(coachId))
  if (!coach) return ''
  return coach.nickname || coach.username
}

const getComputedEndTime = (): string => {
  if (!formState.startTime || !(formState as any).duration) return ''
  const start = new Date(formState.startTime)
  if (isNaN(start.getTime())) return ''
  start.setMinutes(start.getMinutes() + (formState as any).duration)
  const pad = (n: number) => String(n).padStart(2, '0')
  return `${start.getFullYear()}-${pad(start.getMonth() + 1)}-${pad(start.getDate())}T${pad(start.getHours())}:${pad(start.getMinutes())}:${pad(start.getSeconds())}`
}

const checkCoachConflict = async () => {
  const coachId = formState.coachId
  const startTime = formState.startTime
  const durationVal = (formState as any).duration
  if (!coachId || !startTime || !durationVal) {
    coachConflictWarning.value = ''
    return
  }
  const endTime = getComputedEndTime()
  if (!endTime) {
    coachConflictWarning.value = ''
    return
  }
  coachConflictChecking.value = true
  try {
    const res = await groupCourseApi.checkCoachConflict({
      coachId,
      startTime,
      endTime,
      excludeCourseId: formState.id,
    })
    if ((res as any).hasConflict && (res as any).conflicts?.length > 0) {
      const names = (res as any).conflicts.map((c: any) => c.courseName || '未知课程').join('、')
      coachConflictWarning.value = `该教练在此时间段已有团课：${names}`
    } else {
      coachConflictWarning.value = ''
    }
  } catch {
    coachConflictWarning.value = ''
  } finally {
    coachConflictChecking.value = false
  }
}

const fetchData = async () => {
  loading.value = true
  try {
    const res = await groupCourseApi.getPage({
      page: pagination.current - 1,
      size: pagination.pageSize,
      sortBy: sortInfo.sortBy,
      sortOrder: sortInfo.sortOrder,
      keyword: searchKeyword.value || undefined,
      status: searchStatus.value || undefined,
    })
    dataSource.value = res.content
    pagination.total = Number(res.totalElements) || 0
    // load cover images for all rows
    res.content.forEach((row: GroupCourse) => loadRowCover(row.coverImage))
    // 实时获取预约人数
    fetchRealMemberCounts(res.content)
  } catch {
    ElMessage.error('加载数据失败')
  } finally {
    loading.value = false
  }
}

const fetchRealMemberCounts = async (courses: GroupCourse[]) => {
  // 批量获取每门课程的实时预约人数
  const results = await Promise.allSettled(
    courses.map(course =>
      groupCourseBookingApi.getByCourseId(course.id!).then(bookings => ({
        courseId: course.id!,
        count: bookings.filter(b => b.status === '0' || b.status === '2').length,
      }))
    )
  )
  results.forEach(r => {
    if (r.status === 'fulfilled') {
      realMemberCounts[r.value.courseId] = r.value.count
    }
  })
}

const handleTableChange = () => fetchData()
const handleSizeChange = () => {
  pagination.current = 1
  fetchData()
}
const handleSearch = () => {
  pagination.current = 1
  fetchData()
}

const handleSortChange = ({ prop, order }: any) => {
  sortInfo.sortBy = prop
  sortInfo.sortOrder = order === 'ascending' ? 'asc' : 'desc'
  fetchData()
}

const beforeCoverUpload = (file: File) => {
  const isImage = file.type.startsWith('image/')
  if (!isImage) {
    ElMessage.error('只能上传图片文件')
    return false
  }
  const isLt5M = file.size / 1024 / 1024 < 5
  if (!isLt5M) {
    ElMessage.error('图片大小不能超过 5MB')
    return false
  }
  return true
}

const notBeforeToday = (date: Date) => {
  const today = new Date()
  today.setHours(0, 0, 0, 0)
  return date.getTime() < today.getTime()
}

const uploadCover = async (options: any) => {
  const formData = new FormData()
  formData.append('file', options.file)
  try {
    const res: any = await request.post('/files/upload', formData, {
      headers: { 'Content-Type': 'multipart/form-data' },
    })
    // res is the SysFile object (axios interceptor unwrapped response.data)
    if (res && res.id) {
      formState.coverImage = `/api/files/${res.id}/preview`
      // immediately fetch preview for the newly uploaded file
      loadCoverPreview(formState.coverImage)
    }
    ElMessage.success('封面上传成功')
  } catch {
    ElMessage.error('封面上传失败')
  }
}

const coverPreviewSrc = ref('')

// ===== 二维码弹窗相关 =====
const qrDialogVisible = ref(false)
const qrLoading = ref(false)
const qrImageUrl = ref('')
const currentCourseName = ref('')

/**
 * 从 qrCodePath 中提取文件 ID
 * 支持格式：/api/files/preview/{id} 或旧版 OSS URL
 */
const extractFileId = (path: string | undefined): string | null => {
  if (!path) return null
  // 新格式: /api/files/{id}/preview
  const match = path.match(/\/files\/(\d+)\/preview/)
  if (match) return match[1]
  // 旧格式 OSS URL: 直接作为图片 URL 使用
  if (path.startsWith('http')) return path
  // 纯数字 ID
  if (/^\d+$/.test(path)) return path
  return null
}

/**
 * 显示团课签到二维码
 */
const showQrCode = async (row: GroupCourse) => {
  currentCourseName.value = row.courseName
  qrDialogVisible.value = true
  qrLoading.value = true
  qrImageUrl.value = ''

  const fileId = extractFileId(row.qrCodePath)
  if (!fileId) {
    qrLoading.value = false
    return
  }

  try {
    if (fileId.startsWith('http')) {
      // 旧版 OSS URL，直接使用
      qrImageUrl.value = fileId
    } else {
      // 新版文件 ID，通过预览接口获取
      const blob: any = await request.get(`/files/${fileId}/preview`, { responseType: 'blob' })
      // 释放旧 URL
      if (qrImageUrl.value) URL.revokeObjectURL(qrImageUrl.value)
      qrImageUrl.value = URL.createObjectURL(blob)
    }
  } catch {
    ElMessage.error('获取二维码失败')
  } finally {
    qrLoading.value = false
  }
}

/**
 * 下载二维码图片
 */
const downloadQrCode = () => {
  if (!qrImageUrl.value) return
  const a = document.createElement('a')
  a.href = qrImageUrl.value
  a.download = `签到二维码_${currentCourseName.value}.png`
  document.body.appendChild(a)
  a.click()
  document.body.removeChild(a)
  ElMessage.success('二维码下载成功')
}

const loadCoverPreview = async (val: string) => {
  if (!val) {
    coverPreviewSrc.value = ''
    return
  }
  // Extract file ID from either /api/files/{id}/preview format or pure numeric string
  const match = val.match(/\/api\/files\/(\d+)\/preview$/)
  const fileId = match ? match[1] : (/^\d+$/.test(val) ? val : null)
  if (fileId) {
    try {
      const blob: any = await request.get(`/files/${fileId}/preview`, { responseType: 'blob' })
      const oldUrl = coverPreviewSrc.value
      if (oldUrl) URL.revokeObjectURL(oldUrl)
      coverPreviewSrc.value = URL.createObjectURL(blob)
    } catch {
      coverPreviewSrc.value = ''
    }
  } else {
    // Legacy: direct URL
    coverPreviewSrc.value = val
  }
}

// Watch coverImage changes to auto-load preview (e.g. when editing an existing course)
watch(() => formState.coverImage, (val) => loadCoverPreview(val || ''), { immediate: true })

// 监听教练选择和时间变化，检查冲突
watch(() => [formState.coachId, formState.startTime, (formState as any).duration] as const, () => {
  checkCoachConflict()
}, { deep: false })

// 打开弹窗时清除冲突提示
watch(modalVisible, (val) => {
  if (val) {
    coachConflictWarning.value = ''
  }
})

const handleAdd = () => {
  modalTitle.value = '新增团课'
  Object.assign(formState, {
    id: undefined,
    courseName: '',
    courseType: undefined,
    coachId: undefined,
    startTime: '',
    endTime: '',
    maxMembers: 20,
    location: '',
    coverImage: '',
    description: '',
    storedValueAmount: 0,
  })
  formState.duration = 60
  modalVisible.value = true
}

const handleEdit = (row: GroupCourse) => {
  modalTitle.value = '编辑团课'
  Object.assign(formState, { ...row })
  // Calculate duration from startTime and endTime
  if (row.startTime && row.endTime) {
    const start = new Date(row.startTime)
    const end = new Date(row.endTime)
    formState.duration = Math.max(1, Math.round((end.getTime() - start.getTime()) / 60000))
  }
  modalVisible.value = true
}

const handleCancel = async (row: GroupCourse) => {
  try {
    await ElMessageBox.confirm('确定要取消该团课吗？', '提示', {
      confirmButtonText: '确定',
      cancelButtonText: '取消',
      type: 'warning',
    })
    await groupCourseApi.cancel(row.id!)
    ElMessage.success('取消成功')
    fetchData()
  } catch (error) {
    if (error !== 'cancel') {
      ElMessage.error('取消失败')
    }
  }
}

const handleDelete = async (row: GroupCourse) => {
  try {
    await ElMessageBox.confirm('确定要删除该团课吗？', '提示', {
      confirmButtonText: '确定',
      cancelButtonText: '取消',
      type: 'warning',
    })
    await groupCourseApi.delete(row.id!)
    ElMessage.success('删除成功')
    fetchData()
  } catch (error) {
    if (error !== 'cancel') {
      ElMessage.error('删除失败')
    }
  }
}

const handleModalOk = async () => {
  if (!formRef.value) return
  try {
    await formRef.value.validate()
    // Auto-calculate endTime from startTime + duration
    if (formState.startTime && (formState as any).duration) {
      const start = new Date(formState.startTime)
      start.setMinutes(start.getMinutes() + (formState as any).duration)
      const pad = (n: number) => String(n).padStart(2, '0')
      formState.endTime = `${start.getFullYear()}-${pad(start.getMonth() + 1)}-${pad(start.getDate())}T${pad(start.getHours())}:${pad(start.getMinutes())}:${pad(start.getSeconds())}`
    }
    // 最终教练冲突检查
    if (formState.coachId && formState.startTime && formState.endTime) {
      try {
        const res = await groupCourseApi.checkCoachConflict({
          coachId: formState.coachId,
          startTime: formState.startTime,
          endTime: formState.endTime,
          excludeCourseId: formState.id,
        })
        if ((res as any).hasConflict && (res as any).conflicts?.length > 0) {
          const names = (res as any).conflicts.map((c: any) => c.courseName || '未知课程').join('、')
          await ElMessageBox.confirm(
            `该教练在此时间段已有团课：${names}，确定继续吗？`,
            '时间冲突警告',
            { confirmButtonText: '继续保存', cancelButtonText: '取消', type: 'warning' }
          )
          // 用户点击了"继续保存"，继续执行
        }
      } catch (e) {
        // 用户取消了冲突确认对话框，中止保存
        return
      }
    }
    if (formState.id) {
      await groupCourseApi.update(formState.id, formState)
    } else {
      await groupCourseApi.create(formState)
    }
    ElMessage.success('操作成功')
    modalVisible.value = false
    fetchData()
  } catch (error) {
    if (error !== 'cancel') {
      ElMessage.error('操作失败')
    }
  }
}

let refreshTimer: ReturnType<typeof setInterval> | null = null

onMounted(async () => {
  await Promise.all([fetchCourseTypes(), fetchCoaches()])
  fetchData()
  // 每30秒自动刷新列表，捕获调度器状态变更
  refreshTimer = setInterval(() => {
    fetchData()
  }, 30000)
})

onUnmounted(() => {
  if (refreshTimer) {
    clearInterval(refreshTimer)
    refreshTimer = null
  }
})
</script>

<style scoped lang="css">
.group-course-management {
  .card-header {
    display: flex;
    justify-content: space-between;
    align-items: center;
    gap: 16px;

    .search-section {
      display: flex;
      gap: 8px;
      align-items: center;
    }
  }
}

.cover-upload :deep(.el-upload) {
  border: 1px dashed #d9d9d9;
  border-radius: 6px;
  cursor: pointer;
  width: 180px;
  height: 120px;
  display: flex;
  align-items: center;
  justify-content: center;
  transition: border-color 0.3s;
}

.cover-upload :deep(.el-upload:hover) {
  border-color: #409eff;
}

.cover-preview {
  width: 180px;
  height: 120px;
  object-fit: cover;
  border-radius: 6px;
}

.cover-upload-icon {
  font-size: 28px;
  color: #8c939d;
}

.cover-cell {
  display: flex;
  align-items: center;
  justify-content: center;
}

.cover-thumb {
  width: 64px;
  height: 48px;
  object-fit: cover;
  border-radius: 4px;
}

.cover-no-data {
  color: #c0c4cc;
  font-size: 12px;
}

/* 二维码弹窗样式 */
.qr-dialog-body {
  display: flex;
  flex-direction: column;
  align-items: center;
  padding: 16px 0;
}

.qr-image-wrapper {
  width: 260px;
  height: 260px;
  display: flex;
  align-items: center;
  justify-content: center;
  border: 1px solid #ebeef5;
  border-radius: 8px;
  background: #fafafa;
  overflow: hidden;
}

.qr-image {
  width: 100%;
  height: 100%;
  object-fit: contain;
}

.qr-tip {
  margin-top: 16px;
  color: #909399;
  font-size: 14px;
}

.detail-cover {
  width: 120px;
  height: 80px;
  object-fit: cover;
  border-radius: 4px;
}
</style>
