// aislop-ignore-file: duplicate-block -- 表格/表单声明结构相似且参数不同，渲染逻辑已共享组件化
import React, { useMemo, useRef, useState } from 'react';
import { Button, Card, Form, Segmented, Select, Space } from 'antd';
import { CalendarOutlined, CloudSyncOutlined, LeftOutlined, RightOutlined } from '@ant-design/icons';
import dayjs, { Dayjs } from 'dayjs';
import api from '../../api';
import PermissionButton from '../../components/PermissionButton';
import { QueryErrorState } from '../../components/QueryState';
import { NextStepHint } from '../../components/NextStepHint';
import { usePermission } from '../../hooks/usePermission';
import { useVisibleRefetch } from '../../hooks/usePageVisible';
import { message } from '../../ui/app-message';
import { useQuery } from '@tanstack/react-query';
import { useApiMutation } from '../../hooks/useApiMutation';
import { validateResponse } from '../../utils/validate';
import { scheduleLookupsSchema, weeklyScheduleSchema } from '../../api/schemas';
import {
  buildSchedulePayload,
  scheduleToFormValues,
  type ScheduleFormValues,
} from './schedule-form';
import { filterSchedulesForClass, isMaskedSchedule } from './schedule-visibility';
import { classifySyncResult } from './sync-result';
import { getErrorMessage } from '../../utils/error';
import { ScheduleGrid } from './ScheduleGrids';
import type {
  ClassItem,
  ClassScheduleItem,
  ClassTeacherOption,
  ClassroomItem,
} from './ScheduleGrids';
import { ScheduleModal, SyncModal } from './ScheduleModals';

interface ScheduleSyncResult {
  scheduleCount: number;
  shiftCount: number;
  groupCount: number;
  syncedItems: number;
  skippedNoMapping: number;
  failedBatchCount: number;
  failedItems: number;
  errors: string[];
  groups: Array<{ className: string; groupId: number; itemCount: number }>;
}

