import React, { useCallback, useMemo, useState } from 'react';
import { useQuery, useQueryClient } from '@tanstack/react-query';
import { useApiMutation } from '../../hooks/useApiMutation';
import { validateResponse } from '../../utils/validate';
import {
  attendanceAlertsSchema,
  attendanceClassOptionsSchema,
  attendanceRecordsResponseSchema,
  attendanceScheduleOptionsSchema,
  attendancePeriodsSchema,
  attendanceSummarySchema,
  dingTalkSyncStatusSchema,
} from '../../api/schemas';
import { Form, Grid } from 'antd';
import dayjs, { type Dayjs } from 'dayjs';
import api from '../../api';
import { message } from '../../ui/app-message';
import { useUserStore } from '../../store/user/userStore';
import type { AttendanceSummary } from './attendance-workspace';
import { getErrorMessage } from '../../utils/error';
import { AttendanceAdminHeader } from './AttendanceAdminHeader';
import { buildAttendanceAdminColumns } from './AttendanceAdminColumns';
import { PeriodConfigModal, StudentDetailDrawer } from './AttendanceAdminModals';
import { AttendanceAdminWorkspace } from './AttendanceAdminWorkspace';
import {
  DEFAULT_ATTENDANCE_PERIODS,
  EMPTY_SUMMARY,
  buildAdminStudentPanels,
  displayAttendanceStatus,
  formatTeacherNames,
  getTeacherDisplayName,
  type AdminStudentPanel,
  type AlertItem,
  type AttendancePeriodConfigItem,
  type AttendanceRecordItem,
  type ClassOption,
  type DingTalkSyncStatus,
  type HistoryScheduleOption,
} from './AttendanceAdmin.helpers';

