<script setup lang="ts">
import { computed, onMounted, reactive, ref } from 'vue'
import { Plus, Refresh, Search } from '@element-plus/icons-vue'
import http, { apiErrorMessage } from '../api/http'
import { useAuthStore } from '../stores/auth'

const auth = useAuthStore()
const isSuperAdmin = computed(() => auth.user?.roles.includes('SuperAdmin') ?? false)
const isAcademicAdmin = computed(() => auth.user?.roles.includes('AcademicAdmin') ?? false)
const isCollegeAdmin = computed(() => auth.user?.roles.includes('CollegeAdmin') ?? false)
const canGeneratePublic = computed(() => isSuperAdmin.value || isAcademicAdmin.value)
const loading = ref(false)
const rows = ref<any[]>([])
const total = ref(0)
const dialogVisible = ref(false)
const generationDialog = ref(false)
const generating = ref(false)
const editingId = ref('')
const selectedRows = ref<any[]>([])
const terms = ref<any[]>([])
const courses = ref<any[]>([])
const classes = ref<any[]>([])
const majors = ref<any[]>([])
const query = reactive({
  page: 1,
  pageSize: 20,
  keyword: '',
  academicTermId: undefined as string | undefined,
  status: undefined as string | undefined,
})
const form = reactive<Record<string, any>>({})
const generationForm = reactive<Record<string, any>>({})
const manualCourseFilters = reactive({
  keyword: '',
  collegeId: undefined as string | undefined,
  nature: undefined as string | undefined,
})
const manualClassFilters = reactive({
  keyword: '',
  collegeId: undefined as string | undefined,
  majorId: undefined as string | undefined,
  grade: undefined as number | undefined,
})
const generationCourseFilters = reactive({
  keyword: '',
  collegeId: undefined as string | undefined,
})
const generationClassFilters = reactive({
  keyword: '',
  collegeId: undefined as string | undefined,
  majorId: undefined as string | undefined,
  grade: undefined as number | undefined,
})
const eligibleTeachers = ref<any[]>([])
const manualEligibleTeachers = ref<any[]>([])
const statusLabels: Record<string, string> = {
  Draft: '草稿',
  Published: '已发布',
  Closed: '已归档',
}
const courseNatureLabels: Record<string, string> = {
  GeneralRequired: '公共必修',
  GeneralElective: '公共选修',
  MajorRequired: '专业必修',
  MajorElective: '专业选修',
  Practice: '实践教学',
}
const availableClasses = computed(() => {
  const enabled = classes.value.filter((item) => item.isEnabled)
  if (!auth.user?.roles.includes('CollegeAdmin') || !auth.user.collegeId) return enabled
  const ids = new Set(
    majors.value.filter((item) => item.collegeId === auth.user!.collegeId).map((item) => item.id),
  )
  return enabled.filter((item) => ids.has(item.majorId))
})
const selectedCourse = computed(() =>
  courses.value.find((item) => item.id === form.courseId),
)
const plannedHours = computed(() =>
  form.startWeek && form.endWeek && form.weeklyHours && form.endWeek >= form.startWeek
    ? (form.endWeek - form.startWeek + 1) * form.weeklyHours
    : 0,
)
const hoursMatch = computed(() =>
  !selectedCourse.value || plannedHours.value === selectedCourse.value.totalHours,
)
const assignableTeachers = computed(() =>
  manualEligibleTeachers.value.map((item) => ({
    id: item.teacherId,
    teacherNumber: item.teacherNumber,
    name: item.name,
    title: item.title,
    collegeName: item.collegeName,
  })),
)
const selectedTeachers = computed(() =>
  assignableTeachers.value.filter((item) => form.teacherIds?.includes(item.id)),
)
const publicCourses = computed(() =>
  courses.value.filter((item) =>
    ['GeneralRequired', 'GeneralElective'].includes(item.nature),
  ),
)
const selectedGenerationCourse = computed(() =>
  publicCourses.value.find((item) => item.id === generationForm.courseId),
)
const generationPlannedHours = computed(() =>
  generationForm.startWeek &&
  generationForm.endWeek &&
  generationForm.weeklyHours &&
  generationForm.endWeek >= generationForm.startWeek
    ? (generationForm.endWeek - generationForm.startWeek + 1) * generationForm.weeklyHours
    : 0,
)
const generationHoursMatch = computed(() =>
  !selectedGenerationCourse.value ||
  generationPlannedHours.value === selectedGenerationCourse.value.totalHours,
)
const manageableCourses = computed(() => {
  if (isSuperAdmin.value) return courses.value
  return courses.value.filter((item) => {
    const isPublic = ['GeneralRequired', 'GeneralElective'].includes(item.nature)
    return isAcademicAdmin.value && isPublic ||
      isCollegeAdmin.value &&
      item.collegeId === auth.user?.collegeId &&
      ['MajorRequired', 'MajorElective', 'Practice'].includes(item.nature)
  })
})
const courseColleges = computed(() => {
  const result = new Map<string, string>()
  courses.value.forEach((item) => result.set(item.collegeId, item.collegeName))
  return [...result].map(([id, name]) => ({ id, name })).sort((a, b) =>
    a.name.localeCompare(b.name, 'zh-CN'),
  )
})
const classColleges = computed(() => {
  const result = new Map<string, string>()
  majors.value.forEach((item) => result.set(item.collegeId, item.collegeName))
  return [...result].map(([id, name]) => ({ id, name })).sort((a, b) =>
    a.name.localeCompare(b.name, 'zh-CN'),
  )
})
const majorById = computed(() =>
  new Map(majors.value.map((item) => [item.id, item])),
)
const classGrades = computed(() =>
  [...new Set(availableClasses.value.map((item) => item.grade))].sort((a, b) => b - a),
)
const manualClassMajors = computed(() => filteredMajors(manualClassFilters.collegeId))
const generationClassMajors = computed(() => filteredMajors(generationClassFilters.collegeId))
const filteredManageableCourses = computed(() =>
  manageableCourses.value.filter((item) => courseMatches(item, manualCourseFilters)),
)
const filteredPublicCourses = computed(() =>
  publicCourses.value.filter((item) => courseMatches(item, generationCourseFilters)),
)
const filteredManualClasses = computed(() =>
  availableClasses.value.filter((item) => classMatches(item, manualClassFilters)),
)
const filteredGenerationClasses = computed(() =>
  availableClasses.value.filter((item) => classMatches(item, generationClassFilters)),
)
const selectedDraftRows = computed(() =>
  selectedRows.value.filter((item) => item.status === 'Draft'),
)
const selectedPublishedRows = computed(() =>
  selectedRows.value.filter((item) => item.status === 'Published'),
)
const selectedClosedRows = computed(() =>
  selectedRows.value.filter((item) => item.status === 'Closed'),
)
const responsibilityText = computed(() => {
  if (isSuperAdmin.value) return '超级管理员可处理全部教学任务，并保留跨层级纠错权限。'
  if (isAcademicAdmin.value && isCollegeAdmin.value) {
    return '兼岗账号负责校级公共课，以及本学院开设的专业必修、专业选修和实践课程。'
  }
  if (isCollegeAdmin.value) return '学院负责本院专业必修、专业选修和实践课程的教学任务。'
  return '校级教务负责公共必修和公共选修课程；专业课教学任务由课程所属学院维护。'
})
const generationGroupCount = computed(() =>
  Math.ceil((generationForm.classIds?.length ?? 0) / (generationForm.classesPerTask || 1)),
)

