// aislop-ignore-file: duplicate-block -- 表格/表单声明结构相似且参数不同，渲染逻辑已共享组件化
import React, { useMemo, 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 { 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 [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 [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);
      } else {
        message.success(classification.message);
      }
    } 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');
      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);
  };

  const loadClassTeachers = async (classId: number) => {
    try {
      const teachers = await api.get<ClassTeacherOption[]>(
        `/class-schedules/classes/${classId}/teachers`,
      );
      setClassTeachers(teachers);
      return teachers;
    } catch {
      setClassTeachers([]);
      return [];
    }
  };

  const applyClassTeacherDefaults = async (classId: number, subject?: string) => {
    const teachers = await loadClassTeachers(classId);
    const subjectTeachers = teachers.filter((teacher) => teacher.roleType === 'subject_teacher');
    const matchedBySubject = subject
      ? subjectTeachers.filter((teacher) => teacher.subject && teacher.subject === subject)
      : [];
    const matched = matchedBySubject.length > 0 ? matchedBySubject : subjectTeachers;
    if (matched.length === 1) {
      form.setFieldValue('teacherId', matched[0].userId);
      if (!subject && matched[0].subject) form.setFieldValue('subject', matched[0].subject);
    } else {
      form.setFieldValue('teacherId', undefined);
    }
  };

  const handleSubmit = async () => {
    if (modalMode === 'create' && !selectedCell) 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' ? '排课更新成功，请重新同步到钉钉排班' : '排课创建成功',
      );
      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,
      }),
    );
    void loadClassTeachers(schedule.classId);
  };

  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>
      <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');
          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);
        }}
      />

      <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;
