<template>
  <v-container fluid class="pt-0">
    <!-- 页面标题 -->
    <v-row class="mb-4">
      <v-col cols="12">
        <h2>{{ $t('task.title') }}</h2>
      </v-col>
    </v-row>

    <!-- 筛选条件和日历的左右布局 -->
    <div class="d-flex task-layout">
      <!-- 左侧：筛选条件面板 -->
      <div
        class="filter-panel-container"
        :class="{ 'filter-panel-collapsed': !isFilterExpanded }"
      >
        <v-card elevation="2" class="h-100 filter-panel-card">
          <v-card-title class="pa-1">
            <div class="d-flex align-center">
              <v-icon class="me-2">mdi-filter</v-icon>
              <span>{{ $t('task.filters.title') }}</span>
            </div>
          </v-card-title>
          <v-card-text class="pa-1 py-2">
            <v-row dense>
              <v-col cols="12">
                <v-select
                  v-model="filters.studentId"
                  :items="studentOptions"
                  :label="$t('task.task.student')"
                  clearable
                  density="compact"
                  variant="outlined"
                  hide-details
                  @update:model-value="onStudentChange"
                />
              </v-col>
              <v-col cols="12">
                <v-select
                  v-model="filters.subjectId"
                  :items="subjectOptions"
                  :label="$t('task.task.subject')"
                  clearable
                  density="compact"
                  variant="outlined"
                  hide-details
                  @update:model-value="onSubjectChange"
                />
              </v-col>
              <v-col cols="12">
                <v-select
                  v-model="filters.termId"
                  :items="termOptions"
                  :label="$t('task.task.term')"
                  :disabled="!filters.studentId"
                  clearable
                  density="compact"
                  variant="outlined"
                  hide-details
                  @update:model-value="onTermChange"
                />
              </v-col>
              <v-col cols="12">
                <v-select
                  v-model="filters.storyId"
                  :items="filteredStoryOptions"
                  :label="$t('task.task.story')"
                  :disabled="!filters.studentId || !filters.termId"
                  clearable
                  density="compact"
                  variant="outlined"
                  hide-details
                  @update:model-value="onStoryChange"
                />
              </v-col>

              <v-col cols="12">
                <v-btn
                  @click="applyFilters"
                  color="primary"
                  variant="flat"
                  :loading="isLoading"
                  block
                  size="small"
                  class="mb-2"
                >
                  {{ $t('task.filters.apply') }}
                </v-btn>
                <v-btn
                  @click="clearFilters"
                  variant="outlined"
                  block
                  size="small"
                >
                  {{ $t('task.filters.clear') }}
                </v-btn>
              </v-col>
            </v-row>
          </v-card-text>
        </v-card>
      </div>

      <!-- 右侧：日历和数据展示 -->
      <div
        class="calendar-container"
        :class="{ 'calendar-full-width': !isFilterExpanded }"
        :style="xs ? { height: '520px', minHeight: '520px' } : { height: 'calc(100dvh - 270px)', minHeight: '480px' }"
      >
        <!-- 浮动折叠按钮：桌面（左右布局）贴筛选面板右边缘中段, 移动端（上下布局）贴日历上方中央 -->
        <v-btn
          class="filter-panel-toggle"
          :class="{ 'filter-panel-toggle-collapsed': !isFilterExpanded }"
          :icon="xs
            ? (isFilterExpanded ? 'mdi-chevron-up' : 'mdi-chevron-down')
            : (isFilterExpanded ? 'mdi-chevron-left' : 'mdi-chevron-right')"
          variant="elevated"
          color="primary"
          size="small"
          @click="toggleFilterPanel"
          :title="isFilterExpanded ? $t('task.filters.collapse') : $t('task.filters.expand')"
        />
        <!-- 错误提示已移至编辑对话框内部 -->

        <!-- 日历组件 -->
        <v-card
          elevation="2"
          :style="{
            height: '100%',
            display: 'flex',
            'flex-direction': 'column',
            position: isFullscreen ? 'fixed' : 'relative',
            top: isFullscreen ? '0' : 'auto',
            left: isFullscreen ? '0' : 'auto',
            width: isFullscreen ? '100vw' : 'auto',
            height: isFullscreen ? '100vh' : (xs ? '520px' : '100%'),
            'z-index': isFullscreen ? '9999' : 'auto',
            overflow: isFullscreen ? 'auto' : 'visible'
          }"
        >
          <v-card-title class="d-flex justify-space-between align-center">
            <span>{{ $t('task.calendar.title') }}</span>
            <div class="d-flex gap-1">
              <!-- 全屏切换按钮 -->
              <v-btn
                :icon="isFullscreen ? 'mdi-fullscreen-exit' : 'mdi-fullscreen'"
                variant="text"
                size="small"
                @click="toggleFullscreen"
                :title="isFullscreen ? $t('task.calendar.exitFullscreen') : $t('task.calendar.enterFullscreen')"
                :disabled="isLoading"
              />
            </div>
          </v-card-title>
          <v-card-text :style="{
            flex: '1',
            display: 'flex',
            'flex-direction': 'column',
            position: 'relative',
            height: isFullscreen ? '0' : '100%',
            'min-height': '0',
            padding: isFullscreen ? '0' : '16px'
          }">
            <!-- Vuetify v-calendar 组件 -->
            <div :style="{ overflow: isFullscreen ? 'auto' : 'visible', flex: '1', 'min-height': '0', height: '100%' }">
              <FullCalendar
                v-if="tasks.length > 0"
                :options="calendarOptions"
                :key="'fc-' + isFullscreen"
                class="task-calendar"
                :class="{ 'is-fullscreen': isFullscreen }"
              />
            </div>

            <!-- 加载遮罩层 -->
            <div v-if="isLoading" class="loading-overlay">
              <v-progress-circular
                indeterminate
                color="primary"
                size="64"
                width="6"
              />
            </div>

            <!-- FullCalendar 自带空状态 -->
          </v-card-text>
        </v-card>
      </div>
    </div>

    <!-- 任务编辑对话框 -->
    <v-dialog v-model="editDialog" max-width="600">
      <v-card class="edit-task-card">
        <v-card-title class="d-flex align-center pe-2 edit-task-header">
          <span>{{ isCreateMode ? $t('task.edit.createTitle') : $t('task.edit.title') }}</span>
          <v-spacer />
          <v-btn
            icon="mdi-close"
            variant="text"
            density="compact"
            @click="closeEditDialog"
          />
        </v-card-title>
        <v-divider />
        <v-card-text class="edit-task-body">
          <!-- 错误消息（sticky 固定在滚动区顶部） -->
          <div class="error-message-container" v-if="isError">
            <v-alert
              type="error"
              variant="tonal"
              closable
              class="mb-2"
              style="width: 100%;"
              @click:close="isError = false"
            >
              {{ errorMessage }}
            </v-alert>
          </div>
          <v-form ref="editFormRef">
            <v-row>
              <v-col cols="12">
                <v-text-field
                  v-model="editForm.task_name"
                  :label="$t('task.task.name') + ' *'"
                  :placeholder="$t('task.task.name')"
                  variant="outlined"
                  density="compact"
                  required
                />
              </v-col>
              <v-col cols="12">
                <v-textarea
                  v-model="editForm.task_description"
                  :label="$t('task.task.description')"
                  :placeholder="$t('task.task.description')"
                  variant="outlined"
                  density="compact"
                  rows="3"
                />
              </v-col>
              <v-col cols="12" md="6">
                <v-select
                  v-model="editForm.student_id"
                  :items="studentOptions"
                  :label="$t('task.task.student') + ' *'"
                  variant="outlined"
                  density="compact"
                  required
                />
              </v-col>
              <v-col cols="12" md="6">
                <v-select
                  v-model="editForm.subject_id"
                  :items="subjectOptions"
                  :label="$t('task.task.subject') + ' *'"
                  variant="outlined"
                  density="compact"
                  required
                />
              </v-col>
              <v-col cols="12" md="6">
                <v-select
                  v-model="editForm.term_id"
                  :items="editTermOptions"
                  :label="$t('task.task.term') + ' *'"
                  :disabled="!editForm.student_id"
                  variant="outlined"
                  density="compact"
                  :hint="!editForm.student_id ? $t('task.filters.selectStudentFirst') : ''"
                  persistent-hint
                  required
                />
              </v-col>
              <v-col cols="12" md="6">
                <v-select
                  v-model="editForm.story_id"
                  :items="editStoryOptions"
                  :label="$t('task.task.story')"
                  :disabled="!editForm.student_id || !editForm.term_id"
                  variant="outlined"
                  density="compact"
                  clearable
                  :hint="!editForm.student_id ? $t('task.filters.selectStudentFirst') : (!editForm.term_id ? $t('task.filters.selectTermFirst') : '')"
                  persistent-hint
                />
              </v-col>
              <v-col cols="12" md="6">
                <v-text-field
                  v-model="editForm.start_date"
                  :label="$t('task.task.startDate') + ' *'"
                  variant="outlined"
                  density="compact"
                  type="date"
                  required
                />
              </v-col>
              <v-col cols="12" md="6">
                <v-text-field
                  v-model="editForm.end_date"
                  :label="$t('task.task.endDate') + ' *'"
                  variant="outlined"
                  density="compact"
                  type="date"
                  required
                />
              </v-col>
              <v-col cols="12">
                <v-slider
                  v-model="editForm.completion_percent"
                  :label="$t('task.task.completionPercent') + ' *'"
                  :min="0"
                  :max="100"
                  :step="1"
                  thumb-label="always"
                  persistent-hint
                  :style="completionSliderStyle"
                  :class="['completion-slider', { 'completion-slider--custom': isCompletionSliderCustom }]"
                  :hint="`${editForm.completion_percent}%`"
                >
                  <template #append>
                    <v-text-field
                      v-model="editForm.completion_percent"
                      type="number"
                      min="0"
                      max="100"
                      hide-details
                      density="compact"
                      style="width: 80px;"
                    />
                  </template>
                </v-slider>
              </v-col>

              <!-- 图像显示区域 -->
              <v-col cols="12">
                <v-divider class="my-4" />
                <div class="text-h6 mb-4">{{ $t('task.task.images') }}</div>
              </v-col>

              <v-col cols="12" sm="6" md="4" v-for="index in 5" :key="index">
                <div class="image-container">
                  <div class="image-label">{{ $t('task.task.image') }} {{ index }}</div>
                  <div
                    v-if="imageCache[index]"
                    class="image-preview clickable"
                    @click="openImageDialog(index)"
                  >
                    <img
                      :src="imageCache[index]"
                      :alt="`${$t('task.task.image')} ${index}`"
                      class="preview-image"
                    />
                  </div>
                  <div v-if="imageCache[index]" class="image-actions-bottom">
                    <v-btn
                      icon="mdi-upload"
                      size="small"
                      color="primary"
                      variant="tonal"
                      @click.stop="uploadImage(index)"
                      :title="$t('task.task.updateImage')"
                    />
                    <v-btn
                      icon="mdi-delete"
                      size="small"
                      color="error"
                      variant="tonal"
                      @click.stop="deleteImage(index)"
                      :title="$t('task.task.deleteImage')"
                    />
                  </div>
                  <div
                    v-else-if="imageLoading[index]"
                    class="image-preview loading"
                  >
                    <v-progress-circular
                      indeterminate
                      color="primary"
                      size="32"
                      width="3"
                    />
                  </div>
                  <div
                    v-else
                    class="image-preview empty"
                  >
                  </div>
                  <div v-if="!imageCache[index] && !imageLoading[index]" class="image-actions-bottom">
                    <v-btn
                      icon="mdi-upload"
                      size="small"
                      color="primary"
                      variant="tonal"
                      @click.stop="uploadImage(index)"
                      :title="$t('task.task.updateImage')"
                    />
                  </div>
                </div>
              </v-col>
            </v-row>
          </v-form>
        </v-card-text>
        <v-divider />
        <v-card-actions class="edit-task-actions">
          <v-btn
            v-if="!isCreateMode"
            color="error"
            variant="text"
            prepend-icon="mdi-delete"
            @click="openDeleteDialog(editingTask)"
          >
            {{ $t('common.buttons.delete') }}
          </v-btn>
          <v-spacer />
          <v-btn
            @click="closeEditDialog"
            variant="outlined"
          >
            {{ $t('common.buttons.cancel') }}
          </v-btn>
          <v-btn
            @click="updateTask"
            color="primary"
            variant="flat"
            :loading="isSaving"
          >
            {{ isCreateMode ? $t('common.buttons.create') : $t('common.buttons.save') }}
          </v-btn>
        </v-card-actions>
      </v-card>
    </v-dialog>

    <!-- 删除确认对话框 -->
    <v-dialog v-model="deleteDialog" max-width="400px">
      <v-card>
        <v-card-title class="text-h5 text-error">
          <v-icon icon="mdi-alert" class="mr-2" />
          {{ $t('task.dialog.delete') }}
        </v-card-title>
        <v-card-text>
          <div class="text-body-1 mb-4">
            {{ $t('task.deleteConfirmation.message') }}
          </div>
          <div v-if="taskToDelete" class="bg-grey-lighten-4 pa-3 rounded">
            <div class="d-flex align-center mb-2">
              <div>
                <div class="font-weight-medium">{{ taskToDelete.task_name }}</div>
                <div class="text-caption text-medium-emphasis">
                  {{ getStudentName(taskToDelete.student_id) }} | {{ formatDate(taskToDelete.start_date) }} - {{ formatDate(taskToDelete.end_date) }}
                </div>
              </div>
            </div>
          </div>
          <div class="text-body-2 text-error mt-4">
            <v-icon icon="mdi-alert-circle" size="small" class="mr-1" />
            {{ $t('task.deleteConfirmation.warning') }}
          </div>
        </v-card-text>
        <v-card-actions>
          <v-spacer />
          <v-btn
            color="grey-darken-1"
            variant="text"
            @click="closeDeleteDialog"
            :disabled="isDeleting"
          >
            {{ $t('common.buttons.cancel') }}
          </v-btn>
          <v-btn
            color="error"
            variant="elevated"
            prepend-icon="mdi-delete"
            :loading="isDeleting"
            :disabled="isDeleting"
            @click="confirmDeleteTask"
          >
            {{ $t('common.buttons.confirm') + ' ' + $t('common.buttons.delete') }}
          </v-btn>
        </v-card-actions>
      </v-card>
    </v-dialog>

    <!-- 图像放大查看对话框 -->
    <v-dialog v-model="imageDialog" :max-width="$vuetify.display.smAndDown ? '100%' : 800">
      <v-card>
        <v-card-title>
          <span>{{ $t('task.task.image') }} {{ currentImageIndex }}</span>
          <v-spacer />
          <v-btn
            icon="mdi-close"
            variant="text"
            @click="closeImageDialog"
          />
        </v-card-title>
        <v-card-text class="d-flex justify-center align-center image-viewer-container" style="min-height: 500px;">
          <div
            v-if="currentImageSrc"
            class="image-zoom-container"
            @wheel.prevent="handleWheel"
            @mousedown="startDrag"
            @mousemove="doDrag"
            @mouseup="stopDrag"
            @mouseleave="stopDrag"
          >
            <img
              :src="currentImageSrc"
              :alt="`${$t('task.task.image')} ${currentImageIndex}`"
              class="enlarged-image"
              :style="{
                transform: `translate(${imagePosition.x}px, ${imagePosition.y}px) scale(${zoomLevel})`,
                cursor: isDragging ? 'grabbing' : 'grab'
              }"
              @dragstart.prevent
            />
          </div>
          <div v-else-if="currentImageIndex && !currentImageSrc" class="d-flex flex-column align-center">
            <v-progress-circular
              indeterminate
              color="primary"
              size="64"
              width="6"
            />
            <div class="mt-4">{{ $t('task.task.loadingImage') }}</div>
          </div>
          <div v-else class="text-center">
            {{ $t('task.task.noImage') }}
          </div>
        </v-card-text>
      </v-card>
    </v-dialog>
  </v-container>