function canManageRow(row: any) {
  if (isSuperAdmin.value) return true
  const isPublic = ['GeneralRequired', 'GeneralElective'].includes(row.courseNature)
  return isAcademicAdmin.value && isPublic ||
    isCollegeAdmin.value && !isPublic
}

function courseMatches(item: any, filters: Record<string, any>) {
  const keyword = filters.keyword.trim().toLocaleLowerCase()
  return (!keyword ||
      item.code.toLocaleLowerCase().includes(keyword) ||
      item.name.toLocaleLowerCase().includes(keyword)) &&
    (!filters.collegeId || item.collegeId === filters.collegeId) &&
    (!filters.nature || item.nature === filters.nature)
}

function filteredMajors(collegeId?: string) {
  return majors.value
    .filter((item) =>
      item.isEnabled &&
      (!collegeId || item.collegeId === collegeId) &&
      availableClasses.value.some((administrativeClass) =>
        administrativeClass.majorId === item.id),
    )
    .sort((a, b) => a.code.localeCompare(b.code))
}

function classMatches(item: any, filters: Record<string, any>) {
  const keyword = filters.keyword.trim().toLocaleLowerCase()
  const major = majorById.value.get(item.majorId)
  return (!keyword ||
      item.code.toLocaleLowerCase().includes(keyword) ||
      item.name.toLocaleLowerCase().includes(keyword) ||
      item.majorName.toLocaleLowerCase().includes(keyword)) &&
    (!filters.collegeId || major?.collegeId === filters.collegeId) &&
    (!filters.majorId || item.majorId === filters.majorId) &&
    (!filters.grade || item.grade === filters.grade)
}

function resetOptionFilters(filters: Record<string, any>) {
  Object.keys(filters).forEach((key) => {
    filters[key] = key === 'keyword' ? '' : undefined
  })
}

function onClassCollegeChanged(filters: Record<string, any>) {
  if (filters.majorId &&
      majorById.value.get(filters.majorId)?.collegeId !== filters.collegeId) {
    filters.majorId = undefined
  }
}

function selectFilteredClasses(target: Record<string, any>, filtered: any[]) {
  const selected = new Set<string>(target.classIds ?? [])
  filtered.forEach((item) => selected.add(item.id))
  target.classIds = [...selected]
}