export const AdminAttendanceArchive: React.FC<{ canEdit: boolean }> = ({ canEdit }) => {
  const screens = Grid.useBreakpoint();
  const isMobile = !screens.sm;
  const [periodModalOpen, setPeriodModalOpen] = useState(false);
  const [periodForm] = Form.useForm<{ periods: AttendancePeriodConfigItem[] }>();
  const [refreshingDingTalk, setRefreshingDingTalk] = useState(false);
  const [page, setPage] = useState(1);
  const [pageSize, setPageSize] = useState(48);
  const [classId, setClassId] = useState<number>();
  const [attendanceDate, setAttendanceDate] = useState<Dayjs | null>(dayjs());
  const [status, setStatus] = useState<string>();
  const [session, setSession] = useState<string>();
  const [scheduleId, setScheduleId] = useState<number>();
  const [metricFilter, setMetricFilter] = useState('all');
  const [studentSearch, setStudentSearch] = useState('');
  const [selectedStudent, setSelectedStudent] = useState<AdminStudentPanel | null>(null);
  const [correctingRecordId, setCorrectingRecordId] = useState<number | null>(null);
  const queryClient = useQueryClient();
  const recordQueryKey = [
    'attendance',
    'records',
    page,
    pageSize,
    classId,
    attendanceDate,
    status,
    session,
    scheduleId,
  ] as const;

  const { data: classOptions = [] } = useQuery<ClassOption[]>({
    queryKey: ['attendance', 'meta', 'classes'],
    queryFn: async () => {
      try {
        return validateResponse<ClassOption[]>(
          attendanceClassOptionsSchema,
          await api.get<ClassOption[]>('/attendance-records/classes'),
        );
      } catch {
        return [];
      }
    },
  });
  const { data: alerts = [] } = useQuery<AlertItem[]>({
    queryKey: ['attendance', 'meta', 'alerts'],
    queryFn: async () => {
      try {
        return validateResponse<AlertItem[]>(
          attendanceAlertsSchema,
          await api.get<AlertItem[]>('/attendance-records/alerts'),
        );
      } catch {
        return [];
      }
    },
  });
  const { data: periods = DEFAULT_ATTENDANCE_PERIODS } = useQuery<AttendancePeriodConfigItem[]>({
    queryKey: ['attendance', 'meta', 'periods'],
    queryFn: async () => {
      try {
        return validateResponse<AttendancePeriodConfigItem[]>(
          attendancePeriodsSchema,
          await api.get<AttendancePeriodConfigItem[]>('/attendance-period-configs'),
        );
      } catch {
        return DEFAULT_ATTENDANCE_PERIODS;
      }
    },
  });
  const {
    data: scheduleOptions = [],
    isFetching: scheduleOptionsFetching,
  } = useQuery<HistoryScheduleOption[]>({
    queryKey: ['attendance', 'schedules', classId, attendanceDate],
    enabled: !!classId && !!attendanceDate,
    queryFn: async () => {
      try {
        if (!attendanceDate) return [];
        return validateResponse<HistoryScheduleOption[]>(
          attendanceScheduleOptionsSchema,
          await api.get<HistoryScheduleOption[]>('/attendance-records/schedules', {
            params: { classId, date: attendanceDate.format('YYYY-MM-DD') },
          }),
        );
      } catch (error: unknown) {
        message.error(getErrorMessage(error, '加载班级科目失败'));
        return [];
      }
    },
  });
  const scheduleOptionsLoading = scheduleOptionsFetching;
  const effectiveScheduleId =
    scheduleId && scheduleOptions.some((schedule) => schedule.id === scheduleId)
      ? scheduleId
      : undefined;

  const enabledPeriods = useMemo(
    () => periods.filter((period) => period.enabled).sort((a, b) => a.sortOrder - b.sortOrder),
    [periods],
  );
  const sessionOptions = useMemo(
    () => enabledPeriods.map((period) => ({ value: period.periodKey, label: period.label })),
    [enabledPeriods],
  );
  const sessionMap = useMemo(
    () => Object.fromEntries(periods.map((period) => [period.periodKey, period.label])),
    [periods],
  );
  const periodOrder = useMemo(
    () => new Map(periods.map((period, index) => [period.periodKey, index])),
    [periods],
  );
  const sortAttendanceRecords = useCallback(
    (items: readonly AttendanceRecordItem[]) =>
      [...items].sort((a, b) => {
        const periodDiff =
          (periodOrder.get(a.session) ?? Number.MAX_SAFE_INTEGER) -
          (periodOrder.get(b.session) ?? Number.MAX_SAFE_INTEGER);
        if (periodDiff !== 0) return periodDiff;
        return (a.punchTime || a.createdAt || '').localeCompare(b.punchTime || b.createdAt || '');
      }),
    [periodOrder],
  );
  const openPeriodConfig = () => {
    periodForm.setFieldsValue({ periods });
    setPeriodModalOpen(true);
  };

  const savePeriodConfig = async () => {
    try {
      const values = await periodForm.validateFields();
      const payload = {
        periods: values.periods.map((period, index) => ({
          ...period,
          sortOrder: index + 1,
          enabled: period.enabled ?? true,
        })),
      };
      const data = await savePeriodConfigMutation.mutateAsync(payload);
      setPeriodModalOpen(false);
      message.success('考勤时段配置已保存');
      if (session && !data.some((period) => period.enabled && period.periodKey === session)) {
        setSession(undefined);
      }
    } catch {
      // 校验错误静默，接口错误由 useApiMutation 统一提示
    }
  };

  const resetPeriodConfig = async () => {
    try {
      await resetPeriodConfigMutation.mutateAsync();
      message.success('已恢复默认考勤时段');
    } catch {
      // 错误提示由 useApiMutation 统一处理
    }
  };

  const buildParams = useCallback(
    (withPagination = true) => {
      const params: Record<string, string | number> = {};
      if (withPagination) Object.assign(params, { page, pageSize });
      if (classId) params.classId = classId;
      if (attendanceDate) {
        const selectedDate = attendanceDate.format('YYYY-MM-DD');
        params.dateFrom = selectedDate;
        params.dateTo = selectedDate;
      }
      if (status) params.status = status;
      if (session) params.session = session;
      if (effectiveScheduleId) params.scheduleId = effectiveScheduleId;
      return params;
    },
    [page, pageSize, classId, attendanceDate, status, session, effectiveScheduleId],
  );

  const { data: syncStatus = null, refetch: refetchSyncStatus } = useQuery<DingTalkSyncStatus | null>({
    queryKey: ['attendance', 'sync-status'],
    queryFn: async () => {
      try {
        return validateResponse<DingTalkSyncStatus>(
          dingTalkSyncStatusSchema,
          await api.get<DingTalkSyncStatus>('/attendance-records/dingtalk-sync-status'),
        );
      } catch {
        return null;
      }
    },
  });
  const loadSyncStatus = useCallback(() => refetchSyncStatus(), [refetchSyncStatus]);

  const {
    data: recordQuery = { records: [], total: 0, summary: EMPTY_SUMMARY },
    isFetching: recordsFetching,
    refetch: refetchRecords,
  } = useQuery<{
    records: AttendanceRecordItem[];
    total: number;
    summary: AttendanceSummary;
  }>({
    queryKey: recordQueryKey,
    queryFn: async () => {
      try {
        const [recordData, summaryData] = await Promise.all([
          api.get<{ list: AttendanceRecordItem[]; total: number }>('/attendance-records', {
            params: buildParams(true),
          }),
          api.get<AttendanceSummary>('/attendance-records/summary', {
            params: buildParams(false),
          }),
        ]);
        const validatedRecords = validateResponse<{
          list: AttendanceRecordItem[];
          total: number;
        }>(attendanceRecordsResponseSchema, recordData);
        const validatedSummary = validateResponse<AttendanceSummary>(
          attendanceSummarySchema,
          summaryData,
        );
        return {
          records: validatedRecords.list,
          total: validatedRecords.total,
          summary: { ...EMPTY_SUMMARY, ...validatedSummary },
        };
      } catch (error: unknown) {
        message.error(getErrorMessage(error, '加载学生考勤失败'));
        return { records: [], total: 0, summary: EMPTY_SUMMARY };
      }
    },
  });
  const records = recordQuery.records;
  const total = recordQuery.total;
  const summary = recordQuery.summary;
  const loading = recordsFetching;
  const loadRecords = useCallback(() => refetchRecords(), [refetchRecords]);

  const savePeriodConfigMutation = useApiMutation(
    async (payload: Record<string, unknown>) =>
      api.put<AttendancePeriodConfigItem[]>('/attendance-period-configs', payload),
    { invalidate: [['attendance', 'meta', 'periods']] },
  );
  const resetPeriodConfigMutation = useApiMutation(
    async () => api.post<AttendancePeriodConfigItem[]>('/attendance-period-configs/reset'),
    { invalidate: [['attendance', 'meta', 'periods']] },
  );
  const refreshDingTalkMutation = useApiMutation(
    async (payload: Record<string, unknown>) =>
      api.post<{
        refreshed: number;
        imported: number;
        matched: number;
        errors: string[];
      }>('/attendance-records/refresh-dingtalk', payload),
    { invalidate: [['attendance', 'records'], ['attendance', 'sync-status']] },
  );
  const updateStatusMutation = useApiMutation(
    async ({ id, status }: { id: number; status: string }) =>
      api.put(`/attendance-records/${id}`, { status }),
    { invalidate: [['attendance', 'records']] },
  );
  const saveRecordCellMutation = useApiMutation(
    async ({ id, field, value }: { id: number; field: string; value: unknown }) =>
      api.put(`/attendance-records/${id}`, { [field]: value }),
    { invalidate: [['attendance', 'records']] },
  );

  const refreshDingTalkAttendance = useCallback(async () => {
    if (!attendanceDate) {
      message.warning('请先选择日期');
      return;
    }
    if (attendanceDate.isAfter(dayjs(), 'day')) {
      message.warning('不能查看或刷新未来日期的考勤');
      return;
    }
    setRefreshingDingTalk(true);
    try {
      const result = await refreshDingTalkMutation.mutateAsync({
        date: attendanceDate.format('YYYY-MM-DD'),
        classId,
        session,
      });
      if (result.errors.length > 0) {
        message.warning(result.errors[0]);
      } else {
        message.success(`已刷新 ${result.refreshed} 节课程考勤`);
      }
      await Promise.all([loadRecords(), loadSyncStatus()]);
    } catch {
      // 错误提示由 useApiMutation 统一处理
    } finally {
      setRefreshingDingTalk(false);
    }
  }, [attendanceDate, classId, loadRecords, loadSyncStatus, session, refreshDingTalkMutation]);

  const resetFilters = () => {
    setClassId(undefined);
    setAttendanceDate(dayjs());
    setStatus(undefined);
    setSession(undefined);
    setScheduleId(undefined);
    setMetricFilter('all');
    setStudentSearch('');
    setPage(1);
  };

  const handleExport = useCallback(() => {
    const params = new URLSearchParams();
    for (const [key, value] of Object.entries(buildParams(false))) params.set(key, String(value));
    const token = useUserStore.getState().token;
    fetch(`/api/attendance-records/export?${params.toString()}`, {
      headers: { Authorization: `Bearer ${token}` },
    })
      .then((response) => {
        if (!response.ok) throw new Error('导出失败');
        return response.blob();
      })
      .then((blob) => {
        const url = URL.createObjectURL(blob);
        const anchor = document.createElement('a');
        anchor.href = url;
        anchor.download = `学生考勤-${dayjs().format('YYYYMMDD')}.xlsx`;
        anchor.click();
        URL.revokeObjectURL(url);
      })
      .catch(() => message.error('导出失败'));
  }, [buildParams]);

  const patchRecordStatus = (recordId: number, nextStatus: string) => {
    queryClient.setQueryData<{
      records: AttendanceRecordItem[];
      total: number;
      summary: AttendanceSummary;
    }>(recordQueryKey, (prev) =>
      prev
        ? {
            ...prev,
            records: prev.records.map((item) =>
              item.id === recordId ? { ...item, status: nextStatus } : item,
            ),
          }
        : prev,
    );
    setSelectedStudent((student) => {
      if (!student) return student;
      return {
        ...student,
        records: student.records.map((item) =>
          item.id === recordId ? { ...item, status: nextStatus } : item,
        ),
        statusBySession: Object.fromEntries(
          Object.entries(student.statusBySession).map(([key, item]) => [
            key,
            item?.id === recordId ? { ...item, status: nextStatus } : item,
          ]),
        ),
      };
    });
  };

  const updateAdminRecordStatus = async (record: AttendanceRecordItem, nextStatus: string) => {
    const previousStatus = record.status;
    const normalizedPrevious = displayAttendanceStatus(previousStatus);
    if (normalizedPrevious === nextStatus && previousStatus !== 'pending') return;

    patchRecordStatus(record.id, nextStatus);
    setCorrectingRecordId(record.id);
    try {
      await updateStatusMutation.mutateAsync({ id: record.id, status: nextStatus });
      message.success('考勤结果已更新');
    } catch {
      patchRecordStatus(record.id, previousStatus);
    } finally {
      setCorrectingRecordId(null);
    }
  };

  const saveAdminRecordCell = async (
    record: AttendanceRecordItem,
    field: 'status' | 'remark',
    value: unknown,
  ) => {
    try {
      await saveRecordCellMutation.mutateAsync({ id: record.id, field, value });
      message.success('考勤记录已保存');
    } catch {
      // 错误提示由 useApiMutation 统一处理
    }
  };

  const studentPanels = useMemo(() => buildAdminStudentPanels(records), [records]);
  const visibleStudents = useMemo(() => {
    const query = studentSearch.trim().toLocaleLowerCase('zh-CN');
    return studentPanels.filter((student) => {
      const matchesQuery =
        !query ||
        student.studentName.toLocaleLowerCase('zh-CN').includes(query) ||
        student.studentNo.toLocaleLowerCase('zh-CN').includes(query);
      const matchesMetric =
        metricFilter === 'all' ||
        student.records.some((record) => displayAttendanceStatus(record.status) === metricFilter);
      return matchesQuery && matchesMetric;
    });
  }, [metricFilter, studentPanels, studentSearch]);

  const selectedClassOption = classId
    ? classOptions.find((item) => item.classId === classId)
    : undefined;
  const selectedClass =
    selectedClassOption?.className || (classId ? `班级 ${classId}` : '全部班级');
  const overviewTeachers = selectedClassOption?.teachers ?? [];
  const headTeacherNames = classId
    ? formatTeacherNames(overviewTeachers.filter((teacher) => teacher.roleType === 'head_teacher'))
    : '请选择班级';
  const lifeTeacherNames = classId
    ? formatTeacherNames(overviewTeachers.filter((teacher) => teacher.roleType === 'life_teacher'))
    : '请选择班级';
  const currentSchedule = scheduleId
    ? scheduleOptions.find((item) => item.id === scheduleId)
    : undefined;
  const subjectTeacherNames = currentSchedule
    ? getTeacherDisplayName({
        name: currentSchedule.teacherName,
        username: currentSchedule.teacherUsername,
      })
    : classId
      ? formatTeacherNames(
          overviewTeachers.filter((teacher) => teacher.roleType === 'subject_teacher'),
        )
      : '请选择班级';
  const attendanceRate =
    summary.total > 0 ? Math.round((summary.present / summary.total) * 100) : 0;
  const dateLabel = attendanceDate?.format('YYYY-MM-DD') || '未选择日期';

  const columns = buildAttendanceAdminColumns({
    isMobile,
    canEdit,
    sessionMap,
    correctingRecordId,
    onSaveAdminRecordCell: saveAdminRecordCell,
    onUpdateAdminRecordStatus: updateAdminRecordStatus,
  });

  return (
    <div className="attendance-page admin-attendance student-attendance-center">
      <AttendanceAdminHeader
        syncStatus={syncStatus}
        canEdit={canEdit}
        refreshingDingTalk={refreshingDingTalk}
        onOpenPeriodConfig={openPeriodConfig}
        onRefreshDingTalk={() => void refreshDingTalkAttendance()}
        onExport={handleExport}
        attendanceDate={attendanceDate}
        onDateChange={(value) => {
          setAttendanceDate(value);
          setScheduleId(undefined);
          setPage(1);
        }}
        classId={classId}
        onClassChange={(value) => {
          setClassId(value);
          setScheduleId(undefined);
          setPage(1);
        }}
        classOptions={classOptions}
        effectiveScheduleId={effectiveScheduleId}
        onScheduleChange={(value) => {
          setScheduleId(value);
          setPage(1);
        }}
        scheduleOptions={scheduleOptions}
        scheduleOptionsLoading={scheduleOptionsLoading}
        session={session}
        onSessionChange={(value) => {
          setSession(value);
          setPage(1);
        }}
        sessionOptions={sessionOptions}
        onReset={resetFilters}
        onQuery={() => void loadRecords()}
        selectedClass={selectedClass}
        dateLabel={dateLabel}
        visibleStudentCount={visibleStudents.length}
        total={total}
        headTeacherNames={headTeacherNames}
        lifeTeacherNames={lifeTeacherNames}
        subjectTeacherNames={subjectTeacherNames}
        attendanceRate={attendanceRate}
        summary={summary}
        metricFilter={metricFilter}
        onMetricFilterChange={setMetricFilter}
        alerts={alerts}
      />

      <AttendanceAdminWorkspace
        metricFilter={metricFilter}
        studentSearch={studentSearch}
        onSearchChange={setStudentSearch}
        onExport={handleExport}
        visibleStudents={visibleStudents}
        loading={loading}
        selectedStudentId={selectedStudent?.studentId}
        onSelectStudent={setSelectedStudent}
        sortAttendanceRecords={sortAttendanceRecords}
        sessionMap={sessionMap}
        records={records}
        columns={columns}
        page={page}
        pageSize={pageSize}
        total={total}
        onPageChange={(nextPage, nextPageSize) => {
          setPage(nextPage);
          setPageSize(nextPageSize);
        }}
      />

      <StudentDetailDrawer
        student={selectedStudent}
        isMobile={isMobile}
        canEdit={canEdit}
        sessionMap={sessionMap}
        correctingRecordId={correctingRecordId}
        sortAttendanceRecords={sortAttendanceRecords}
        onClose={() => setSelectedStudent(null)}
        onUpdateAdminRecordStatus={updateAdminRecordStatus}
      />

      <PeriodConfigModal
        open={periodModalOpen}
        form={periodForm}
        onOk={savePeriodConfig}
        onCancel={() => setPeriodModalOpen(false)}
        onReset={() => void resetPeriodConfig()}
      />
    </div>
  );
};