</template>

<script setup>
import { onMounted, ref, computed, watch, onUnmounted, nextTick } from 'vue'
import { useI18n } from 'vue-i18n'
import { useDisplay } from 'vuetify'
import { useAuthStore } from '@/stores/auth'
import { getTasks, getTaskImage, updateTask as updateTaskAPI, deleteTask } from '@/api/taskService.js'
import { getStudents } from '@/api/studentService.js'
import { getSubjects } from '@/api/subjectService.js'
import { getTerms } from '@/api/termService.js'
import { getStories } from '@/api/storyService.js'
import { COLOR_MAPPING } from '@/components/constants/colors'
import FullCalendar from '@fullcalendar/vue3'
import dayGridPlugin from '@fullcalendar/daygrid'
import interactionPlugin from '@fullcalendar/interaction'

// 注册 FullCalendar 组件
defineOptions({
  components: {
    FullCalendar
  }
})

const { t, locale: i18nLocale } = useI18n()
const authStore = useAuthStore()
const { xs } = useDisplay()

// FullCalendar 使用 'en' / 'zh-cn'（小写），与项目内部 'zh-CN' 略有差异
const fcLocale = computed(() => (i18nLocale.value === 'zh-CN' ? 'zh-cn' : 'en'))

// 基础状态
const isLoading = ref(false)
const isError = ref(false)
const errorMessage = ref('')

// 删除功能相关状态
const deleteDialog = ref(false)
const taskToDelete = ref(null)
const isDeleting = ref(false)
const tasks = ref([])

// 日历相关状态
const isFullscreen = ref(false)

// 筛选相关状态
const filters = ref({
  studentId: null,
  subjectId: null,
  termId: null,
  storyId: null
})
const isFilterExpanded = ref(true)

// 关联数据
const students = ref([])
const subjects = ref([])
const terms = ref([])
const stories = ref([])

// 计算属性
// "全部"不再作为选项，clear 按钮本身就是"全部"的意思，避免 value 碰撞
const studentOptions = computed(() =>
  students.value.map(student => ({
    title: student.student_name,
    value: student.student_id
  }))
)