function clearFilteredClasses(target: Record<string, any>, filtered: any[]) {
  const filteredIds = new Set(filtered.map((item) => item.id))
  target.classIds = (target.classIds ?? []).filter((id: string) => !filteredIds.has(id))
}

function rowSelectable(row: any) {
  return canManageRow(row) && ['Draft', 'Published', 'Closed'].includes(row.status)
}

function onTaskSelectionChanged(selection: any[]) {
  selectedRows.value = selection
}

async function load() {
  loading.value = true
  try {
    const { data } = await http.get('/teaching-tasks', {
      params: {
        ...query,
        keyword: query.keyword || undefined,
      },
    })
    rows.value = data.items
    total.value = data.total
    selectedRows.value = []
  } catch (error) {
    ElMessage.error(apiErrorMessage(error))
  } finally {
    loading.value = false
  }
}

function resetFilters() {
  Object.assign(query, {
    page: 1,
    keyword: '',
    academicTermId: undefined,
    status: undefined,
  })
  load()
}

function resetForm(detail?: any) {
  Object.keys(form).forEach((key) => delete form[key])
  const currentTerm = terms.value.find((item) => item.isCurrent)
  Object.assign(form, {
    taskNumber: '',
    name: '',
    academicTermId: currentTerm?.id,
    courseId: undefined,
    capacity: 60,
    startWeek: 1,
    endWeek: 16,
     weeklyHours: 2,
     schedulingMode: 'Standard',
    teacherIds: [],
    primaryTeacherId: undefined,
    classIds: [],
    notes: '',
  }, detail ?? {})
}

function openCreate() {
  editingId.value = ''
  resetForm()
  resetOptionFilters(manualCourseFilters)
  resetOptionFilters(manualClassFilters)
  dialogVisible.value = true
}

async function openEdit(row: any) {
  try {
    const detail = (await http.get(`/teaching-tasks/${row.id}`)).data
    editingId.value = row.id
    resetForm(detail)
    resetOptionFilters(manualCourseFilters)
    resetOptionFilters(manualClassFilters)
    await loadManualEligibleTeachers()
    dialogVisible.value = true
  } catch (error) {
    ElMessage.error(apiErrorMessage(error))
  }
}

async function loadManualEligibleTeachers() {
  manualEligibleTeachers.value = []
  if (!form.academicTermId || !form.courseId) return
  try {
    manualEligibleTeachers.value = (await http.get('/teacher-course-applications/eligible', {
      params: {
        academicTermId: form.academicTermId,
        courseId: form.courseId,
      },
    })).data
  } catch (error) {
    ElMessage.error(apiErrorMessage(error))
  }
}

function onTeachersChanged(values: string[]) {
  if (form.primaryTeacherId && !values.includes(form.primaryTeacherId)) {
    form.primaryTeacherId = undefined
  }
}

async function save() {
  if (!form.taskNumber?.trim() || !form.name?.trim() || !form.academicTermId || !form.courseId) {
    ElMessage.warning('请填写任务编号、名称、学期和课程。')
    return
  }
  if (form.startWeek > form.endWeek) {
    ElMessage.warning('开始周不能晚于结束周。')
    return
  }
  if (!hoursMatch.value) {
    ElMessage.warning(
      `课程总学时为 ${selectedCourse.value.totalHours}，当前授课安排合计 ${plannedHours.value} 学时，请调整周次或周学时。`,
    )
    return
  }
  try {
    if (editingId.value) await http.put(`/teaching-tasks/${editingId.value}`, form)
    else await http.post('/teaching-tasks', form)
    ElMessage.success(editingId.value ? '教学任务已更新' : '教学任务草稿已创建')
    dialogVisible.value = false
    await load()
  } catch (error) {
    ElMessage.error(apiErrorMessage(error))
  }
}

async function remove(row: any) {
  const isArchived = row.status === 'Closed'
  try {
    await ElMessageBox.confirm(
      isArchived
        ? `将永久删除已归档教学任务“${row.name}”。如任务已被排课、选课、成绩或考试引用，系统会拒绝删除。确定继续吗？`
        : `确定删除教学任务草稿“${row.name}”吗？`,
      isArchived ? '删除已归档任务' : '删除草稿',
      {
        type: 'error',
        confirmButtonText: '确认删除',
        cancelButtonText: '取消',
      },
    )
    await http.delete(`/teaching-tasks/${row.id}`)
    ElMessage.success(isArchived ? '已归档教学任务已删除' : '教学任务草稿已删除')
    await load()
  } catch (error: any) {
    if (error !== 'cancel' && error !== 'close') ElMessage.error(apiErrorMessage(error))
  }
}

