import React, { useMemo, useState } from 'react';
import { useDebounceValue } from 'usehooks-ts';
import {
  App,
  Button,
  Card,
  Checkbox,
  Col,
  Form,
  Input,
  Popconfirm,
  Progress,
  Row,
  Select,
  Space,
  Switch,
  Tag,
} from 'antd';
import {
  CalendarOutlined,
  DeleteOutlined,
  InboxOutlined,
  PlusOutlined,
  SearchOutlined,
  TeamOutlined,
} from '@ant-design/icons';
import dayjs from 'dayjs';
import { useNavigate } from 'react-router';
import api from '../../api';
import { message } from '../../ui/app-message';
import ExamFormModal from './ExamFormModal';
import { selectAllExamIds, toggleExamSelection } from './selection';
import { EXAM_TYPE_OPTIONS, type ClassOption, type ExamFormValues, type ExamItem } from './types';
import './style.css';
import { usePermission } from '../../hooks/usePermission';
import { useQuery } from '@tanstack/react-query';
import { useApiMutation } from '../../hooks/useApiMutation';
import { validateResponse } from '../../utils/validate';
import { classOptionsSchema, examsSchema } from '../../api/schemas';
import { getErrorMessage } from '../../utils/error';
import { QueryErrorState, QueryEmpty } from '../../components/QueryState';
import { NextStepHint } from '../../components/NextStepHint';
import { useVisibleRefetch } from '../../hooks/usePageVisible';
import { useDirtyGuard } from '../../hooks/useDirtyGuard';