const subjectOptions = computed(() =>
  subjects.value.map(subject => ({
    title: subject.subject_name,
    value: subject.subject_id
  }))
)

// 学期选项根据选择的学生进行筛选，按开始日期倒序
const termOptions = computed(() => {
  if (!filters.value.studentId) {
    return []  // 没选学生时无学期可选
  }
  const filteredTerms = [...terms.value]
    .filter(term => term.student_id === filters.value.studentId)
    .sort((a, b) => new Date(b.term_start_date) - new Date(a.term_start_date))
  return filteredTerms.map(term => ({
    title: term.term_name,
    value: term.term_id
  }))
})

// 筛选面板：系列任务选项（cascade：未选学生或学期时无选项，story 依赖 student + term）
const filteredStoryOptions = computed(() => {
  if (!filters.value.studentId || !filters.value.termId) {
    return []  // 未选学生或学期时无系列任务可选
  }
  let filtered = [...stories.value]
  filtered = filtered.filter(s => s.student_id === filters.value.studentId)
  filtered = filtered.filter(s => s.term_id === filters.value.termId)
  if (filters.value.subjectId) {
    filtered = filtered.filter(s => s.subject_id === filters.value.subjectId)
  }
  return filtered.map(story => ({
    title: story.story_name,
    value: story.story_id
  }))
})



// 监听学生选择变化，重置学期和系列任务选择
watch(() => filters.value.studentId, (newStudentId, oldStudentId) => {
  if (newStudentId !== oldStudentId) {
    filters.value.termId = null
    filters.value.storyId = null
    console.log('Student selection changed, term & story selection reset')
  }
})

// 将任务数据转换为日历事件格式
// FullCalendar 响应式配置
const calendarOptions = computed(() => ({
  plugins: [dayGridPlugin, interactionPlugin],
  initialView: 'dayGridMonth',
  height: 'parent',
  expandRows: true,
  firstDay: 0,
  locale: fcLocale.value,
  headerToolbar: {
    left: xs.value ? 'prev,next' : 'prev,next today',
    center: 'title',
    right: xs.value ? 'dayGridMonth,dayGridWeek' : 'dayGridMonth,dayGridWeek,dayGridDay'
  },
  buttonText: {
    today: t('task.calendar.buttonText.today'),
    month: t('task.calendar.buttonText.month'),
    week: t('task.calendar.buttonText.week'),
    day: t('task.calendar.buttonText.day')
  },
  editable: !xs.value,
  selectable: !xs.value,
  eventDurationEditable: !xs.value,
  displayEventTime: false,
  dayMaxEvents: xs.value ? 3 : 5,
  // 原生跨天事件（FullCalendar 原生支持 allDay 跨天条）
  events: (() => {
    const result = []
    for (const task of (tasks.value || [])) {
      if (!task.start_date) continue
      const start = task.start_date
      const end = task.end_date
      if (!end) continue
      // end 加一天（FullCalendar 的 end 是 exclusive）
      const endDate = new Date(end)
      endDate.setDate(endDate.getDate() + 1)

      result.push({
        id: String(task.task_id),
        title: task.task_name,
        start: start.split('T')[0],
        end: endDate.toISOString().split('T')[0],
        allDay: true,
        color: getSubjectColor(task.subject_id),
        backgroundColor: getSubjectColor(task.subject_id),
        display: 'auto',
        extendedProps: {
          task_id: task.task_id,
          student_id: task.student_id,
          subject_id: task.subject_id,
          completion_percent: task.completion_percent,
          description: task.task_description
        }
      })
    }
    return result
  })(),
  // hover tooltip: 作业名 + 描述 + 进度。原生 title 属性, 浏览器自动渲染气泡。
  eventDidMount: (arg) => {
    const taskData = arg.event.extendedProps
    const lines = [arg.event.title]
    if (taskData.description) lines.push(taskData.description)
    const pct = taskData.completion_percent || 0
    if (pct > 0) lines.push(t('task.calendar.tooltip.progress', { percent: pct }))
    arg.el.title = lines.join('\n')
  },
  // 自定义事件渲染
  eventContent: (arg) => {
    const taskData = arg.event.extendedProps
    const studentName = getStudentName(taskData.student_id) || ''
    const initial = getAvatarFallback(studentName)
    const subjectShort = getSubjectShortName(taskData.subject_id) || ''
    const compPct = taskData.completion_percent || 0

    // 学科背景色由后端给（深浅不定），根据亮度算文字色：暗底配白字、亮底配黑字。
    // 在 .fc-event-custom 上设 color，所有文字子元素继承。
    const subjectBg = getSubjectColor(taskData.subject_id)
    const textColor = getTextColor(subjectBg)

    // 进度环：1-99% 画弧，100% 用对勾。0% 不显示。
    // 弧长 = 2πr，stroke-dashoffset 控制已绘制比例；rotate(-90) 让起点在 12 点钟方向。
    let pctIcon = ''
    if (compPct >= 100) {
      pctIcon = `<svg class="fc-event-ring" width="11" height="11" viewBox="0 0 24 24" fill="currentColor" aria-label="completed"><path d="M9 16.17L4.83 12l-1.42 1.41L9 19 21 7l-1.41-1.41z"/></svg>`
    } else if (compPct > 0) {
      const r = 5
      const c = 2 * Math.PI * r
      const offset = c * (1 - compPct / 100)
      pctIcon = `<svg class="fc-event-ring" width="11" height="11" viewBox="0 0 12 12" aria-label="${compPct}%"><circle cx="6" cy="6" r="${r}" fill="none" stroke="currentColor" stroke-opacity="0.3" stroke-width="1.5"/><circle cx="6" cy="6" r="${r}" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-dasharray="${c.toFixed(2)}" stroke-dashoffset="${offset.toFixed(2)}" transform="rotate(-90 6 6)"/></svg>`
    }

    return {
      html: `
        <div class="fc-event-custom" data-task-id="${arg.event.id}" style="color:${textColor}">
          <div class="fc-event-content">
            <span class="fc-event-avatar" style="background:${getStudentColor(taskData.student_id)};color:${getTextColor(getStudentColor(taskData.student_id))}">${initial}</span>
            <span class="fc-event-subject">[${subjectShort}]</span>
            <span class="fc-event-title">${arg.event.title}</span>
            <span class="fc-event-pct">${pctIcon}</span>
          </div>
        </div>
      `
    }
  },
  // 事件点击
  eventClick: (info) => {
    const taskId = info.event.extendedProps.task_id
    if (taskId) onEventClick({ taskData: { id: taskId } })
  },
  // 日期点击
  dateClick: (info) => {
    onDateClick(new Date(info.dateStr + 'T12:00:00'))
  },
  // 拖拽事件
  eventDrop: async (info) => {
    const taskId = info.event.extendedProps.task_id
    if (!taskId) return
    const oldStart = info.oldEvent.start
    const newStart = info.event.start
    if (!oldStart || !newStart) return
    const diffDays = Math.round((newStart - oldStart) / 86400000)
    if (diffDays === 0) return

    const task = tasks.value.find(t => t.task_id === taskId)
    if (!task) return
    const oldTaskStart = new Date(task.start_date)
    const oldTaskEnd = new Date(task.end_date)
    const newTaskStart = new Date(oldTaskStart.getTime() + diffDays * 86400000)
    const newTaskEnd = new Date(oldTaskEnd.getTime() + diffDays * 86400000)

    isLoading.value = true
    try {
      await updateTaskAPI(taskId, {
        start_date: newTaskStart.toISOString(),
        end_date: newTaskEnd.toISOString()
      })
      await fetchTasks()
    } catch (err) {
      console.error('Failed to move task:', err)
      info.revert()
      errorMessage.value = err?.message || t('task.messages.updateError')
      isError.value = true
      setTimeout(() => { isError.value = false; errorMessage.value = '' }, 3000)
    } finally {
      isLoading.value = false
    }
  }
}))


const getSubjectColor = (subjectId) => {
  const subject = subjects.value.find(s => s.subject_id === subjectId)
  if (subject && subject.calendar_color) {
    // 如果有预定义的颜色映射，使用映射值；否则直接使用颜色键
    return COLOR_MAPPING[subject.calendar_color] || subject.calendar_color
  }
  // 如果没有找到学科或没有设置颜色，返回默认颜色
  return '#9E9E9E' // 默认灰色
}

// 根据背景色计算文字颜色（黑色或白色）
const getTextColor = (backgroundColor) => {
  // 如果背景色是十六进制颜色值
  if (backgroundColor && backgroundColor.startsWith('#')) {
    // 移除 # 符号并解析 RGB 值
    const hex = backgroundColor.replace('#', '')
    const r = parseInt(hex.substring(0, 2), 16)
    const g = parseInt(hex.substring(2, 4), 16)
    const b = parseInt(hex.substring(4, 6), 16)

    // 计算亮度
    const brightness = (r * 299 + g * 587 + b * 114) / 1000

    // 如果亮度大于 128，使用黑色文字，否则使用白色文字
    return brightness > 128 ? '#000000' : '#FFFFFF'
  }

  // 对于其他颜色值，使用白色文字
  return '#FFFFFF'
}