async function publish(row: any) {
  try {
    await ElMessageBox.confirm(
      '发布后任务将锁定，并可进入排课与选课流程。确定发布吗？',
      '发布教学任务',
      { type: 'warning', confirmButtonText: '发布', cancelButtonText: '取消' },
    )
    await http.post(`/teaching-tasks/${row.id}/publish`)
    ElMessage.success('教学任务已发布')
    await load()
  } catch (error: any) {
    if (error !== 'cancel' && error !== 'close') ElMessage.error(apiErrorMessage(error))
  }
}

async function closeTask(row: any) {
  try {
    await ElMessageBox.confirm('归档后将停止后续选课和排课调整，确定继续吗？', '归档教学任务', {
      type: 'warning',
      confirmButtonText: '确认归档',
      cancelButtonText: '取消',
    })
    await http.post(`/teaching-tasks/${row.id}/close`)
    ElMessage.success('教学任务已归档')
    await load()
  } catch (error: any) {
    if (error !== 'cancel' && error !== 'close') ElMessage.error(apiErrorMessage(error))
  }
}

async function unarchive(row: any) {
  try {
    await ElMessageBox.confirm(
      '撤销归档后，教学任务将恢复为已发布状态，并重新进入相关业务流程。确定继续吗？',
      '撤销教学任务归档',
      {
        type: 'warning',
        confirmButtonText: '撤销归档',
        cancelButtonText: '取消',
      },
    )
    await http.post(`/teaching-tasks/${row.id}/unarchive`)
    ElMessage.success('教学任务已撤销归档')
    await load()
  } catch (error: any) {
    if (error !== 'cancel' && error !== 'close') ElMessage.error(apiErrorMessage(error))
  }
}

async function batchAction(
  operation: 'Publish' | 'Delete' | 'Archive' | 'Unarchive',
  targets: any[],
) {
  if (!targets.length) return
  const deletingArchived = operation === 'Delete' &&
    targets.every((item) => item.status === 'Closed')
  const descriptions = {
    Publish: {
      title: '批量发布教学任务',
      message: `将发布选中的 ${targets.length} 个草稿。发布前会逐项校验教师资格、行政班和容量，确定继续吗？`,
      confirmButtonText: '批量发布',
      success: '教学任务已批量发布',
      type: 'warning',
    },
    Delete: {
      title: deletingArchived ? '批量删除已归档任务' : '批量删除教学任务草稿',
      message: deletingArchived
        ? `将永久删除选中的 ${targets.length} 个已归档任务。已被后续业务引用的任务会拒绝删除，此操作不可撤销。确定继续吗？`
        : `将永久删除选中的 ${targets.length} 个草稿，此操作不可撤销。确定继续吗？`,
      confirmButtonText: '批量删除',
      success: deletingArchived ? '已归档教学任务已批量删除' : '教学任务草稿已批量删除',
      type: 'error',
    },
    Archive: {
      title: '批量归档教学任务',
      message: `将选中的 ${targets.length} 个已发布任务结课并归档，后续将停止选课和排课调整。确定继续吗？`,
      confirmButtonText: '批量归档',
      success: '教学任务已批量归档',
      type: 'warning',
    },
    Unarchive: {
      title: '批量撤销教学任务归档',
      message: `将选中的 ${targets.length} 个已归档任务恢复为已发布状态，并重新进入相关业务流程。确定继续吗？`,
      confirmButtonText: '批量撤销归档',
      success: '教学任务已批量撤销归档',
      type: 'warning',
    },
  } as const
  const description = descriptions[operation]
  try {
    await ElMessageBox.confirm(description.message, description.title, {
      type: description.type,
      confirmButtonText: description.confirmButtonText,
      cancelButtonText: '取消',
    })
    const { data } = await http.post('/teaching-tasks/batch', {
      operation,
      ids: targets.map((item) => item.id),
    })
    ElMessage.success(`${description.success}，共 ${data.affectedCount} 个`)
    await load()
  } catch (error: any) {
    if (error !== 'cancel' && error !== 'close') ElMessage.error(apiErrorMessage(error))
  }
}

function openGeneration() {
  const currentTerm = terms.value.find((item) => item.isCurrent)
  Object.assign(generationForm, {
    academicTermId: query.academicTermId ?? currentTerm?.id,
    courseId: undefined,
    classesPerTask: 3,
    startWeek: 1,
    endWeek: 16,
    weeklyHours: 2,
    classIds: [],
  })
  eligibleTeachers.value = []
  resetOptionFilters(generationCourseFilters)
  resetOptionFilters(generationClassFilters)
  generationDialog.value = true
}

async function loadEligibleTeachers() {
  eligibleTeachers.value = []
  if (!generationForm.academicTermId || !generationForm.courseId) return
  try {
    eligibleTeachers.value = (await http.get('/teacher-course-applications/eligible', {
      params: {
        academicTermId: generationForm.academicTermId,
        courseId: generationForm.courseId,
      },
    })).data
  } catch (error) {
    ElMessage.error(apiErrorMessage(error))
  }
}