const ExamsPage: React.FC = () => {
  const { modal } = App.useApp();
  const navigate = useNavigate();
  const { hasPermission } = usePermission();
  const canPurgeExam = hasPermission('exam:purge');
  const [form] = Form.useForm<ExamFormValues>();
  const examFormGuard = useDirtyGuard(form);
  const [batchLoading, setBatchLoading] = useState(false);
  const [saving, setSaving] = useState(false);
  const [modalOpen, setModalOpen] = useState(false);
  const [keyword, setKeyword] = useState('');
  const [examType, setExamType] = useState<string>();
  const [classId, setClassId] = useState<number>();
  const [showArchived, setShowArchived] = useState(false);
  const [selectedExamIds, setSelectedExamIds] = useState<number[]>([]);
  // 考试创建成功后的「下一步：去详情录成绩」引导
  const [examCreatedId, setExamCreatedId] = useState<number | null>(null);
  const [debouncedFilters] = useDebounceValue(
    { keyword, examType, classId, showArchived },
    200,
  );

  const { data: classes = [] } = useQuery<ClassOption[]>({
    queryKey: ['exams', 'classes'],
    queryFn: async () => {
      try {
        return (
          validateResponse<ClassOption[]>(
            classOptionsSchema,
            await api.get<ClassOption[]>('/classes'),
          ) ?? []
        );
      } catch (error: unknown) {
        message.error(getErrorMessage(error, '加载班级失败'));
        return [];
      }
    },
  });

  const { data = [], isFetching, isError, refetch } = useQuery<ExamItem[]>({
    queryKey: [
      'exams',
      debouncedFilters.keyword,
      debouncedFilters.examType,
      debouncedFilters.classId,
      debouncedFilters.showArchived,
    ],
    queryFn: async () => {
      const params = new URLSearchParams();
      if (debouncedFilters.keyword.trim())
        params.set('keyword', debouncedFilters.keyword.trim());
      if (debouncedFilters.examType) params.set('examType', debouncedFilters.examType);
      if (debouncedFilters.classId) params.set('classId', String(debouncedFilters.classId));
      params.set('isArchived', String(debouncedFilters.showArchived));
      return (
        validateResponse<ExamItem[]>(
          examsSchema,
          await api.get<ExamItem[]>(`/exams?${params.toString()}`),
        ) ?? []
      );
    },
  });
  const loading = isFetching;
  // RouteKeeper 保活页面切回时刷新考试列表
  useVisibleRefetch(['exams']);

  const saveMutation = useApiMutation(
    async (payload: Record<string, unknown>) => api.post('/exams', payload),
    { invalidate: [['exams']] },
  );
  const archiveMutation = useApiMutation(
    async ({ id, archive }: { id: number; archive: boolean }) =>
      api.put(`/exams/${id}/${archive ? 'archive' : 'restore'}`),
    { invalidate: [['exams']] },
  );
  const purgeMutation = useApiMutation(
    async (id: number) => api.delete(`/exams/${id}/permanent`),
    { invalidate: [['exams']] },
  );
  const batchPurgeMutation = useApiMutation(
    async (ids: number[]) => api.post<{ deleted: number; skipped: number }>('/exams/batch-permanent-delete', { ids }),
    { invalidate: [['exams']] },
  );
  const batchArchiveMutation = useApiMutation(
    async (ids: number[]) => api.put<{ archived: number; skipped: number }>('/exams/batch-archive', { ids }),
    { invalidate: [['exams']] },
  );
  const batchRestoreMutation = useApiMutation(
    async (ids: number[]) => api.put<{ restored: number; skipped: number }>('/exams/batch-restore', { ids }),
    { invalidate: [['exams']] },
  );

  const updateKeyword = (value: string) => {
    setKeyword(value);
    setSelectedExamIds([]);
  };
  const updateExamType = (value: string | undefined) => {
    setExamType(value);
    setSelectedExamIds([]);
  };
  const updateClassId = (value: number | undefined) => {
    setClassId(value);
    setSelectedExamIds([]);
  };

  const classOptions = useMemo(
    () => classes.map((item) => ({ value: item.id, label: item.name })),
    [classes],
  );

  const openCreate = () => {
    form.resetFields();
    form.setFieldValue('examDate', dayjs());
    examFormGuard.snapshot();
    setModalOpen(true);
  };

  const submit = async () => {
    try {
      const values = await form.validateFields();
      setSaving(true);
      const payload = { ...values, examDate: values.examDate.format('YYYY-MM-DD') };
      const created = (await saveMutation.mutateAsync(payload)) as { id?: number };
      message.success('考试已创建');
      setModalOpen(false);
      if (created?.id != null) setExamCreatedId(created.id);
    } catch {
      // 校验错误静默，接口错误由 useApiMutation 统一提示
    } finally {
      setSaving(false);
    }
  };

  const changeArchiveStatus = async (exam: ExamItem, archive: boolean) => {
    try {
      await archiveMutation.mutateAsync({ id: exam.id, archive });
      message.success(archive ? '考试已归档' : '考试已恢复');
    } catch {
      // 错误提示由 useApiMutation 统一处理
    }
  };

  const handlePurge = (exam: ExamItem) => {
    modal.confirm({
      title: `永久删除考试「${exam.examName}」？`,
      content: '删除后不可恢复，该考试及其成绩记录将被物理删除。确定继续？',
      okText: '永久删除',
      okButtonProps: { danger: true },
      cancelText: '取消',
      onOk: async () => {
        try {
          await purgeMutation.mutateAsync(exam.id);
          message.success('已永久删除（不可恢复）');
        } catch {
          // 错误提示由 useApiMutation 统一处理
        }
      },
    });
  };

  const batchPurge = async () => {
    if (selectedExamIds.length === 0 || batchLoading) return;
    setBatchLoading(true);
    try {
      const result = await batchPurgeMutation.mutateAsync(selectedExamIds);
      message.success(
        `已永久删除 ${result.deleted} 场考试${result.skipped ? `，跳过 ${result.skipped} 场` : ''}`,
      );
      setSelectedExamIds([]);
    } catch {
      // 错误提示由 useApiMutation 统一处理
    } finally {
      setBatchLoading(false);
    }
  };

  const allCurrentSelected = data.length > 0 && selectedExamIds.length === data.length;
  const partiallySelected = selectedExamIds.length > 0 && !allCurrentSelected;

  const toggleSelectAll = (checked: boolean) => {
    setSelectedExamIds(
      selectAllExamIds(
        data.map((exam) => exam.id),
        checked,
      ),
    );
  };

  const changeArchiveView = (checked: boolean) => {
    setSelectedExamIds([]);
    setShowArchived(checked);
  };

  const batchChangeArchiveStatus = async (archive: boolean) => {
    if (selectedExamIds.length === 0 || batchLoading) return;
    setBatchLoading(true);
    try {
      if (archive) {
        const result = await batchArchiveMutation.mutateAsync(selectedExamIds);
        message.success(
          `已归档 ${result.archived} 场考试${result.skipped ? `，跳过 ${result.skipped} 场` : ''}`,
        );
      } else {
        const result = await batchRestoreMutation.mutateAsync(selectedExamIds);
        message.success(
          `已恢复 ${result.restored} 场考试${result.skipped ? `，跳过 ${result.skipped} 场` : ''}`,
        );
      }
      setSelectedExamIds([]);
    } catch {
      // 错误提示由 useApiMutation 统一处理
    } finally {
      setBatchLoading(false);
    }
  };

  return (
    <div className="exam-page">
      {examCreatedId !== null && (
        <NextStepHint
          title="考试已创建"
          description="接下来可以在考试详情中添加学生名单、录入成绩。"
          action={{
            label: '去考试详情',
            onClick: () => {
              navigate(`/exams/${examCreatedId}`);
              setExamCreatedId(null);
            },
          }}
          onClose={() => setExamCreatedId(null)}
        />
      )}
      <div className="exam-toolbar">
        <Space wrap>
          <Input
            value={keyword}
            onChange={(event) => updateKeyword(event.target.value)}
            prefix={<SearchOutlined />}
            placeholder="搜索考试名称"
            allowClear
          />
          <Select
            value={examType}
            onChange={updateExamType}
            options={EXAM_TYPE_OPTIONS}
            placeholder="考试类型"
            allowClear
            style={{ width: 140 }}
          />
          <Select
            value={classId}
            onChange={updateClassId}
            options={classOptions}
            placeholder="考试班级"
            allowClear
            showSearch
            optionFilterProp="label"
            style={{ width: 180 }}
          />
        </Space>
        <Space wrap>
          <Checkbox
            checked={allCurrentSelected}
            indeterminate={partiallySelected}
            disabled={data.length === 0 || loading || batchLoading}
            onChange={(event) => toggleSelectAll(event.target.checked)}
          >
            全选当前结果
          </Checkbox>
          <Popconfirm
            title={showArchived ? '确认恢复选中的考试？' : '确认归档选中的考试？'}
            description={
              showArchived
                ? `将恢复选中的 ${selectedExamIds.length} 场考试。`
                : `将归档选中的 ${selectedExamIds.length} 场考试，归档后成绩将变为只读。`
            }
            disabled={selectedExamIds.length === 0 || batchLoading}
            onConfirm={() => void batchChangeArchiveStatus(!showArchived)}
          >
            <Button
              danger={!showArchived}
              loading={batchLoading}
              disabled={selectedExamIds.length === 0}
            >
              {showArchived ? '批量恢复' : '批量归档'}
            </Button>
          </Popconfirm>
          {showArchived && canPurgeExam ? (
            <Popconfirm
              title={`确认永久删除选中的 ${selectedExamIds.length} 场考试？`}
              description="删除后不可恢复，相关成绩将一并清除。"
              disabled={selectedExamIds.length === 0 || batchLoading}
              onConfirm={() => void batchPurge()}
              okText="永久删除"
              okButtonProps={{ danger: true }}
            >
              <Button
                danger
                icon={<DeleteOutlined />}
                loading={batchLoading}
                disabled={selectedExamIds.length === 0}
              >
                批量删除
              </Button>
            </Popconfirm>
          ) : null}
          <span className="exam-archive-toggle">
            <InboxOutlined />
            归档
            <Switch size="small" checked={showArchived} onChange={changeArchiveView} />
          </span>
          {!showArchived ? (
            <Button type="primary" icon={<PlusOutlined />} onClick={openCreate}>
              创建考试
            </Button>
          ) : null}
        </Space>
      </div>

      {isError ? (
        <QueryErrorState
          title="考试数据加载失败"
          description="请检查网络后重试。"
          onRetry={() => void refetch()}
        />
      ) : data.length === 0 && !loading ? (
        <div className="exam-empty">
          <QueryEmpty
            description="暂无考试"
            action={
              !showArchived
                ? { label: '创建考试', icon: <PlusOutlined />, onClick: openCreate }
                : undefined
            }
          />
        </div>
      ) : (
        <Row gutter={[16, 16]}>
          {data.map((exam) => {
            const percent =
              exam.totalStudents === 0
                ? 0
                : Math.round((exam.enteredScores / exam.totalStudents) * 100);
            return (
              <Col key={exam.id} xs={24} sm={12} xl={8} xxl={6}>
                <Card
                  className={`exam-card${selectedExamIds.includes(exam.id) ? ' exam-card-selected' : ''}`}
                  loading={loading}
                  title={
                    <Space>
                      <Checkbox
                        aria-label={`选择考试 ${exam.examName}`}
                        checked={selectedExamIds.includes(exam.id)}
                        disabled={batchLoading}
                        onChange={(event) => {
                          setSelectedExamIds((current) =>
                            toggleExamSelection(current, exam.id, event.target.checked),
                          );
                        }}
                      />
                      <Tag color="blue">{exam.examType}</Tag>
                      <span>{exam.examName}</span>
                    </Space>
                  }
                  extra={
                    <Tag color={exam.status === 'archived' ? 'default' : 'green'}>
                      {exam.status === 'archived' ? '已归档' : '成绩录入'}
                    </Tag>
                  }
                  actions={[
                    <span key="detail" onClick={() => navigate(`/exams/${exam.id}`)}>
                      查看成绩
                    </span>,
                    exam.status === 'archived' ? (
                      <>
                        <Popconfirm
                          key="restore"
                          title="确认恢复该考试？"
                          onConfirm={() => changeArchiveStatus(exam, false)}
                        >
                          <span>恢复</span>
                        </Popconfirm>
                        {canPurgeExam ? (
                          <Popconfirm
                            key="purge"
                            title="确认永久删除该考试？"
                            description="删除后不可恢复，成绩记录将一并清除。"
                            onConfirm={() => handlePurge(exam)}
                            okText="永久删除"
                            okButtonProps={{ danger: true }}
                          >
                            <span className="exam-purge-action">删除</span>
                          </Popconfirm>
                        ) : null}
                      </>
                    ) : (
                      <Popconfirm
                        key="archive"
                        title="确认归档该考试？"
                        description={`当前已录入 ${exam.enteredScores}/${exam.totalStudents} 人，归档后成绩将变为只读。`}
                        onConfirm={() => changeArchiveStatus(exam, true)}
                      >
                        <span>归档</span>
                      </Popconfirm>
                    ),
                  ]}
                >
                  <div className="exam-meta">
                    <span>科目</span>
                    <strong>{exam.subject}</strong>
                  </div>
                  <div className="exam-meta">
                    <span>
                      <TeamOutlined /> 班级
                    </span>
                    <strong>{exam.className}</strong>
                  </div>
                  <div className="exam-meta">
                    <span>
                      <CalendarOutlined /> 日期
                    </span>
                    <strong>{exam.examDate}</strong>
                  </div>
                  <div className="exam-progress">
                    <div>
                      <span>成绩录入</span>
                      <strong>
                        {exam.enteredScores}/{exam.totalStudents}
                      </strong>
                    </div>
                    <Progress percent={percent} size="small" />
                  </div>
                </Card>
              </Col>
            );
          })}
        </Row>
      )}

      <ExamFormModal
        open={modalOpen}
        editing={false}
        saving={saving}
        form={form}
        classes={classes}
        onCancel={() => examFormGuard.confirmClose(() => setModalOpen(false))}
        onSubmit={() => void submit()}
      />
    </div>
  );
};

export default ExamsPage;