// 格式化日期
const formatDate = (date) => {
  if (!date) return ''

  // 如果是字符串，转换为 Date 对象
  const dateObj = typeof date === 'string' ? new Date(date) : date

  if (isNaN(dateObj.getTime())) return ''

  return dateObj.toLocaleDateString('zh-CN', {
    year: 'numeric',
    month: '2-digit',
    day: '2-digit'
  })
}

// 获取学生名称
const getStudentName = (studentId) => {
  const student = students.value.find(s => s.student_id === studentId)
  return student ? student.student_name : `Student ${studentId}`
}

// 学生头像相关函数已移除，改为使用学生姓名最后一个字符作为头像

// 获取头像占位文本（取名字最后一个字）
const getAvatarFallback = (name) => {
  if (!name) return '?'
  return name.charAt(name.length - 1).toUpperCase()
}

// 为每个学生生成唯一的颜色
const getStudentColor = (studentId) => {
  if (!studentId) return '#9E9E9E' // 默认灰色

  // 使用学生ID作为种子生成一个伪随机颜色
  // 从COLOR_MAPPING中选择一个颜色
  const colorKeys = Object.keys(COLOR_MAPPING)
  // 使用学生ID的数值来确定颜色索引，确保同一学生始终获得相同颜色
  const colorIndex = parseInt(studentId) % colorKeys.length
  return COLOR_MAPPING[colorKeys[colorIndex]] || '#9E9E9E'
}

// 查表：学生主数据里的 slider_icon → data URL。
// 编辑对话框里 v-slider 的 thumb 读取这个值，未设置时调用方不应用图片样式。
const getStudentSliderIcon = (studentId) => {
  if (!studentId) return null
  const student = students.value.find(s => s.student_id === studentId)
  if (!student || !student.slider_icon || !student.slider_icon_mime_type) return null
  return `data:${student.slider_icon_mime_type};base64,${student.slider_icon}`
}

// 获取学科简称（取学科名称的第一个字符）
const getSubjectShortName = (subjectId) => {
  const subject = subjects.value.find(s => s.subject_id === subjectId)
  if (subject && subject.subject_name) {
    // 取学科名称的第一个字符，如果第一个字符是英文字母则大写
    const firstChar = subject.subject_name.charAt(0)
    return /^[\u4e00-\u9fa5]$/.test(firstChar) ? firstChar : firstChar.toUpperCase()
  }
  return '学' // 默认显示"学"
}

// 获取学期名称
// eslint-disable-next-line no-unused-vars
const getTermName = (termId) => {
  const term = terms.value.find(t => t.term_id === termId)
  return term ? term.term_name : `Term ${termId}`
}

// API调用函数
const fetchTasks = async (filterParams = null) => {
  isLoading.value = true
  isError.value = false
  errorMessage.value = ''
  try {
    // 使用传入的筛选参数或当前筛选条件
    const params = filterParams || {
      student_id: filters.value.studentId,
      subject_id: filters.value.subjectId,
      term_id: filters.value.termId,
      story_id: filters.value.storyId
    }

    console.log('Fetching tasks with filters:', params)

    const response = await getTasks(params)

    // 现在API返回的直接是数组（已经处理了所有分页）
    tasks.value = Array.isArray(response) ? response : []

    console.log('Fetched tasks:', tasks.value)
    console.log('Sample task dates:', tasks.value[0] ? {
      start_date: tasks.value[0].start_date,
      end_date: tasks.value[0].end_date,
      start_parsed: new Date(tasks.value[0].start_date),
      end_parsed: new Date(tasks.value[0].end_date)
    } : 'No tasks')
  } catch (e) {
    isError.value = true
    errorMessage.value = e?.response?.data?.detail || e?.message || t('task.messages.loadError')
    setTimeout(() => {
      isError.value = false
      errorMessage.value = ''
    }, 3000)
  } finally {
    isLoading.value = false
  }
}

// 获取单个任务的详细信息 - 此方法已被onEventClick中的直接调用getTaskById替代，可以移除
// const fetchTaskDetail = async (taskId) => {
//   try {
//     const taskDetail = await getTaskById(taskId)
//     console.log('Fetched task detail:', taskDetail)
//     return taskDetail
//   } catch (error) {
//     console.error('Failed to fetch task detail:', error)
//     errorMessage.value = error?.response?.data?.detail || error?.message || t('task.messages.loadError')
//     isError.value = true
//     setTimeout(() => {
//       isError.value = false
//       errorMessage.value = ''
//     }, 3000)
//     throw error
//   }
// }

const fetchStudents = async () => {
  try {
    const students_data = await getStudents()
    console.log('Students data:', students_data)

    // 现在API返回的直接是数组（已经处理了所有分页）
    students.value = Array.isArray(students_data) ? students_data : []
  } catch (error) {
    console.error('Failed to fetch students:', error)
  }
}

const fetchSubjects = async () => {
  try {
    const subjects_data = await getSubjects()
    console.log('Subjects data:', subjects_data)

    // 现在API返回的直接是数组（已经处理了所有分页）
    subjects.value = Array.isArray(subjects_data) ? subjects_data : []
  } catch (error) {
    console.error('Failed to fetch subjects:', error)
  }
}

const fetchTerms = async () => {
  try {
    const terms_data = await getTerms()
    console.log('Terms data:', terms_data)

    // 现在API返回的直接是数组（已经处理了所有分页）
    terms.value = Array.isArray(terms_data) ? terms_data : []
  } catch (error) {
    console.error('Failed to fetch terms:', error)
  }
}

const fetchStories = async () => {
  try {
    const stories_data = await getStories()
    stories.value = Array.isArray(stories_data) ? stories_data : []
  } catch (error) {
    console.error('Failed to fetch stories:', error)
  }
}

// 事件处理函数
const onEventClick = (event) => {
  console.log('Calendar event clicked:', event)
  // 从事件中获取任务ID
  const taskId = event.taskData?.id || event.id

  if (taskId) {
    // 列表接口已经返回编辑对话框所需的全量字段，直接复用缓存避免一次额外的请求。
    // 之前这里会 await getTaskById 并 toggle isLoading，导致日历上的 loading-overlay 闪一下。
    const cachedTask = tasks.value.find((t) => t.task_id === taskId)
    if (!cachedTask) {
      console.error('Task not found in local cache:', taskId)
      errorMessage.value = t('task.messages.loadError')
      isError.value = true
      setTimeout(() => {
        isError.value = false
        errorMessage.value = ''
      }, 3000)
      return
    }
    openEditDialog(cachedTask)
  } else {
    console.error('Task ID not found for event:', event)
  }
}

const onDateClick = (date) => {
  console.log('onDateClick called with date:', date);

  // FullCalendar 的 dateClick 始终传入日期字符串
  // 若收到 DOM 事件或无效参数，使用当前日期
  if (date && !date.target) {
    console.log('Received valid date object');
    openCreateDialog(date);
  } else {
    console.log('No/invalid date received, using current date');
    openCreateDialog(null);
  }
}

// ── 拖拽事件处理 ──
const toggleFullscreen = () => {
  isFullscreen.value = !isFullscreen.value
}

const toggleFilterPanel = () => {
  isFilterExpanded.value = !isFilterExpanded.value
}

// 监听键盘事件：有对话框打开时 Esc 只关对话框，不退出全屏；
// 无对话框时 Esc 才退出全屏
const handleKeydown = (event) => {
  if (event.key !== 'Escape') return
  if (editDialog.value || deleteDialog.value || imageDialog.value) return
  if (isFullscreen.value) {
    isFullscreen.value = false
  }
}

const applyFilters = async () => {
  console.log('Applying filters:', filters.value)
  // 直接使用当前筛选条件调用 fetchTasks
  await fetchTasks()
}

const clearFilters = async () => {
  filters.value = {
    studentId: null,
    subjectId: null,
    termId: null,
    storyId: null
  }
  console.log('All filters cleared')
  // 清除筛选后重新获取所有数据
  await fetchTasks({})
}

// 筛选面板处理器：只负责自动应用筛选；
// 子项重置由 watcher（line 634+）统一处理，避免重复
const onStudentChange = () => fetchTasks()
const onSubjectChange = () => fetchTasks()
const onTermChange = () => fetchTasks()
const onStoryChange = () => fetchTasks()



// 编辑功能相关状态
const editDialog = ref(false)
const isCreateMode = ref(false) // 标记是否为创建模式
const editingTask = ref(null)
const editForm = ref({
  task_name: '',
  task_description: '',
  student_id: null,
  subject_id: null,
  term_id: null,
  start_date: '',
  end_date: '',
  completion_percent: 0,
  // 图像数据
  image_01: null,
  image_01_mime_type: null,
  image_02: null,
  image_02_mime_type: null,
  image_03: null,
  image_03_mime_type: null,
  image_04: null,
  image_04_mime_type: null,
  image_05: null,
  image_05_mime_type: null,
  delete_image_01: false,
  delete_image_02: false,
  delete_image_03: false,
  delete_image_04: false,
  delete_image_05: false
})
const isSaving = ref(false)
const editFormRef = ref(null)
// 加载任务到表单时抑制 cascade 重置，避免覆盖已加载的 term/story
const suppressCascade = ref(false)