async function generatePublicTasks() {
  if (!generationForm.academicTermId ||
      !generationForm.courseId ||
      !generationForm.classIds?.length) {
    ElMessage.warning('请选择学期、公共课和需要合班的行政班。')
    return
  }
  if (!eligibleTeachers.value.length) {
    ElMessage.warning('该课程还没有审核通过的可授课教师。')
    return
  }
  if (!generationHoursMatch.value) {
    ElMessage.warning(
      `课程总学时为 ${selectedGenerationCourse.value.totalHours}，当前授课安排合计 ${generationPlannedHours.value} 学时，请调整周次或周学时。`,
    )
    return
  }
  try {
    await ElMessageBox.confirm(
      `将 ${generationForm.classIds.length} 个行政班分成 ${generationGroupCount.value} 个教学班，并在 ${eligibleTeachers.value.length} 名合格教师中随机均衡分配。生成结果为草稿，确定继续吗？`,
      '批量生成公共课教学任务',
      { type: 'warning', confirmButtonText: '生成草稿', cancelButtonText: '取消' },
    )
    generating.value = true
    const { data } = await http.post('/teaching-tasks/generate-public-course', generationForm)
    generationDialog.value = false
    ElMessage.success(`已生成 ${data.createdCount} 个教学任务草稿`)
    await load()
  } catch (error: any) {
    if (error !== 'cancel' && error !== 'close') ElMessage.error(apiErrorMessage(error))
  } finally {
    generating.value = false
  }
}

onMounted(async () => {
  const [termRes, courseRes, classRes, majorRes] = await Promise.all([
    http.get('/base-data/terms'),
    http.get('/courses/options'),
    http.get('/base-data/classes'),
    http.get('/base-data/majors'),
  ])
  terms.value = termRes.data
  courses.value = courseRes.data
  classes.value = classRes.data
  majors.value = majorRes.data
  query.academicTermId = terms.value.find((item) => item.isCurrent)?.id
  await load()
})
</script>