const SchedulesPage: React.FC = () => {
  const { hasPermission } = usePermission();
  const [viewMode, setViewMode] = useState<'week' | 'month'>('week');
  const [viewDate, setViewDate] = useState<Dayjs>(() => dayjs().weekday(1).startOf('day'));
  const [selectedDate, setSelectedDate] = useState<Dayjs | null>(null);
  const [classTeachers, setClassTeachers] = useState<ClassTeacherOption[]>([]);
  const [classTeachersLoading, setClassTeachersLoading] = useState(false);
  const [filterClassroomIds, setFilterClassroomIds] = useState<number[]>([]);
  const [filterClassId, setFilterClassId] = useState<number | undefined>(undefined);
  const [modalOpen, setModalOpen] = useState(false);
  const [modalMode, setModalMode] = useState<'create' | 'edit' | 'detail'>('create');
  const [editingSchedule, setEditingSchedule] = useState<ClassScheduleItem | null>(null);
  const [selectedCell, setSelectedCell] = useState<{
    classroomId: number;
    weekDay: number;
  } | null>(null);
  const [selectedSchedules, setSelectedSchedules] = useState<ClassScheduleItem[]>([]);
  const [submitting, setSubmitting] = useState(false);
  const [syncModalOpen, setSyncModalOpen] = useState(false);
  const [syncing, setSyncing] = useState(false);
  // 排课创建成功后的「下一步：同步钉钉」引导
  const [syncHint, setSyncHint] = useState(false);
  const [syncStatus, setSyncStatus] = useState<{
    activeSchedules: number;
    mappedClasses: number;
    totalClasses: number;
  } | null>(null);
  const [syncStatusError, setSyncStatusError] = useState(false);
  const [syncResult, setSyncResult] = useState<ScheduleSyncResult | null>(null);
  const [syncDateFrom, setSyncDateFrom] = useState<Dayjs>(dayjs);
  const [syncDays, setSyncDays] = useState(30);
  const [attendanceMachineOnly, setAttendanceMachineOnly] = useState(false);
  const [form] = Form.useForm<ScheduleFormValues>();

  const openSyncModal = async () => {
    setSyncModalOpen(true);
    setSyncResult(null);
    setSyncStatusError(false);
    try {
      const res = await api.get<{
        success: boolean;
        data: { activeSchedules: number; mappedClasses: number; totalClasses: number };
      }>('/sync/schedule/status');
      setSyncStatus(res.data);
    } catch {
      setSyncStatusError(true);
      setSyncStatus(null);
    }
  };

  const handleSyncSchedule = async () => {
    setSyncing(true);
    try {
      const res = await api.post<{
        success: boolean;
        data: ScheduleSyncResult;
      }>('/sync/schedule/sync', null, {
        params: {
          dateFrom: syncDateFrom.format('YYYY-MM-DD'),
          days: syncDays,
          attendanceMachineOnly,
        },
      });
      setSyncResult(res.data);
      const classification = classifySyncResult(res.data);
      if (classification.level === 'error') {
        message.error(classification.message);
      } else if (classification.level === 'warning') {
        message.warning(classification.message);
        setSyncHint(false);
      } else {
        message.success(classification.message);
        setSyncHint(false);
      }
    } catch (e: unknown) {
      message.error(getErrorMessage(e, '同步失败'));
    } finally {
      setSyncing(false);
    }
  };

  const weekStart = useMemo(() => viewDate.weekday(1).startOf('day'), [viewDate]);
  const monthStart = useMemo(() => viewDate.startOf('month'), [viewDate]);
  const weekEnd = useMemo(() => weekStart.add(6, 'day'), [weekStart]);
  const weekNum = useMemo(() => weekStart.week(), [weekStart]);
  const weekYear = useMemo(() => weekStart.year(), [weekStart]);
  const calendarDays = useMemo(() => {
    const monthEnd = monthStart.endOf('month');
    const startOffset = (monthStart.day() + 6) % 7;
    const endOffset = (7 - monthEnd.day()) % 7;
    const start = monthStart.subtract(startOffset, 'day');
    const end = monthEnd.add(endOffset, 'day');
    const totalDays = end.diff(start, 'day') + 1;
    const days: Dayjs[] = [];
    for (let i = 0; i < totalDays; i++) {
      days.push(start.add(i, 'day'));
    }
    return days;
  }, [monthStart]);

  const weeks = useMemo(() => {
    const result: Dayjs[][] = [];
    for (let i = 0; i < calendarDays.length; i += 7) {
      result.push(calendarDays.slice(i, i + 7));
    }
    return result;
  }, [calendarDays]);

  const startDateStr = useMemo(() => {
    if (viewMode === 'month') return calendarDays[0].format('YYYY-MM-DD');
    return weekStart.format('YYYY-MM-DD');
  }, [viewMode, weekStart, calendarDays]);

  const endDateStr = useMemo(() => {
    if (viewMode === 'month') return calendarDays[calendarDays.length - 1].format('YYYY-MM-DD');
    return weekEnd.format('YYYY-MM-DD');
  }, [viewMode, weekEnd, calendarDays]);

  const {
    data: fetchResult = { classrooms: [], classes: [], matrix: {} },
    isLoading,
    isFetching,
    isError,
    refetch,
  } = useQuery<{
    classrooms: ClassroomItem[];
    classes: ClassItem[];
    matrix: Record<number, Record<number, ClassScheduleItem[]>>;
  }>({
    queryKey: ['class-schedules', startDateStr, endDateStr, filterClassroomIds],
    queryFn: async () => {
      const [lookups, schedulesRes] = await Promise.all([
        api.get('/class-schedules/lookups') as Promise<{
          classrooms: ClassroomItem[];
          classes: ClassItem[];
        }>,
        api.get('/class-schedules/weekly', {
          params: {
            startDate: startDateStr,
            endDate: endDateStr,
            ...(filterClassroomIds.length === 1 ? { classroomId: filterClassroomIds[0] } : {}),
          },
        }) as Promise<Record<string, Record<string, ClassScheduleItem[]>>>,
      ]);
      const validatedLookups = validateResponse<{
        classrooms: ClassroomItem[];
        classes: ClassItem[];
      }>(scheduleLookupsSchema, lookups);
      const validatedWeekly = validateResponse<
        Record<string, Record<string, ClassScheduleItem[]>>
      >(weeklyScheduleSchema, schedulesRes);

      const typedMatrix: Record<number, Record<number, ClassScheduleItem[]>> = {};
      for (const [cId, dayMap] of Object.entries(validatedWeekly)) {
        const classroomId = Number(cId);
        typedMatrix[classroomId] = {};
        for (const [wd, schedules] of Object.entries(dayMap)) {
          typedMatrix[classroomId][Number(wd)] = schedules;
        }
      }
      return {
        classrooms: validatedLookups.classrooms,
        classes: validatedLookups.classes,
        matrix: typedMatrix,
      };
    },
  });
  const classrooms = fetchResult.classrooms;
  const classes = fetchResult.classes;
  const matrix = fetchResult.matrix;
  const loading = isLoading || isFetching;
  // RouteKeeper 保活页面切回时刷新排课数据
  useVisibleRefetch(['class-schedules']);

  const saveMutation = useApiMutation(
    async (payload: Record<string, unknown>) =>
      modalMode === 'edit' && editingSchedule
        ? api.put(`/class-schedules/${editingSchedule.id}`, payload)
        : api.post('/class-schedules', payload),
    { invalidate: [['class-schedules']] },
  );
  const disableMutation = useApiMutation(
    async (id: number) => api.put(`/class-schedules/${id}`, { status: 'inactive' }),
    { invalidate: [['class-schedules']] },
  );

  const filteredClassrooms = useMemo(() => {
    if (filterClassroomIds.length === 0) return classrooms;
    const idSet = new Set(filterClassroomIds);
    return classrooms.filter((c) => idSet.has(c.id));
  }, [classrooms, filterClassroomIds]);

  const displayMatrix = useMemo(() => {
    if (filterClassId == null) return matrix;
    const filtered: Record<number, Record<number, ClassScheduleItem[]>> = {};
    for (const [cId, dayMap] of Object.entries(matrix)) {
      const classroomId = Number(cId);
      filtered[classroomId] = {};
      for (const [wd, schedules] of Object.entries(dayMap)) {
        const matched = filterSchedulesForClass(schedules, filterClassId);
        if (matched.length > 0) {
          filtered[classroomId][Number(wd)] = matched;
        }
      }
    }
    return filtered;
  }, [matrix, filterClassId]);

  const monthScheduleMap = useMemo(() => {
    const map: Record<string, ClassScheduleItem[]> = {};
    for (const day of calendarDays) {
      const wd = day.day() === 0 ? 7 : day.day();
      const dateStr = day.format('YYYY-MM-DD');
      const result: ClassScheduleItem[] = [];
      for (const classroom of filteredClassrooms) {
        const daySchedules = displayMatrix[classroom.id]?.[wd] || [];
        for (const s of daySchedules) {
          if (dateStr >= s.startDate && dateStr <= s.endDate) {
            result.push(s);
          }
        }
      }
      map[dateStr] = result;
    }
    return map;
  }, [calendarDays, displayMatrix, filteredClassrooms]);

  const handleCellClick = (classroomId: number, weekDay: number) => {
    const schedules = displayMatrix[classroomId]?.[weekDay] || [];
    setSelectedCell({ classroomId, weekDay });
    setSelectedDate(null);
    if (schedules.length > 0) {
      setSelectedSchedules(schedules);
      setModalMode('detail');
      setModalOpen(true);
    } else if (hasPermission('schedule:create')) {
      setSelectedSchedules([]);
      setEditingSchedule(null);
      setModalMode('create');
      // 作废编辑/默认值流程中在途的教师请求，避免过期响应回填刚重置的表单
      teacherReqSeq.current += 1;
      form.resetFields();
      form.setFieldsValue({ classroomId, weekDay, attendanceAdvanceMinutes: 30 });
      setModalOpen(true);
    }
  };

  const getSchedulesForDate = (date: Dayjs): ClassScheduleItem[] => {
    const wd = date.day() === 0 ? 7 : date.day();
    const dateStr = date.format('YYYY-MM-DD');
    const result: ClassScheduleItem[] = [];
    for (const classroom of filteredClassrooms) {
      const daySchedules = displayMatrix[classroom.id]?.[wd] || [];
      for (const s of daySchedules) {
        if (dateStr >= s.startDate && dateStr <= s.endDate) {
          result.push(s);
        }
      }
    }
    return result;
  };

  const handleDateClick = (date: Dayjs) => {
    const dateKey = date.format('YYYY-MM-DD');
    const schedules = monthScheduleMap[dateKey] || getSchedulesForDate(date);
    setSelectedDate(date);
    setSelectedCell(null);
    setSelectedSchedules(schedules);
    setModalMode('detail');
    setModalOpen(true);
  };

  // 教师列表/默认值请求序号：applyClassTeacherDefaults 与 openEditSchedule 可能并发，
  // 过期响应不得覆盖共享 classTeachers 状态或表单默认值
  const teacherReqSeq = useRef(0);

  const loadClassTeachers = async (classId: number): Promise<ClassTeacherOption[] | null> => {
    const seq = teacherReqSeq.current;
    setClassTeachersLoading(true);
    try {
      const teachers = await api.get<ClassTeacherOption[]>(
        `/class-schedules/classes/${classId}/teachers`,
      );
      if (seq === teacherReqSeq.current) {
        setClassTeachers(teachers);
        setClassTeachersLoading(false);
      }
      return teachers;
    } catch {
      // 失败：清空选项，避免展示上一个班级的教师列表并误导选择；
      // 返回 null 区分「加载失败」与「真无教师」，由调用方提示且不触碰表单
      if (seq === teacherReqSeq.current) {
        setClassTeachers([]);
        setClassTeachersLoading(false);
      }
      return null;
    }
  };

  // 每行一科目 -> 按老师合并（一个老师一行，subjects 为该班全部科目）
  const buildTeacherGroups = (teachers: ClassTeacherOption[]) => {
    const groups = new Map<
      number,
      { userId: number; name?: string; username?: string; roleType: string; subjects: string[] }
    >();
    for (const teacher of teachers) {
      const group = groups.get(teacher.userId) ?? {
        userId: teacher.userId,
        name: teacher.name,
        username: teacher.username,
        roleType: teacher.roleType,
        subjects: [],
      };
      // 同一用户可同时持多个角色行（唯一键含 role_type），优先归类为任课老师，
      // 避免班主任行排序在前时整个分组被误排除出 subjectTeachers
      if (teacher.roleType === 'subject_teacher') group.roleType = 'subject_teacher';
      if (teacher.subject) group.subjects.push(teacher.subject);
      groups.set(teacher.userId, group);
    }
    return [...groups.values()];
  };

  const applyClassTeacherDefaults = async (classId: number, subject?: string) => {
    teacherReqSeq.current += 1;
    const seq = teacherReqSeq.current;
    const teachers = await loadClassTeachers(classId);
    if (seq !== teacherReqSeq.current) return; // 期间发起了更新的请求，丢弃过期结果
    if (teachers === null) {
      message.error('教师列表加载失败，请重试');
      return;
    }
    const groups = buildTeacherGroups(teachers);
    const subjectTeachers = groups.filter((teacher) => teacher.roleType === 'subject_teacher');
    const matchedBySubject = subject
      ? subjectTeachers.filter((teacher) => teacher.subjects.includes(subject))
      : [];
    if (matchedBySubject.length === 1) {
      form.setFieldValue('teacherId', matchedBySubject[0].userId);
      return;
    }
    if (matchedBySubject.length > 1) {
      // 多人都教该科目：无法自动判定，清空老师让用户选择；科目本身有效，保留
      form.setFieldValue('teacherId', undefined);
      return;
    }
    // 无科目（新增/清空科目）：仅当存在唯一任课老师时自动带出老师与首个科目
    if (!subject && subjectTeachers.length === 1) {
      form.setFieldValue('teacherId', subjectTeachers[0].userId);
      if (subjectTeachers[0].subjects.length > 0) {
        form.setFieldValue('subject', subjectTeachers[0].subjects[0]);
      }
      return;
    }
    // 显式科目无人教授（含切班残留的旧科目）：清空老师与科目，避免错配提交
    form.setFieldValue('teacherId', undefined);
    if (subject) form.setFieldValue('subject', undefined);
  };

  // 选择任课老师行（key = userId__科目）后带出对应科目；科目为空（非任课老师）只记录老师
  const handleTeacherChange = (key?: string) => {
    if (!key) {
      form.setFieldValue('teacherId', undefined);
      form.setFieldValue('subject', undefined);
      return;
    }
    const [userIdRaw, ...subjectParts] = key.split('__');
    const userId = Number(userIdRaw);
    if (!Number.isInteger(userId)) return;
    form.setFieldValue('teacherId', userId);
    const subject = subjectParts.join('__');
    // 非任课老师行（key 无科目部分）必须清空科目，避免残留旧科目与错误老师配对提交
    form.setFieldValue('subject', subject || undefined);
  };

  const handleSubmit = async () => {
    // 周视图走 selectedCell；月视图走 selectedDate（此时 selectedCell 为 null）
    if (modalMode === 'create' && !selectedCell && !selectedDate) return;
    if (modalMode === 'edit' && !editingSchedule) return;
    try {
      const values = (await form.validateFields()) as ScheduleFormValues;
      setSubmitting(true);
      const payload = buildSchedulePayload(values);
      await saveMutation.mutateAsync(payload);
      message.success(
        modalMode === 'edit' ? '排课更新成功，请重新同步到钉钉排班' : '排课创建成功',
      );
      if (modalMode === 'create') setSyncHint(true);
      setModalOpen(false);
      setEditingSchedule(null);
    } catch {
      // 错误提示由 useApiMutation 统一处理
    } finally {
      setSubmitting(false);
    }
  };

  const openEditSchedule = (schedule: ClassScheduleItem) => {
    if (isMaskedSchedule(schedule) || schedule.id === null || schedule.classId === null) return;
    if (schedule.scheduleType === 'RENTAL') {
      message.warning('租赁排课请在租赁订单中修改');
      return;
    }
    setEditingSchedule(schedule);
    setModalMode('edit');
    form.setFieldsValue(
      scheduleToFormValues({
        ...schedule,
        id: schedule.id ?? undefined,
        classId: schedule.classId ?? undefined,
      }),
    );
    // 排课科目可能不在该老师当前配置的科目中（科目已调整）或老师为非任课老师行：
    // 等老师选项加载完后再校验/同步表单值，保证下拉与表单一致。
    // 闭包内 TS 无法保留属性收窄，先捕获收窄后的值
    const classId = schedule.classId;
    const teacherId = schedule.teacherId;
    const resolveTeacherKey = async () => {
      teacherReqSeq.current += 1;
      const seq = teacherReqSeq.current;
      const teachers = await loadClassTeachers(classId);
      if (seq !== teacherReqSeq.current) return; // 期间打开了其他排课/发起了新请求，丢弃
      if (teachers === null) {
        // 加载失败：下拉选项已清空（loadClassTeachers 失败时置空），表单保留排课原值，用户可重试
        message.error('教师列表加载失败，请重试');
        return;
      }
      if (teacherId === null || teacherId === undefined) {
        form.setFieldValue('teacherId', undefined);
        return;
      }
      const exact = `${teacherId}__${schedule.subject ?? ''}`;
      if (teachers.some((t) => `${t.userId}__${t.subject ?? ''}` === exact)) {
        return; // 表单值本就与选项一致，无需改动
      }
      // 无精确匹配：优先取该老师的任课老师行（getClassTeachers 按 roleType 排序，
      // 非科目行在前，直接 find 首个会匹配到 subject 为空的班主任/生活老师行并误清科目），
      // 科目同步为该老师当前科目，避免下拉与表单不一致；无任何行时清空老师与科目
      const fallback =
        teachers.find((t) => t.userId === teacherId && t.roleType === 'subject_teacher') ??
        teachers.find((t) => t.userId === teacherId);
      if (fallback) {
        form.setFieldValue('subject', fallback.subject ?? undefined);
      } else {
        form.setFieldValue('teacherId', undefined);
        form.setFieldValue('subject', undefined);
      }
    };
    void resolveTeacherKey();
  };

  const removeScheduleFromSelection = (id: number) => {
    const remaining = selectedSchedules.filter((s) => s.id !== id);
    setSelectedSchedules(remaining);
    if (remaining.length === 0) {
      setModalOpen(false);
    }
  };

  const handleDisable = async (id: number | null) => {
    if (id === null) return;
    try {
      await disableMutation.mutateAsync(id);
      message.success('排课已停用，历史考勤记录已保留，教室占用已释放');
      removeScheduleFromSelection(id);
    } catch {
      // 错误提示由 useApiMutation 统一处理
    }
  };

  const classroomOptions = useMemo(
    () =>
      classrooms.map((c) => ({
        value: c.id,
        label: `${c.building ? c.building + ' · ' : ''}${c.name}`,
      })),
    [classrooms],
  );

  const classOptions = useMemo(
    () =>
      classes.map((c) => ({
        value: c.id,
        label: `${c.name}${c.code ? ` (${c.code})` : ''}`,
      })),
    [classes],
  );

  const selectedClassroom = selectedCell
    ? classrooms.find((c) => c.id === selectedCell.classroomId)
    : undefined;

  return (
    <div>
      {syncHint && (
        <NextStepHint
          title="排课已创建"
          description="同步到钉钉后，教师端与考勤设备才能看到新排课。"
          action={{ label: '同步钉钉', onClick: () => void handleSyncSchedule() }}
          onClose={() => setSyncHint(false)}
        />
      )}
      <div
        style={{
          marginBottom: 16,
          display: 'flex',
          justifyContent: 'space-between',
          alignItems: 'center',
          flexWrap: 'wrap',
          gap: 8,
        }}
      >
        <Space>
          <CalendarOutlined style={{ fontSize: 20 }} />
          <h3 style={{ margin: 0 }}>排课管理</h3>
        </Space>
        <Space>
          <Segmented
            value={viewMode}
            onChange={(v) => {
              setViewMode(v as 'week' | 'month');
              setSelectedDate(null);
            }}
            options={[
              { label: '周视图', value: 'week' },
              { label: '月视图', value: 'month' },
            ]}
          />
          <PermissionButton
            permission="sync:trigger"
            type="primary"
            icon={<CloudSyncOutlined />}
            onClick={() => void openSyncModal()}
          >
            同步到钉钉排班
          </PermissionButton>
          {viewMode === 'week' ? (
            <>
              <Button icon={<LeftOutlined />} onClick={() => setViewDate(viewDate.subtract(7, 'day'))}>
                上一周
              </Button>
              <span style={{ fontWeight: 500, fontSize: 15 }}>
                {weekYear} W{weekNum}
                <span style={{ color: '#8c8c8c', fontWeight: 400, fontSize: 13, marginLeft: 6 }}>
                  ({startDateStr} ~ {endDateStr})
                </span>
              </span>
              <Button icon={<RightOutlined />} onClick={() => setViewDate(viewDate.add(7, 'day'))}>
                下一周
              </Button>
            </>
          ) : (
            <>
              <Button icon={<LeftOutlined />} onClick={() => setViewDate(viewDate.subtract(1, 'month'))}>
                上一月
              </Button>
              <span style={{ fontWeight: 500, fontSize: 15 }}>{monthStart.format('YYYY年 M月')}</span>
              <Button icon={<RightOutlined />} onClick={() => setViewDate(viewDate.add(1, 'month'))}>
                下一月
              </Button>
            </>
          )}
        </Space>
      </div>

      <Card size="small" style={{ marginBottom: 16 }}>
        <Space wrap>
          <Select
            mode="multiple"
            placeholder="筛选教室"
            allowClear
            style={{ minWidth: 200 }}
            value={filterClassroomIds}
            onChange={(v) => setFilterClassroomIds(v)}
            options={classroomOptions}
            maxTagCount={3}
          />
          <Select
            placeholder="筛选班级"
            allowClear
            style={{ minWidth: 160 }}
            value={filterClassId}
            onChange={(v) => setFilterClassId(v)}
            options={classOptions}
          />
        </Space>
      </Card>

      {isError ? (
        <QueryErrorState
          title="排课数据加载失败"
          description="请检查网络后重试。"
          onRetry={() => void refetch()}
        />
      ) : (
        <ScheduleGrid
          loading={loading}
          viewMode={viewMode}
          classrooms={classrooms}
          filteredClassrooms={filteredClassrooms}
          displayMatrix={displayMatrix}
          weeks={weeks}
          monthStart={monthStart}
          monthScheduleMap={monthScheduleMap}
          onCellClick={handleCellClick}
          onDateClick={handleDateClick}
        />
      )}

      <ScheduleModal
        open={modalOpen}
        mode={modalMode}
        submitting={submitting}
        form={form}
        selectedCell={selectedCell}
        selectedDate={selectedDate}
        selectedSchedules={selectedSchedules}
        editingSchedule={editingSchedule}
        selectedClassroom={selectedClassroom}
        classOptions={classOptions}
        classroomOptions={classroomOptions}
        classTeachers={classTeachers}
        classes={classes}
        onCancel={() => {
          setModalOpen(false);
          setEditingSchedule(null);
        }}
        onSubmit={handleSubmit}
        onStartCreate={() => {
          setEditingSchedule(null);
          setModalMode('create');
          teacherReqSeq.current += 1; // 作废在途教师请求，防止过期响应污染新建表单
          form.resetFields();
          form.setFieldsValue({
            classroomId: selectedCell?.classroomId,
            weekDay:
              selectedCell?.weekDay ?? (selectedDate ? selectedDate.day() || 7 : undefined),
            dateRange: selectedDate ? [selectedDate, selectedDate] : undefined,
            attendanceAdvanceMinutes: 30,
          });
        }}
        onEdit={openEditSchedule}
        onDisable={handleDisable}
        onClassChange={(classId) => {
          form.setFieldValue('teacherId', undefined);
          void applyClassTeacherDefaults(classId, form.getFieldValue('subject'));
        }}
        onSubjectBlur={(value) => {
          const classId = form.getFieldValue('classId');
          if (classId) void applyClassTeacherDefaults(classId, value);
        }}
        onTeacherChange={handleTeacherChange}
        classTeachersLoading={classTeachersLoading}
        weekMatrix={matrix}
      />

      <SyncModal
        open={syncModalOpen}
        syncing={syncing}
        syncStatus={syncStatus}
        syncStatusError={syncStatusError}
        syncResult={syncResult}
        syncDateFrom={syncDateFrom}
        syncDays={syncDays}
        attendanceMachineOnly={attendanceMachineOnly}
        onClose={() => {
          setSyncModalOpen(false);
          setSyncResult(null);
        }}
        onRetryStatus={() => void openSyncModal()}
        onSync={handleSyncSchedule}
        onDateChange={setSyncDateFrom}
        onDaysChange={setSyncDays}
        onMachineOnlyChange={setAttendanceMachineOnly}
      />
    </div>
  );
};

export default SchedulesPage;