// 为编辑对话框创建独立的学期选项计算属性
const editTermOptions = computed(() => {
  // 如果没有选择学生，显示所有学期
  if (!editForm.value.student_id) {
    const sortedTerms = [...terms.value].sort((a, b) => new Date(b.term_start_date) - new Date(a.term_start_date))
    return sortedTerms.map(term => ({
      title: term.term_name,
      value: term.term_id
    }))
  }

  // 筛选属于选定学生的学期，按开始日期倒序排列
  const filteredTerms = [...terms.value]
    .filter(term => term.student_id === editForm.value.student_id)
    .sort((a, b) => new Date(b.term_start_date) - new Date(a.term_start_date))

  return filteredTerms.map(term => ({
    title: term.term_name,
    value: term.term_id
  }))
})

// 系列任务选项（Story）- 编辑对话框用，带 cascade 过滤（依赖 student + term）
const editStoryOptions = computed(() => {
  if (!editForm.value.student_id || !editForm.value.term_id) {
    return []
  }
  let filtered = [...stories.value]
  filtered = filtered.filter(s => s.student_id === editForm.value.student_id)
  filtered = filtered.filter(s => s.term_id === editForm.value.term_id)
  if (editForm.value.subject_id) {
    filtered = filtered.filter(s => s.subject_id === editForm.value.subject_id)
  }
  return filtered.map(story => ({
    title: story.story_name,
    value: story.story_id
  }))
})

// 编辑对话框：completion 滑块的 thumb 自定义样式。
// 学生没选 / 没设 slider_icon 时返回空对象 → 不加 --custom class → 默认 Vuetify thumb。
// 学生设了 slider_icon 时返回 CSS 变量 + 加 class → 深选择器改写 .v-slider-thumb__surface。
const completionSliderStyle = computed(() => {
  const url = getStudentSliderIcon(editForm.value.student_id)
  if (!url) return {}
  return { '--v-slider-thumb-image': `url("${url}")` }
})

const isCompletionSliderCustom = computed(() => Boolean(completionSliderStyle.value['--v-slider-thumb-image']))

// 图像放大查看相关状态
const imageDialog = ref(false)
const currentImageIndex = ref(null)
const currentImageSrc = ref(null)
const zoomLevel = ref(1) // 缩放级别，1表示原始大小
const imagePosition = ref({ x: 0, y: 0 }) // 图像位置，用于拖拽平移
const isDragging = ref(false) // 是否正在拖拽
const dragStart = ref({ x: 0, y: 0 }) // 拖拽起始位置

// 图像加载状态管理
const imageLoading = ref({
  1: false,
  2: false,
  3: false,
  4: false,
  5: false
})

const imageCache = ref({
  1: null,
  2: null,
  3: null,
  4: null,
  5: null
})

// 编辑功能方法
const openEditDialog = async (task) => {
  // 设置为编辑模式
  isCreateMode.value = false;
  console.log('Setting isCreateMode to false for editing existing task');

  // 清理图像缓存
  Object.keys(imageCache.value).forEach(key => {
    if (imageCache.value[key]) {
      URL.revokeObjectURL(imageCache.value[key]);
      imageCache.value[key] = null;
    }
  });

  // 清理图片删除标记
  for (let i = 1; i <= 5; i++) {
    const imageKey = `image_0${i}`;
    editForm.value[`delete_${imageKey}`] = false;
  }

  // 重置加载状态
  Object.keys(imageLoading.value).forEach(key => {
    imageLoading.value[key] = false;
  });

  editingTask.value = task
  // 抑制 cascade watcher，避免加载时把 term/story 重置为 null
  suppressCascade.value = true
  editForm.value = {
    task_name: task.task_name || '',
    task_description: task.task_description || '',
    student_id: task.student_id || null,
    subject_id: task.subject_id || null,
    term_id: task.term_id || null,
    story_id: task.story_id || null,
    start_date: task.start_date ? task.start_date.split('T')[0] : '',
    end_date: task.end_date ? task.end_date.split('T')[0] : '',
    completion_percent: task.completion_percent || 0,
    // 图像数据
    image_01: task.image_01 || null,
    image_01_mime_type: task.image_01_mime_type || null,
    image_02: task.image_02 || null,
    image_02_mime_type: task.image_02_mime_type || null,
    image_03: task.image_03 || null,
    image_03_mime_type: task.image_03_mime_type || null,
    image_04: task.image_04 || null,
    image_04_mime_type: task.image_04_mime_type || null,
    image_05: task.image_05 || null,
    image_05_mime_type: task.image_05_mime_type || null
  }
  console.log('Editing task data:', task)
  console.log('Converted form data:', editForm.value)
  await nextTick()
  suppressCascade.value = false
  editDialog.value = true

  // 对话框打开后，预加载所有图片
  if (task && task.task_id) {
    // 使用setTimeout让对话框先显示出来，然后再加载图片
    setTimeout(() => {
      // 仅当该槽位确实有图时才去拉，否则会触发一堆 404 + 5 个 spinner 一起转的视觉噪音。
      for (let i = 1; i <= 5; i++) {
        const slotKey = `image_0${i}`
        if (!editForm.value[slotKey]) continue

        loadTaskImage(task.task_id, i).catch((error) => {
          // 404 表示该槽位没有图，正常情况
          if (error?.response?.status === 404) return
          console.error(`Failed to preload image ${i}:`, error)
        })
      }
    }, 100);
  }
}

// 打开创建对话框
const openCreateDialog = async (date) => {
  // 添加调试信息，记录传入的date参数
  console.log('openCreateDialog called with date:', date);
  console.log('date type:', typeof date);
  if (date) {
    console.log('date properties:', Object.keys(date));
  }

  // 清理图像缓存
  Object.keys(imageCache.value).forEach(key => {
    if (imageCache.value[key]) {
      URL.revokeObjectURL(imageCache.value[key]);
      imageCache.value[key] = null;
    }
  });

  // 清理图片删除标记
  for (let i = 1; i <= 5; i++) {
    const imageKey = `image_0${i}`;
    editForm.value[`delete_${imageKey}`] = false;
  }

  // 重置加载状态
  Object.keys(imageLoading.value).forEach(key => {
    imageLoading.value[key] = false;
  });

  // 设置为创建模式
  isCreateMode.value = true;
  editingTask.value = null;

  // 初始化表单数据 - 增强对各种date参数格式的处理
  let formattedDate;
  try {
    // 处理不同格式的日期参数
    if (date) {
      if (date.date) {
        // v-calendar可能传递{date: '2023-01-01'}格式
        console.log('Using date.date property:', date.date);
        formattedDate = new Date(date.date).toISOString().split('T')[0];
      } else if (date instanceof Date) {
        // 直接传递Date对象
        console.log('Using Date object');
        formattedDate = date.toISOString().split('T')[0];
      } else if (typeof date === 'string') {
        // 直接传递日期字符串
        console.log('Using date string');
        formattedDate = new Date(date).toISOString().split('T')[0];
      } else if (date.year && date.month && date.day) {
        // v-calendar可能传递{year: 2023, month: 1, day: 1}格式
        console.log('Using year/month/day properties');
        formattedDate = new Date(date.year, date.month - 1, date.day).toISOString().split('T')[0];
      } else {
        // 其他情况，尝试转换为日期
        console.log('Attempting to convert unknown date format');
        const dateObj = new Date(date);
        if (!isNaN(dateObj.getTime())) {
          formattedDate = dateObj.toISOString().split('T')[0];
        } else {
          throw new Error('Invalid date format');
        }
      }
    } else {
      throw new Error('No date provided');
    }
  } catch (error) {
    // 如果日期处理出错，使用当前日期
    console.warn('Error processing date, using current date:', error);
    formattedDate = new Date().toISOString().split('T')[0];
  }

  // 抑制 cascade watcher，避免加载时把预填的 term 重置为 null
  suppressCascade.value = true;
  editForm.value = {
    task_name: '',
    task_description: '',
    student_id: filters.value.studentId || null,
    subject_id: filters.value.subjectId || null,
    term_id: filters.value.termId || null,
    story_id: null,
    start_date: formattedDate,
    end_date: formattedDate,
    completion_percent: 0,
    // 图像数据初始化为空
    image_01: null,
    image_01_mime_type: null,
    image_02: null,
    image_02_mime_type: null,
    image_03: null,
    image_03_mime_type: null,
    image_04: null,
    image_04_mime_type: null,
    image_05: null,
    image_05_mime_type: null,
    delete_image_01: false,
    delete_image_02: false,
    delete_image_03: false,
    delete_image_04: false,
    delete_image_05: false
  };

  console.log('Creating new task with date:', formattedDate);
  await nextTick();
  suppressCascade.value = false;
  editDialog.value = true;
}