<template>
  <div class="page-stack">
    <section class="page-intro">
      <div>
        <span class="section-kicker">TEACHING COMMISSION</span>
        <h2>教学任务</h2>
        <p>将学期、课程、授课教师和行政班组合为可排课、可选课的教学班。</p>
      </div>
      <div class="page-actions">
        <el-button v-if="canGeneratePublic" @click="openGeneration">公共课合班生成</el-button>
        <el-button type="primary" :icon="Plus" @click="openCreate">新建教学任务</el-button>
      </div>
    </section>

    <section class="task-summary">
      <div><span>当前结果</span><b>{{ total }}</b><small>个教学班</small></div>
      <p>{{ responsibilityText }} 发布时还会校验教师授课资格、容量和周次；选修课可不关联行政班。</p>
    </section>

    <section class="data-card">
      <div class="filter-bar">
        <el-input v-model="query.keyword" :prefix-icon="Search" clearable placeholder="搜索任务编号、教学班或课程" @keyup.enter="query.page = 1; load()" />
        <el-select v-model="query.academicTermId" clearable placeholder="全部学期">
          <el-option v-for="item in terms" :key="item.id" :label="item.name" :value="item.id" />
        </el-select>
        <el-select v-model="query.status" clearable placeholder="全部状态">
          <el-option v-for="(label, value) in statusLabels" :key="value" :label="label" :value="value" />
        </el-select>
        <el-button type="primary" @click="query.page = 1; load()">查询</el-button>
        <el-button :icon="Refresh" @click="resetFilters">重置</el-button>
      </div>

      <div v-if="selectedRows.length" class="batch-action-bar">
        <div>
          <b>已选择 {{ selectedRows.length }} 个教学任务</b>
          <span>可操作：{{ selectedDraftRows.length }} 个草稿，{{ selectedPublishedRows.length }} 个已发布，{{ selectedClosedRows.length }} 个已归档</span>
        </div>
        <div>
          <el-button
            type="success"
            plain
            :disabled="!selectedDraftRows.length"
            @click="batchAction('Publish', selectedDraftRows)"
          >
            发布草稿（{{ selectedDraftRows.length }}）
          </el-button>
          <el-button
            type="danger"
            plain
            :disabled="!selectedDraftRows.length"
            @click="batchAction('Delete', selectedDraftRows)"
          >
            删除草稿（{{ selectedDraftRows.length }}）
          </el-button>
          <el-button
            type="warning"
            plain
            :disabled="!selectedPublishedRows.length"
            @click="batchAction('Archive', selectedPublishedRows)"
          >
            归档已发布（{{ selectedPublishedRows.length }}）
          </el-button>
          <el-button
            type="primary"
            plain
            :disabled="!selectedClosedRows.length"
            @click="batchAction('Unarchive', selectedClosedRows)"
          >
            撤销归档（{{ selectedClosedRows.length }}）
          </el-button>
          <el-button
            type="danger"
            plain
            :disabled="!selectedClosedRows.length"
            @click="batchAction('Delete', selectedClosedRows)"
          >
            删除已归档（{{ selectedClosedRows.length }}）
          </el-button>
        </div>
      </div>

      <el-table
        v-loading="loading"
        :data="rows"
        class="data-table task-table"
        @selection-change="onTaskSelectionChanged"
      >
        <el-table-column type="selection" width="48" :selectable="rowSelectable" />
        <el-table-column label="任务编号" width="155"><template #default="{ row }"><span class="registry-number">{{ row.taskNumber }}</span></template></el-table-column>
        <el-table-column label="教学班 / 课程" min-width="230">
          <template #default="{ row }">
            <div class="course-name">
              <b>{{ row.name }}</b>
              <span>{{ row.courseCode }} · {{ row.courseName }}</span>
              <small>{{ ['GeneralRequired', 'GeneralElective'].includes(row.courseNature) ? '校级教务负责' : '开课学院负责' }}</small>
            </div>
          </template>
        </el-table-column>
        <el-table-column prop="termName" label="开课学期" min-width="180" />
        <el-table-column label="授课教师" min-width="130"><template #default="{ row }">{{ row.teacherNames.join('、') || '未安排' }}</template></el-table-column>
        <el-table-column label="行政班" min-width="150"><template #default="{ row }">{{ row.classNames.join('、') || '开放选课' }}</template></el-table-column>
        <el-table-column label="人数 / 容量" width="105"><template #default="{ row }">{{ row.studentCount }} / {{ row.capacity }}</template></el-table-column>
        <el-table-column label="周次" width="105"><template #default="{ row }">{{ row.startWeek }}—{{ row.endWeek }} 周</template></el-table-column>
        <el-table-column label="状态" width="90"><template #default="{ row }"><span class="table-status" :class="{ off: row.status === 'Closed' }">{{ statusLabels[row.status] }}</span></template></el-table-column>
        <el-table-column label="操作" width="210" fixed="right">
          <template #default="{ row }">
            <el-button v-if="canManageRow(row) && row.status === 'Draft'" link type="primary" @click="openEdit(row)">编辑</el-button>
            <el-button v-if="canManageRow(row) && row.status === 'Draft'" link type="success" @click="publish(row)">发布</el-button>
            <el-button v-if="canManageRow(row) && row.status === 'Draft'" link type="danger" @click="remove(row)">删除</el-button>
            <el-button v-if="canManageRow(row) && row.status === 'Published'" link type="warning" @click="closeTask(row)">归档</el-button>
            <el-button v-if="canManageRow(row) && row.status === 'Closed'" link type="primary" @click="unarchive(row)">撤销归档</el-button>
            <el-button v-if="canManageRow(row) && row.status === 'Closed'" link type="danger" @click="remove(row)">删除</el-button>
            <span v-if="!canManageRow(row) && row.status !== 'Closed'" class="muted-action">只读</span>
            <span v-if="!canManageRow(row) && row.status === 'Closed'" class="muted-action">已归档</span>
          </template>
        </el-table-column>
        <template #empty><el-empty description="没有符合条件的教学任务" /></template>
      </el-table>
      <div class="pagination-bar">
        <span>共 {{ total }} 个教学班</span>
        <el-pagination v-model:current-page="query.page" :page-size="query.pageSize" :total="total" layout="prev, pager, next" @current-change="load" />
      </div>
    </section>

    <el-dialog v-model="dialogVisible" :title="editingId ? '编辑教学任务' : '新建教学任务'" width="720px">
      <el-form label-position="top">
        <div class="form-grid">
          <el-form-item label="任务编号" required><el-input v-model="form.taskNumber" placeholder="如 2026-1-CS101-01" /></el-form-item>
          <el-form-item label="教学班名称" required><el-input v-model="form.name" /></el-form-item>
        </div>
        <div class="form-grid">
          <el-form-item label="开课学期" required><el-select v-model="form.academicTermId" @change="loadManualEligibleTeachers"><el-option v-for="item in terms" :key="item.id" :label="item.name" :value="item.id" /></el-select></el-form-item>
          <el-form-item label="课程" required>
            <el-select v-model="form.courseId" filterable @change="form.teacherIds = []; form.primaryTeacherId = undefined; loadManualEligibleTeachers()">
              <el-option v-for="item in filteredManageableCourses" :key="item.id" :label="`${item.code} · ${item.name}（${item.totalHours} 学时）`" :value="item.id" />
            </el-select>
          </el-form-item>
        </div>
        <div class="option-filter-panel">
          <span class="option-filter-title">筛选课程</span>
          <div class="option-filter-grid course">
            <el-input v-model="manualCourseFilters.keyword" clearable placeholder="课程代码或名称" />
            <el-select v-model="manualCourseFilters.collegeId" clearable placeholder="全部开课单位">
              <el-option v-for="item in courseColleges" :key="item.id" :label="item.name" :value="item.id" />
            </el-select>
            <el-select v-model="manualCourseFilters.nature" clearable placeholder="全部课程性质">
              <el-option v-for="(label, value) in courseNatureLabels" :key="value" :label="label" :value="value" />
            </el-select>
          </div>
          <small>当前显示 {{ filteredManageableCourses.length }} 门可维护课程</small>
        </div>
        <div class="form-grid three">
          <el-form-item label="容量"><el-input-number v-model="form.capacity" :min="1" /></el-form-item>
          <el-form-item label="开始周"><el-input-number v-model="form.startWeek" :min="1" :max="30" /></el-form-item>
          <el-form-item label="结束周"><el-input-number v-model="form.endWeek" :min="1" :max="30" /></el-form-item>
        </div>
         <el-form-item label="周学时"><el-input-number v-model="form.weeklyHours" :min="1" :max="40" /></el-form-item>
        <div
          v-if="selectedCourse"
          :class="['hours-validation', { invalid: !hoursMatch }]"
        >
          <b>{{ hoursMatch ? '学时匹配' : '学时不匹配' }}</b>
          <span>
            第 {{ form.startWeek }}—{{ form.endWeek }} 周 × 每周 {{ form.weeklyHours }} 学时
            = {{ plannedHours }} 学时；课程库总学时 {{ selectedCourse.totalHours }}。
          </span>
        </div>
        <el-form-item label="授课方式">
          <el-radio-group v-model="form.schedulingMode">
            <el-radio-button value="Standard">正常排课</el-radio-button>
            <el-radio-button value="Flexible">非排时课程</el-radio-button>
          </el-radio-group>
          <small class="field-hint">
            非排时课程不进入自动排课，不占用星期、节次和场地，并在课表中单独列出。
          </small>
        </el-form-item>
        <el-form-item label="授课教师">
          <el-select v-model="form.teacherIds" multiple filterable @change="onTeachersChanged">
            <el-option v-for="item in assignableTeachers" :key="item.id" :label="`${item.teacherNumber} · ${item.name}`" :value="item.id" />
          </el-select>
          <small v-if="selectedCourse" class="field-hint">仅显示本学期已审核通过或由学院直接分配的授课资格教师。</small>
        </el-form-item>
        <el-form-item label="主讲教师">
          <el-select v-model="form.primaryTeacherId" clearable :disabled="!form.teacherIds?.length">
            <el-option v-for="item in selectedTeachers" :key="item.id" :label="item.name" :value="item.id" />
          </el-select>
        </el-form-item>
        <el-form-item label="关联行政班（选修课可留空）">
          <div class="class-picker">
            <div class="option-filter-grid classes">
              <el-input v-model="manualClassFilters.keyword" clearable placeholder="班级代码、名称或专业" />
              <el-select v-model="manualClassFilters.collegeId" clearable placeholder="全部学院" @change="onClassCollegeChanged(manualClassFilters)">
                <el-option v-for="item in classColleges" :key="item.id" :label="item.name" :value="item.id" />
              </el-select>
              <el-select v-model="manualClassFilters.majorId" clearable placeholder="全部专业">
                <el-option v-for="item in manualClassMajors" :key="item.id" :label="item.name" :value="item.id" />
              </el-select>
              <el-select v-model="manualClassFilters.grade" clearable placeholder="全部年级">
                <el-option v-for="grade in classGrades" :key="grade" :label="`${grade} 级`" :value="grade" />
              </el-select>
            </div>
            <div class="class-picker-actions">
              <span>筛选到 {{ filteredManualClasses.length }} 个班，已选择 {{ form.classIds?.length ?? 0 }} 个</span>
              <div>
                <el-button link type="primary" @click="selectFilteredClasses(form, filteredManualClasses)">选择筛选结果</el-button>
                <el-button link @click="clearFilteredClasses(form, filteredManualClasses)">取消筛选结果</el-button>
              </div>
            </div>
          </div>
          <el-select v-model="form.classIds" multiple filterable collapse-tags collapse-tags-tooltip>
            <el-option v-for="item in filteredManualClasses" :key="item.id" :label="`${item.code} · ${item.name}`" :value="item.id" />
          </el-select>
        </el-form-item>
        <el-form-item label="备注"><el-input v-model="form.notes" type="textarea" :rows="2" /></el-form-item>
      </el-form>
      <template #footer><el-button @click="dialogVisible = false">取消</el-button><el-button type="primary" @click="save">保存草稿</el-button></template>
    </el-dialog>

    <el-dialog v-model="generationDialog" title="公共课合班生成教学任务" width="760px">
      <div class="generation-notice">
        行政班按编码顺序分组；教师只从学院审核通过的名单中选择，并优先分配给本学期教学任务较少的教师。
      </div>
      <el-form label-position="top">
        <div class="form-grid">
          <el-form-item label="开课学期" required>
            <el-select v-model="generationForm.academicTermId" @change="loadEligibleTeachers">
              <el-option v-for="item in terms" :key="item.id" :label="item.name" :value="item.id" />
            </el-select>
          </el-form-item>
          <el-form-item label="公共课程" required>
            <el-select v-model="generationForm.courseId" filterable @change="loadEligibleTeachers">
              <el-option v-for="item in filteredPublicCourses" :key="item.id" :label="`${item.code} · ${item.name}`" :value="item.id" />
            </el-select>
          </el-form-item>
        </div>
        <div class="option-filter-panel">
          <span class="option-filter-title">筛选公共课程</span>
          <div class="option-filter-grid course compact">
            <el-input v-model="generationCourseFilters.keyword" clearable placeholder="课程代码或名称" />
            <el-select v-model="generationCourseFilters.collegeId" clearable placeholder="全部开课单位">
              <el-option v-for="item in courseColleges" :key="item.id" :label="item.name" :value="item.id" />
            </el-select>
          </div>
          <small>当前显示 {{ filteredPublicCourses.length }} 门公共课程</small>
        </div>
        <el-alert
          :title="eligibleTeachers.length ? `已有 ${eligibleTeachers.length} 名教师审核通过` : '请选择课程查看可分配教师'"
          :type="eligibleTeachers.length ? 'success' : 'warning'"
          :closable="false"
          show-icon
        >
          <template #default>
            <span v-if="eligibleTeachers.length">{{ eligibleTeachers.map((item) => item.name).join('、') }}</span>
          </template>
        </el-alert>
        <el-form-item label="参与合班的行政班" required style="margin-top: 16px">
          <div class="class-picker">
            <div class="option-filter-grid classes">
              <el-input v-model="generationClassFilters.keyword" clearable placeholder="班级代码、名称或专业" />
              <el-select v-model="generationClassFilters.collegeId" clearable placeholder="全部学院" @change="onClassCollegeChanged(generationClassFilters)">
                <el-option v-for="item in classColleges" :key="item.id" :label="item.name" :value="item.id" />
              </el-select>
              <el-select v-model="generationClassFilters.majorId" clearable placeholder="全部专业">
                <el-option v-for="item in generationClassMajors" :key="item.id" :label="item.name" :value="item.id" />
              </el-select>
              <el-select v-model="generationClassFilters.grade" clearable placeholder="全部年级">
                <el-option v-for="grade in classGrades" :key="grade" :label="`${grade} 级`" :value="grade" />
              </el-select>
            </div>
            <div class="class-picker-actions">
              <span>筛选到 {{ filteredGenerationClasses.length }} 个班，已选择 {{ generationForm.classIds?.length ?? 0 }} 个</span>
              <div>
                <el-button link type="primary" @click="selectFilteredClasses(generationForm, filteredGenerationClasses)">选择筛选结果</el-button>
                <el-button link @click="clearFilteredClasses(generationForm, filteredGenerationClasses)">取消筛选结果</el-button>
              </div>
            </div>
          </div>
          <el-select v-model="generationForm.classIds" multiple filterable collapse-tags>
            <el-option v-for="item in filteredGenerationClasses" :key="item.id" :label="`${item.code} · ${item.name}`" :value="item.id" />
          </el-select>
        </el-form-item>
        <div class="form-grid three">
          <el-form-item label="每个教学班合并行政班数">
            <el-input-number v-model="generationForm.classesPerTask" :min="1" :max="10" />
          </el-form-item>
          <el-form-item label="开始周">
            <el-input-number v-model="generationForm.startWeek" :min="1" :max="30" />
          </el-form-item>
          <el-form-item label="结束周">
            <el-input-number v-model="generationForm.endWeek" :min="1" :max="30" />
          </el-form-item>
        </div>
         <el-form-item label="周学时">
           <el-input-number v-model="generationForm.weeklyHours" :min="1" :max="40" />
         </el-form-item>
        <div
          v-if="selectedGenerationCourse"
          :class="['hours-validation', { invalid: !generationHoursMatch }]"
        >
          <b>{{ generationHoursMatch ? '学时匹配' : '学时不匹配' }}</b>
          <span>
            当前合计 {{ generationPlannedHours }} 学时；课程库总学时
            {{ selectedGenerationCourse.totalHours }}。
          </span>
        </div>
        <div class="generation-preview">
          <span>预计生成</span>
          <b>{{ generationGroupCount }}</b>
          <small>个教学任务草稿</small>
        </div>
      </el-form>
      <template #footer>
        <el-button @click="generationDialog = false">取消</el-button>
        <el-button type="primary" :loading="generating" @click="generatePublicTasks">生成教学任务草稿</el-button>
      </template>
    </el-dialog>
  </div>
</template>