const closeEditDialog = () => {
  editDialog.value = false;
  editingTask.value = null;
  isCreateMode.value = false;

  // 清除错误消息状态
  isError.value = false;
  errorMessage.value = '';

  editForm.value = {
    task_name: '',
    task_description: '',
    student_id: null,
    subject_id: null,
    term_id: null,
    story_id: null,
    start_date: '',
    end_date: '',
    completion_percent: 0,
    // 图像数据
    image_01: null,
    image_01_mime_type: null,
    image_02: null,
    image_02_mime_type: null,
    image_03: null,
    image_03_mime_type: null,
    image_04: null,
    image_04_mime_type: null,
    image_05: null,
    image_05_mime_type: null,
    delete_image_01: false,
    delete_image_02: false,
    delete_image_03: false,
    delete_image_04: false,
    delete_image_05: false
  }

  // 清理图像缓存
  Object.keys(imageCache.value).forEach(key => {
    if (imageCache.value[key]) {
      URL.revokeObjectURL(imageCache.value[key]);
      imageCache.value[key] = null;
    }
  });

  // 重置加载状态
  Object.keys(imageLoading.value).forEach(key => {
    imageLoading.value[key] = false;
  });
}

const closeImageDialog = () => {
  imageDialog.value = false
  currentImageIndex.value = null
  currentImageSrc.value = null
  resetZoom() // 关闭对话框时重置缩放
  resetPosition() // 关闭对话框时重置位置
}

// 删除功能方法
const openDeleteDialog = (task) => {
  taskToDelete.value = task
  deleteDialog.value = true
}

const closeDeleteDialog = () => {
  deleteDialog.value = false
  taskToDelete.value = null
}

const confirmDeleteTask = async () => {
  if (!taskToDelete.value?.task_id) {
    console.error('Unable to get task ID')
    return
  }

  isDeleting.value = true

  try {
    console.log('Deleting task:', taskToDelete.value)

    // 1. 调用API删除任务
    await deleteTask(taskToDelete.value.task_id)

    // 2. 从本地列表中移除任务
    const index = tasks.value.findIndex(t => t.task_id === taskToDelete.value.task_id)
    if (index !== -1) {
      tasks.value.splice(index, 1)
    }

    // 3. 关闭对话框
    closeDeleteDialog()
    closeEditDialog() // 如果正在编辑，也关闭编辑对话框

  } catch (error) {
    console.error('Deletion failed:', error)
    // 在删除对话框中显示错误，但不关闭对话框
    errorMessage.value = error.message || t('task.messages.deleteError')
    isError.value = true
    setTimeout(() => {
      isError.value = false
      errorMessage.value = ''
    }, 3000)
  } finally {
    isDeleting.value = false
  }
}

// 图像缩放功能 - 这些方法已被鼠标滚轮缩放功能替代，可以移除
// const zoomIn = () => {
//   if (zoomLevel.value < 3) { // 最大放大3倍
//     zoomLevel.value += 0.25
//   }
// }

// const zoomOut = () => {
//   if (zoomLevel.value > 0.5) { // 最小缩小到0.5倍
//     zoomLevel.value -= 0.25
//   }
// }

const resetZoom = () => {
  zoomLevel.value = 1 // 重置为原始大小
}

// 图像拖拽平移功能
const startDrag = (event) => {
  if (event.button === 0) { // 只响应鼠标左键
    isDragging.value = true
    dragStart.value = {
      x: event.clientX - imagePosition.value.x,
      y: event.clientY - imagePosition.value.y
    }
  }
}

const doDrag = (event) => {
  if (isDragging.value) {
    imagePosition.value = {
      x: event.clientX - dragStart.value.x,
      y: event.clientY - dragStart.value.y
    }
  }
}

const stopDrag = () => {
  isDragging.value = false
}

const resetPosition = () => {
  imagePosition.value = { x: 0, y: 0 }
}

// 鼠标滚轮缩放功能
const handleWheel = (event) => {
  event.preventDefault()

  // 获取鼠标在图像上的相对位置
  //const container = event.currentTarget
  // const rect = container.getBoundingClientRect()

  // 计算缩放前的缩放级别
  // const oldZoom = zoomLevel.value

  // 根据滚轮方向调整缩放级别
  if (event.deltaY < 0) { // 向上滚动，放大
    if (zoomLevel.value < 3) {
      zoomLevel.value += 0.1
    }
  } else { // 向下滚动，缩小
    if (zoomLevel.value > 0.5) {
      zoomLevel.value -= 0.1
    }
  }

  // 限制缩放范围
  zoomLevel.value = Math.max(0.5, Math.min(3, zoomLevel.value))
}

// 上传图片方法
const uploadImage = async (index) => {
  // 创建文件选择器
  const input = document.createElement('input');
  input.type = 'file';
  input.accept = 'image/*'; // 只接受图片文件

  // 监听文件选择事件
  input.onchange = async (e) => {
    const file = e.target.files[0];
    if (!file) return;

    try {
      // 设置加载状态
      imageLoading.value[index] = true;

      // 读取文件为base64
      const reader = new FileReader();
      reader.onload = (event) => {
        const base64String = event.target.result;

        // 更新表单数据
        const imageKey = `image_0${index}`;
        // 重要：如果之前标记了删除，现在上传新图片，需要取消删除标记
        editForm.value[`delete_${imageKey}`] = false;

        // 清除旧的图片数据，确保不会有冲突
        editForm.value[imageKey] = null;

        // 设置新的图片数据
        editForm.value[`${imageKey}_mime_type`] = file.type;
        editForm.value[`${imageKey}_file_name`] = file.name;
        editForm.value[`${imageKey}_base64`] = base64String;

        // 更新图片缓存，立即显示上传的图片
        if (imageCache.value[index]) {
          URL.revokeObjectURL(imageCache.value[index]);
        }
        imageCache.value[index] = URL.createObjectURL(file);

        console.log(`Image ${index} uploaded successfully:`, {
          fileName: file.name,
          mimeType: file.type,
          hasBase64: !!base64String,
          deleteFlag: editForm.value[`delete_${imageKey}`]
        });

        // 重置加载状态
        imageLoading.value[index] = false;
      };

      reader.readAsDataURL(file);
    } catch (error) {
      console.error(`Failed to upload image ${index}:`, error);
      errorMessage.value = t('task.messages.uploadImageError');
      isError.value = true;
      setTimeout(() => {
        isError.value = false;
        errorMessage.value = '';
      }, 3000);

      // 重置加载状态
      imageLoading.value[index] = false;
    }
  };

  // 触发文件选择器
  input.click();
};

// 删除图片方法
const deleteImage = (index) => {
  // 确认删除
  if (confirm(t('task.messages.confirmDeleteImage'))) {
    // 更新表单数据，标记为删除
    const imageKey = `image_0${index}`;
    editForm.value[`delete_${imageKey}`] = true;

    // 清除图片缓存
    if (imageCache.value[index]) {
      URL.revokeObjectURL(imageCache.value[index]);
      imageCache.value[index] = null;
    }

    // 清除其他相关字段
    editForm.value[`${imageKey}_mime_type`] = null;
    editForm.value[`${imageKey}_file_name`] = null;
    editForm.value[`${imageKey}_base64`] = null;
  }
};

// 图像加载方法
const loadTaskImage = async (taskId, imageIndex) => {
  // 检查缓存
  if (imageCache.value[imageIndex]) {
    return imageCache.value[imageIndex];
  }

  // 设置加载状态
  imageLoading.value[imageIndex] = true;

  try {
    const imageBlob = await getTaskImage(taskId, imageIndex);
    const imageUrl = URL.createObjectURL(imageBlob);

    // 缓存图像URL
    imageCache.value[imageIndex] = imageUrl;

    return imageUrl;
  } catch (error) {
    // 如果是404错误，说明该索引位置没有图片，这是正常的
    if (error.response && error.response.status === 404) {
      console.log(`No image found at index ${imageIndex} - this is normal`);
      return null;
    }
    console.error(`Failed to load task image ${imageIndex}:`, error);
    throw error;
  } finally {
    imageLoading.value[imageIndex] = false;
  }
}

// 图像放大查看方法（更新版）
const openImageDialog = async (index) => {
  // 首先检查是否有缓存的图像
  if (imageCache.value[index]) {
    // 使用已缓存的图像
    currentImageIndex.value = index;
    currentImageSrc.value = imageCache.value[index];
    imageDialog.value = true;
  } else if (editingTask.value && editingTask.value.task_id) {
    // 从API加载图像
    try {
      // 显示加载对话框
      currentImageIndex.value = index;
      currentImageSrc.value = null; // 初始化为null，显示加载状态
      imageDialog.value = true;

      // 加载图像
      const imageUrl = await loadTaskImage(editingTask.value.task_id, index);

      // 更新图像源
      if (imageDialog.value && currentImageIndex.value === index) {
        currentImageSrc.value = imageUrl;
      }
    } catch (error) {
      // 如果是404错误，说明该索引位置没有图片，这是正常的
      if (error.response && error.response.status === 404) {
        console.log(`No image found at index ${index} - this is normal`);
        // 关闭对话框，但不显示错误消息
        imageDialog.value = false;
      } else {
        console.error(`Failed to open image dialog for image ${index}:`, error);
        // 显示错误消息
        errorMessage.value = t('task.messages.loadImageError');
        isError.value = true;
        setTimeout(() => {
          isError.value = false;
          errorMessage.value = '';
        }, 3000);

        // 关闭对话框
        imageDialog.value = false;
      }
    }
  } else {
    // 没有图像可显示
    console.log(`No image data available for index ${index}`);
  }
}

// 导入创建任务API
import { createTask as createTaskAPI } from '@/api/taskService'

const updateTask = async () => {
  isSaving.value = true
  try {
    console.log('Form data before update/create:', editForm.value)

    // 验证必填字段
    if (!editForm.value.task_name) {
      throw new Error(t('task.validation.nameRequired') || '作业名称不能为空')
    }
    // 作业描述可以为空，不需要验证
    if (!editForm.value.student_id) {
      throw new Error(t('task.validation.studentRequired') || '请选择学生')
    }
    if (!editForm.value.subject_id) {
      throw new Error(t('task.validation.subjectRequired') || '请选择学科')
    }
    if (!editForm.value.term_id) {
      throw new Error(t('task.validation.termRequired') || '请选择学期')
    }
    if (editForm.value.completion_percent === undefined || editForm.value.completion_percent === null) {
      throw new Error(t('task.validation.completionRequired') || '请输入完成度')
    }
    if (!editForm.value.start_date) {
      throw new Error(t('task.validation.startDateRequired') || '请选择开始日期')
    }
    if (!editForm.value.end_date) {
      throw new Error(t('task.validation.endDateRequired') || '请选择截止日期')
    }

    // 构建要发送的数据
    const taskData = {
      task_name: editForm.value.task_name,
      task_description: editForm.value.task_description,
      student_id: parseInt(editForm.value.student_id),
      subject_id: parseInt(editForm.value.subject_id),
      term_id: editForm.value.term_id ? parseInt(editForm.value.term_id) : null,
      story_id: editForm.value.story_id || null,
      start_date: new Date(editForm.value.start_date).toISOString(),
      end_date: new Date(editForm.value.end_date).toISOString(),
      completion_percent: parseInt(editForm.value.completion_percent)
    }

    // 添加图片上传和删除相关数据
    for (let i = 1; i <= 5; i++) {
      const imageKey = `image_0${i}`;

      // 处理图片上传 - 优先处理上传，因为上传会覆盖删除标记
      if (editForm.value[`${imageKey}_base64`]) {
        // 如果有新上传的图片，确保删除标记为false
        taskData[`delete_${imageKey}`] = false;

        // 添加图片数据
        taskData[`${imageKey}_mime_type`] = editForm.value[`${imageKey}_mime_type`];
        taskData[`${imageKey}_file_name`] = editForm.value[`${imageKey}_file_name`];
        taskData[`${imageKey}_base64`] = editForm.value[`${imageKey}_base64`];

        console.log(`Sending new image data for ${imageKey}`);
      }
      // 处理图片删除 - 只有在没有新上传图片的情况下才处理删除
      else if (editForm.value[`delete_${imageKey}`]) {
        taskData[`delete_${imageKey}`] = true;
        console.log(`Marking ${imageKey} for deletion`);
      }
    }

    console.log('Sending data:', taskData)

    let result;

    // 根据模式决定是创建还是更新任务
    if (isCreateMode.value) {
      // 创建新任务
      console.log('Creating new task...');
      result = await createTaskAPI(taskData);

      // 将新任务添加到任务列表
      tasks.value.push(result);
      console.log('Task created successfully:', result);
    } else {
      // 更新现有任务
      console.log('Updating existing task...');
      result = await updateTaskAPI(editingTask.value.task_id, taskData);

      // 更新本地列表中的任务
      const index = tasks.value.findIndex(t => t.task_id === editingTask.value.task_id);
      if (index !== -1) {
        tasks.value[index] = { ...tasks.value[index], ...result };
      }
      console.log('Task updated successfully:', result);
    }

    // 关闭对话框
    closeEditDialog();

  } catch (error) {
    console.error('Operation failed:', error);
    errorMessage.value = error.message || (isCreateMode.value ? '创建作业失败' : t('task.messages.updateError'));
    isError.value = true;
    // 不再使用setTimeout自动清除错误消息，而是让用户手动关闭或在对话框关闭时清除

    // 滚动到对话框顶部，确保错误消息可见
    setTimeout(() => {
      const dialogElement = document.querySelector('.v-dialog--active');
      if (dialogElement) {
        dialogElement.scrollTop = 0;
      }
    }, 100);
  } finally {
    isSaving.value = false;
  }
}

// 组件挂载
onMounted(() => {
  if (authStore && authStore.initializeAuth) {
    authStore.initializeAuth()
  }

  // 并行获取所有数据，初始加载时不使用筛选
  Promise.all([
    fetchTasks({}), // 传入空对象获取所有任务
    fetchStudents(),
    fetchSubjects(),
    fetchTerms(),
    fetchStories()
  ])

  // 添加键盘事件监听
  document.addEventListener('keydown', handleKeydown)
})

// 组件卸载时移除监听器
onUnmounted(() => {
  document.removeEventListener('keydown', handleKeydown)
})

// 监听编辑表单中学生选择的变化，重置学期和系列任务选择
watch(() => editForm.value.student_id, (newStudentId, oldStudentId) => {
  if (suppressCascade.value) return
  if (newStudentId !== oldStudentId) {
    // cascade: 学生变了 → 重置学期和系列任务
    editForm.value.term_id = null
    editForm.value.story_id = null
    console.log('Edit: student changed, term & story reset')
  }
})

// 学期变了 → 重置系列任务
watch(() => editForm.value.term_id, (newTermId, oldTermId) => {
  if (suppressCascade.value) return
  if (newTermId !== oldTermId) {
    editForm.value.story_id = null
    console.log('Edit: term changed, story reset')
  }
})

// 学科变了 → 重置系列任务
watch(() => editForm.value.subject_id, (newSubjectId, oldSubjectId) => {
  if (suppressCascade.value) return
  if (newSubjectId !== oldSubjectId) {
    editForm.value.story_id = null
    console.log('Edit: subject changed, story reset')
  }
})
</script>

<style scoped>
.task-calendar {
  height: 100%;
}

/* 错误消息容器样式 */
.error-message-container {
  position: sticky;
  top: 0;
  z-index: 100;
  width: 100%;
}

/* 编辑对话框：头/尾固定，中间内容滚动 */
.edit-task-card {
  display: flex;
  flex-direction: column;
  max-height: 90vh;
  overflow: hidden;
}

.edit-task-header {
  flex: 0 0 auto;
}

.edit-task-body {
  flex: 1 1 auto;
  min-height: 0;
  overflow-y: auto;
}

.edit-task-actions {
  flex: 0 0 auto;
}

/* 完成度滑块：当学生在 Student 主数据里设置了 slider_icon 时，
   用 CSS 变量把 .v-slider-thumb__surface 的默认纯色背景替换为该图。
   --v-slider-thumb-image 由 :style 上的 completionSliderStyle 计算属性在 v-slider 根上注入。
   学生未设置 → 没有 --custom class → 深选择器不命中 → 默认 Vuetify thumb 视觉不动。 */
.completion-slider--custom :deep(.v-slider-thumb__surface) {
  background-color: rgb(var(--v-theme-primary));
  background-image: var(--v-slider-thumb-image);
  background-repeat: no-repeat;
  background-position: center;
  background-size: contain;
  box-shadow: 0 1px 4px rgba(0, 0, 0, 0.25);
}

/* 图像容器样式 */
.image-container {
  display: flex;
  flex-direction: column;
  align-items: center;
  padding: 8px;
  border: 1px solid #e0e0e0;
  border-radius: 4px;
  background-color: #fafafa;
}

.image-label {
  font-weight: 500;
  margin-bottom: 8px;
  color: #616161;
}

.image-preview {
  width: 100%;
  height: 120px;
  display: flex;
  justify-content: center;
  align-items: center;
  overflow: hidden;
  border-radius: 4px;
  background-color: #eeeeee;
}

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

.no-image {
  width: 100%;
  height: 120px;
  display: flex;
  justify-content: center;
  align-items: center;
  color: #9e9e9e;
  font-style: italic;
  flex-direction: column;
}

/* 图像加载提示 */
.image-hint {
  font-size: 12px;
  margin-top: 8px;
  color: #1976d2;
  text-align: center;
}

/* 图像加载状态 */
.loading {
  display: flex;
  justify-content: center;
  align-items: center;
  background-color: #f5f5f5;
}

/* 可点击的图像预览 */
.clickable {
  cursor: pointer;
  transition: transform 0.2s;
}

.clickable:hover {
  transform: scale(1.05);
}

/* 放大图像样式 */
.image-viewer-container {
  overflow: hidden;
  position: relative;
}

.image-zoom-container {
  display: flex;
  justify-content: center;
  align-items: center;
  width: 100%;
  height: 100%;
  overflow: auto;
}

.enlarged-image {
  max-width: 100%;
  max-height: 70vh;
  object-fit: contain;
  transition: transform 0.2s ease;
  transform-origin: center center;
  user-select: none;
}

/* 悬浮提示 */
.event-hover-tip {
  display: none;
  position: absolute;
  bottom: calc(100% + 8px);
  left: 50%;
  transform: translateX(-50%);
  background: rgba(30, 30, 30, 0.95);
  color: #fff;
  padding: 8px 12px;
  border-radius: 6px;
  font-size: 12px;
  line-height: 1.5;
  white-space: normal;
  min-width: 200px;
  max-width: 320px;
  z-index: 9999;
  pointer-events: none;
  box-shadow: 0 4px 12px rgba(0,0,0,0.3);
}

.event-hover-tip .tip-title {
  font-weight: 600;
  font-size: 13px;
  margin-bottom: 2px;
}

.event-hover-tip .tip-desc {
  color: #ccc;
  font-size: 11px;
  margin-top: 4px;
  border-top: 1px solid rgba(255,255,255,0.15);
  padding-top: 4px;
  word-break: break-word;
}

.event-hover-tip .tip-meta {
  display: flex;
  gap: 10px;
  margin-top: 6px;
  font-size: 11px;
  opacity: 0.8;
}

.event-hover-tip::after {
  content: '';
  position: absolute;
  top: 100%;
  left: 50%;
  transform: translateX(-50%);
  border: 6px solid transparent;
  border-top-color: rgba(30, 30, 30, 0.95);
}

.custom-event.tip-active .event-hover-tip {
  display: block;
}

/* 拖拽放置区域 */
.calendar-drop-zone {
  position: relative;
}

/* 日格拖拽悬停高亮 */
.v-calendar-month__day.drag-over {
  background-color: rgba(33, 150, 243, 0.12) !important;
  outline: 2px dashed #1976D2;
  outline-offset: -2px;
  border-radius: 4px;
}

/* 拖拽中的卡片 */
.custom-event[draggable="true"] {
  cursor: grab;
}

.custom-event[draggable="true"]:active {
  cursor: grabbing;
}

/* 拖拽时的样式 */
.custom-event.dragging {
  opacity: 0.4;
}

/* 任务布局容器 */
.task-layout {
  position: relative;
  height: calc(100vh - 270px);
  overflow: visible;
}

/* 筛选面板容器 */
.filter-panel-container {
  position: relative;
  width: 240px;
  min-width: 240px;
  max-width: 240px;
  transition: all 0.4s cubic-bezier(0.4, 0.0, 0.2, 1);
  overflow: hidden;
  margin-right: 16px;
}

.filter-panel-container.filter-panel-collapsed {
  width: 0;
  min-width: 0;
  max-width: 0;
  margin-right: 0;
  transform: translateX(-100%);
}

/* 浮动折叠按钮：展开时贴在 panel 右侧边缘, 折叠时移至最左侧 */
.filter-panel-toggle {
  position: absolute;
  top: 50%;
  left: -16px;            /* panel 宽 240 + margin-right 16, 按钮在 calendar 左边缘外 16px */
  transform: translate(-50%, -50%);
  z-index: 10;
  transition: left 0.4s cubic-bezier(0.4, 0.0, 0.2, 1);
}

.filter-panel-toggle.filter-panel-toggle-collapsed {
  left: 16px;
}

.filter-panel-card {
  width: 240px;
  min-width: 240px;
}

/* 筛选面板标题与控件统一缩小 */
.filter-panel-card .v-card-title {
  font-size: 0.9375rem;
  padding: 6px 8px;
}

.filter-panel-card .v-card-title .v-icon {
  font-size: 18px;
}

/* 筛选面板内的标签和输入框缩小 */
.filter-panel-card :deep(.v-label),
.filter-panel-card :deep(.v-field__input) {
  font-size: 0.8125rem;
}

/* 日历容器 */
.calendar-container {
  flex: 1;
  transition: all 0.4s cubic-bezier(0.4, 0.0, 0.2, 1);
  min-width: 0;
  position: relative;
}

.calendar-container.calendar-full-width {
  width: 100%;
}

/* 筛选器面板样式 */
.filter-panel {
  position: sticky;
  top: 20px;
}

/* 筛选器卡片高度自适应 */
.filter-panel .v-card {
  min-height: 100%;
}

/* 过渡动画 */
.transition-all {
  transition: all 0.3s ease-in-out;
}

/* 加载遮罩层样式 */
.loading-overlay {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  background-color: rgba(255, 255, 255, 0.8);
  display: flex;
  justify-content: center;
  align-items: center;
  z-index: 1000;
  border-radius: 4px;
}

/* 全屏模式优化 */
.fullscreen-calendar {
  position: fixed;
  top: 0;
  left: 0;
  width: 100vw;
  height: 100vh;
  z-index: 9999;
  background: white;
}

/* 响应式设计 */
@media (max-width: 960px) {
  .filter-panel {
    position: static;
    margin-bottom: 16px;
  }

  .task-layout {
    flex-direction: column;
    height: auto;
  }

  .filter-panel-container {
    width: 100%;
    max-width: 100%;
    margin-right: 0;
    margin-bottom: 16px;
  }

  .filter-panel-container.filter-panel-collapsed {
    height: 0;
    margin-bottom: 0;
    transform: translateY(-100%);
  }

  .calendar-container {
    height: auto;
    min-height: 480px;
  }
}

/* xs: 给 .calendar-container 显式 height（不是 min-height），让内部 <v-card height="100%"> 能正确级联 */
@media (max-width: 600px) {
  .calendar-container {
    height: 520px !important;
    min-height: 520px;
  }

  /* 折叠按钮：从「面板右侧中段」改成「日历上方中央」（半个按钮高探出日历顶部） */
  .filter-panel-toggle {
    top: -16px;
    left: 50%;
    transform: translateX(-50%);
  }
  /* 折叠态位置不变（已经在日历上方中央） */
  .filter-panel-toggle.filter-panel-toggle-collapsed {
    left: 50%;
  }
}

/* ── FullCalendar 自定义样式 ── */
.task-calendar :deep(.fc-event) {
  border: none;
  background: transparent;
  padding: 0;
  cursor: pointer;
}

.task-calendar :deep(.fc-event-main) {
  padding: 0;
}

.task-calendar :deep(.fc-event-custom) {
  display: flex;
  flex-direction: column;
  width: 100%;
  border-radius: 3px;
  overflow: hidden;
  border: 1px solid transparent;
  transition: all 0.15s;
  cursor: grab;
  position: relative;
}

.task-calendar :deep(.fc-event-custom:hover) {
  border-color: #000;
  box-shadow: 0 1px 4px rgba(0,0,0,0.15);
  z-index: 5;
}

.task-calendar :deep(.fc-event-content) {
  display: flex;
  align-items: center;
  gap: 3px;
  padding: 1px 3px;
  font-size: 14px;
  font-weight: 500;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  position: relative;
  z-index: 2;
}

.task-calendar :deep(.fc-event-avatar) {
  width: 22px;
  height: 22px;
  border-radius: 50%;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  font-size: 12px;
  font-weight: 700;
  flex-shrink: 0;
}

.task-calendar :deep(.fc-event-subject) {
  font-weight: 700;
  flex-shrink: 0;
}

.task-calendar :deep(.fc-event-title) {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  font-size: 14px;
  font-weight: 600;
}

.task-calendar :deep(.fc-event-pct) {
  margin-left: auto;
  padding-left: 4px;
  flex-shrink: 0;
  display: inline-flex;
  align-items: center;
  line-height: 0;
}

.task-calendar :deep(.fc-event-ring) {
  display: block;
}

.task-calendar :deep(.fc) {
  min-height: 300px;
}

.task-calendar.is-fullscreen :deep(.fc) {
  min-height: 300px;
  height: auto !important;
}

.task-calendar :deep(.fc-daygrid-day-frame) {
  min-height: 60px;
}

/* 跨天事件条加强可见性 */
.task-calendar :deep(.fc-h-event) {
  border-left: 3px solid rgba(0,0,0,0.3) !important;
}

.task-calendar :deep(.fc-daygrid-block-event .fc-event-main) {
  overflow: visible;
}

/* 移动端 xs：日历字号 / 按钮 padding / 事件字号压缩 */
@media (max-width: 600px) {
  .task-calendar :deep(.fc) { font-size: 12px; }
  .task-calendar :deep(.fc-toolbar-title) { font-size: 14px; }
  .task-calendar :deep(.fc-button) { padding: 4px 6px; font-size: 12px; }
  .task-calendar :deep(.fc-toolbar.fc-header-toolbar) {
    flex-wrap: wrap;
    gap: 4px;
  }
  .task-calendar :deep(.fc-event-content) { font-size: 11px; padding: 0 2px; }
  .task-calendar :deep(.fc-event-avatar) { width: 16px; height: 16px; font-size: 10px; }
  .task-calendar :deep(.fc-daygrid-day-frame) { min-height: 48px; }
}
</style>
