import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react';
import {
  CheckCircleOutlined,
  CloseCircleOutlined,
  DownloadOutlined,
  InboxOutlined,
  ReloadOutlined,
  StepForwardOutlined,
} from '@ant-design/icons';
import {
  Alert,
  Button,
  Checkbox,
  Descriptions,
  Flex,
  Modal,
  Progress,
  Select,
  Space,
  Spin,
  Steps,
  Table,
  Tag,
  Tooltip,
  Typography,
  Upload,
} from 'antd';
import type { UploadProps } from 'antd';
import { message } from '../../ui/app-message';
import { useUserStore } from '../../store/user/userStore';
import {
  commitImportStep,
  createImportRun,
  getImportRun,
  importErrorReportUrl,
  previewImportStep,
} from '../../api/imports';
import {
  STEP_FIELDS,
  type ImportPreviewResult,
  type ImportReceipt,
  type ImportRunDetail,
  type ImportStageRequest,
  type ImportStepKey,
} from './types';

interface ImportWizardModalProps {
  open: boolean;
  /** AI 对话生成的导入任务；为空时向导从上传文件开始。 */
  runId?: string | null;
  onClose: () => void;
}

type RowAction = 'create' | 'update' | 'skip';

const ACTION_META: Record<RowAction, { label: string; color: string }> = {
  create: { label: '新建', color: 'blue' },
  update: { label: '更新', color: 'orange' },
  skip: { label: '跳过', color: 'default' },
};

function guessMapping(stepKey: ImportStepKey, headers: string[]): Record<string, string> {
  const mapping: Record<string, string> = {};
  for (const field of STEP_FIELDS[stepKey]) {
    const hit = headers.find((header) => {
      const normalizedHeader = header.replace(/[\s（）()]/g, '').toLowerCase();
      const normalizedLabel = field.label.replace(/[\s（）()]/g, '').toLowerCase();
      return (
        normalizedHeader === normalizedLabel ||
        normalizedHeader.includes(normalizedLabel) ||
        normalizedLabel.includes(normalizedHeader)
      );
    });
    if (hit) mapping[field.key] = hit;
  }
  return mapping;
}

function keyInfo(stepKey: ImportStepKey, row: ImportPreviewResult['rows'][number]): string {
  const fields = row.fields;
  if (stepKey === 'students') {
    return [fields.name, fields.studentNo, fields.phone].filter(Boolean).join(' / ');
  }
  if (stepKey === 'rooms') {
    return [fields.roomNumber, fields.building, fields.floor].filter(Boolean).join(' / ');
  }
  if (stepKey === 'checkins') {
    return [fields.name, fields.studentNo, fields.phone, fields.roomNumber, fields.checkInDate]
      .filter(Boolean)
      .join(' / ');
  }
  return [fields.studentNo, fields.phone, fields.oldRoom, fields.newRoom, fields.transferDate]
    .filter(Boolean)
    .join(' / ');
}

async function downloadErrorReport(runId: string, stepKey?: ImportStepKey): Promise<void> {
  const token = useUserStore.getState().token;
  const response = await fetch(importErrorReportUrl(runId, stepKey), {
    headers: token ? { Authorization: `Bearer ${token}` } : undefined,
  });
  if (!response.ok) throw new Error('错误报告下载失败');
  const blob = await response.blob();
  const url = URL.createObjectURL(blob);
  const anchor = document.createElement('a');
  anchor.href = url;
  anchor.download = `导入错误报告-${runId.slice(0, 8)}.csv`;
  anchor.click();
  window.setTimeout(() => URL.revokeObjectURL(url), 60_000);
}

export const ImportWizardModal: React.FC<ImportWizardModalProps> = ({
  open,
  runId: initialRunId,
  onClose,
}) => {
  const [run, setRun] = useState<ImportRunDetail | null>(null);
  const [loadingRun, setLoadingRun] = useState(false);
  const [uploading, setUploading] = useState(false);
  const [uploadPercent, setUploadPercent] = useState(0);
  const [activeStepKey, setActiveStepKey] = useState<ImportStepKey | null>(null);
  const [sheetSelection, setSheetSelection] = useState<Record<string, string[]>>({});
  const [mappingDraft, setMappingDraft] = useState<Record<string, Record<string, string>>>({});
  const [previewByStep, setPreviewByStep] = useState<Record<string, ImportPreviewResult>>({});
  const [previewLoading, setPreviewLoading] = useState(false);
  const [onlyErrors, setOnlyErrors] = useState(false);
  const [rowActions, setRowActions] = useState<Record<number, RowAction>>({});
  const [commitLoading, setCommitLoading] = useState(false);
  const [receipt, setReceipt] = useState<ImportReceipt | null>(null);
  const [reportDownloading, setReportDownloading] = useState(false);
  const requestSeq = useRef(0);

  const loadRun = useCallback(async (runId: string) => {
    const seq = ++requestSeq.current;
    setLoadingRun(true);
    try {
      const detail = await getImportRun(runId);
      if (seq !== requestSeq.current) return;
      setRun(detail);
      const selections: Record<string, string[]> = {};
      const mappings: Record<string, Record<string, string>> = {};
      const sheetHeaders = new Map(detail.sheets.map((sheet) => [sheet.name, sheet.headers]));
      for (const step of detail.steps) {
        selections[step.stepKey] = step.sheets;
        const mapped =
          step.mapping && Object.keys(step.mapping).length > 0
            ? step.mapping
            : guessMapping(step.stepKey, sheetHeaders.get(step.sheets[0] ?? '') ?? []);
        mappings[step.stepKey] = mapped;
      }
      setSheetSelection(selections);
      setMappingDraft(mappings);
      setPreviewByStep({});
      setRowActions({});
      setReceipt(null);
      const firstActive =
        detail.steps.find((step) => step.status !== 'skipped' && step.status !== 'committed') ??
        detail.steps.find((step) => step.status !== 'skipped');
      setActiveStepKey(firstActive?.stepKey ?? detail.currentStepKey);
    } catch (error) {
      if (seq !== requestSeq.current) return;
      message.error(error instanceof Error ? error.message : '导入任务加载失败');
      setRun(null);
    } finally {
      if (seq === requestSeq.current) setLoadingRun(false);
    }
  }, []);

  useEffect(() => {
    if (!open || !initialRunId) return;
    void loadRun(initialRunId);
  }, [open, initialRunId, loadRun]);

  const activeStep = useMemo(
    () => run?.steps.find((step) => step.stepKey === activeStepKey) ?? null,
    [run, activeStepKey],
  );
  const preview = activeStepKey ? (previewByStep[activeStepKey] ?? null) : null;

  const sheetOptions = useMemo(() => (run?.sheets ?? []).map((sheet) => sheet.name), [run]);
  const headerOptions = useMemo(() => {
    if (!run || !activeStepKey) return [];
    const names = sheetSelection[activeStepKey] ?? [];
    const headers = new Set<string>();
    for (const sheet of run.sheets) {
      if (names.includes(sheet.name)) sheet.headers.forEach((header) => headers.add(header));
    }
    return [...headers];
  }, [run, activeStepKey, sheetSelection]);

  /** 创建导入任务并加载详情：统一处理上传进度与 loading 状态。成功返回 run 详情，失败返回 null */
  const uploadRun = async (
    file: File,
    options: {
      source: 'ai' | 'manual';
      conversationId?: number;
      stages?: ImportStageRequest[];
      mapping?: Record<string, Record<string, string>>;
    },
    errorMessage: string,
  ): Promise<ImportRunDetail | null> => {
    setUploading(true);
    setUploadPercent(0);
    try {
      const detail = await createImportRun(file, {
        ...options,
        onProgress: (percent) => setUploadPercent(percent),
      });
      await loadRun(detail.id);
      return detail;
    } catch (error) {
      message.error(error instanceof Error ? error.message : errorMessage);
      return null;
    } finally {
      setUploading(false);
      setUploadPercent(0);
    }
  };

  const handleUpload: UploadProps['customRequest'] = async (options) => {
    const file = options.file as File;
    const detail = await uploadRun(file, { source: 'manual' }, '文件上传失败');
    if (detail) message.success(`已识别 ${detail.sheets.length} 个工作表`);
  };

  const handlePreview = async () => {
    if (!run || !activeStepKey || !activeStep) return;
    const mapping = mappingDraft[activeStepKey] ?? {};
    const required = STEP_FIELDS[activeStepKey]
      .filter((field) => field.required)
      .map((field) => field.key);
    const missing = required.filter((key) => !mapping[key]);
    if (missing.length > 0) {
      message.warning('请先完成必填列的映射');
      return;
    }
    setPreviewLoading(true);
    try {
      const result = await previewImportStep(run.id, activeStepKey, {
        sheets: sheetSelection[activeStepKey] ?? [],
        mapping,
      });
      setPreviewByStep((prev) => ({ ...prev, [activeStepKey]: result }));
      setRowActions({});
    } catch (error) {
      message.error(error instanceof Error ? error.message : '预览失败');
    } finally {
      setPreviewLoading(false);
    }
  };

  const handleCommit = async () => {
    if (!run || !activeStepKey || !preview) return;
    setCommitLoading(true);
    try {
      const decisions = preview.rows
        .filter((row) => row.status === 'valid')
        .map((row) => ({ rowId: row.id, action: rowActions[row.id] ?? row.action ?? 'create' }));
      const result = await commitImportStep(run.id, activeStepKey, decisions);
      setReceipt(result);
      const refreshed = await getImportRun(run.id);
      setRun(refreshed);
      setPreviewByStep({});
      setRowActions({});
      if (result.nextStepKey) {
        setActiveStepKey(result.nextStepKey);
        const nextStep = refreshed.steps.find((step) => step.stepKey === result.nextStepKey);
        setMappingDraft((prev) => ({
          ...prev,
          [result.nextStepKey as string]: nextStep?.mapping ?? {},
        }));
      }
    } catch (error) {
      message.error(error instanceof Error ? error.message : '提交失败');
    } finally {
      setCommitLoading(false);
    }
  };

  const handleReupload = async (file: File) => {
    if (!run || !activeStepKey) return;
    const detail = await uploadRun(
      file,
      {
        source: 'manual',
        stages: [{ stepKey: activeStepKey, sheets: sheetSelection[activeStepKey] ?? [] }],
        mapping: { [activeStepKey]: mappingDraft[activeStepKey] ?? {} },
      },
      '重新上传失败',
    );
    if (detail) message.success('已重新上传，并保留原列映射');
  };

  const previewRows = useMemo(() => {
    if (!preview) return [];
    return onlyErrors ? preview.rows.filter((row) => row.status === 'error') : preview.rows;
  }, [preview, onlyErrors]);

  const columns = useMemo(() => {
    if (!activeStepKey) return [];
    return [
      { title: '行号', dataIndex: 'rowNumber', width: 70 },
      { title: '工作表', dataIndex: 'sheetName', width: 120, ellipsis: true },
      {
        title: '数据',
        key: 'data',
        render: (_: unknown, row: ImportPreviewResult['rows'][number]) =>
          keyInfo(activeStepKey, row),
      },
      {
        title: '判定',
        key: 'action',
        width: 90,
        render: (_: unknown, row: ImportPreviewResult['rows'][number]) => {
          const action = rowActions[row.id] ?? row.action;
          return action ? (
            <Tag color={ACTION_META[action].color}>{ACTION_META[action].label}</Tag>
          ) : (
            '-'
          );
        },
      },
      {
        title: '状态',
        key: 'status',
        width: 90,
        render: (_: unknown, row: ImportPreviewResult['rows'][number]) =>
          row.status === 'error' ? (
            <Tag color="red" icon={<CloseCircleOutlined />}>
              错误
            </Tag>
          ) : (
            <Tag color="green" icon={<CheckCircleOutlined />}>
              有效
            </Tag>
          ),
      },
      {
        title: '错误信息',
        key: 'errors',
        ellipsis: true,
        render: (_: unknown, row: ImportPreviewResult['rows'][number]) =>
          row.errors.length > 0 ? (
            <Tooltip title={row.errors.join('；')}>
              <Typography.Text type="danger" style={{ maxWidth: 320 }}>
                {row.errors.join('；')}
              </Typography.Text>
            </Tooltip>
          ) : null,
      },
      {
        title: '处理方式',
        key: 'decision',
        width: 120,
        render: (_: unknown, row: ImportPreviewResult['rows'][number]) =>
          row.status === 'valid' ? (
            <Select
              size="small"
              value={rowActions[row.id] ?? row.action ?? 'create'}
              options={
                row.action === 'update'
                  ? [
                      { value: 'update', label: '更新' },
                      { value: 'skip', label: '跳过' },
                    ]
                  : [
                      { value: 'create', label: '新建' },
                      { value: 'skip', label: '跳过' },
                    ]
              }
              onChange={(value: RowAction) =>
                setRowActions((prev) => ({ ...prev, [row.id]: value }))
              }
            />
          ) : null,
      },
    ];
  }, [activeStepKey, rowActions]);

  const stageItems = useMemo(
    () =>
      (run?.steps ?? [])
        .filter((step) => step.status !== 'skipped')
        .map((step) => ({
          key: step.stepKey,
          title: step.label,
          status:
            step.status === 'committed'
              ? ('finish' as const)
              : step.stepKey === activeStepKey
                ? ('process' as const)
                : ('wait' as const),
        })),
    [run, activeStepKey],
  );

  const allCommitted = run?.status === 'committed';

  return (
    <Modal
      open={open}
      onCancel={onClose}
      footer={null}
      width={980}
      title="Excel 批量导入向导"
      destroyOnHidden={false}
    >
      {!run && !loadingRun ? (
        <Space orientation="vertical" size={16} style={{ width: '100%' }}>
          <Alert
            type="info"
            showIcon
            title="上传 Excel 后，系统会自动识别工作表并按业务依赖分阶段（学生/宿舍 → 入住/换宿）。每一阶段都需要先预览、再确认，确认后才会写入数据库。"
          />
          <Upload.Dragger
            accept=".xlsx,.csv"
            maxCount={1}
            showUploadList={false}
            disabled={uploading}
            customRequest={handleUpload}
          >
            <p className="ant-upload-drag-icon">
              <InboxOutlined />
            </p>
            <p className="ant-upload-text">点击或拖拽 .xlsx / .csv 文件到此区域</p>
            <p className="ant-upload-hint">单文件不超过 10MB；.xls 请先另存为 .xlsx</p>
          </Upload.Dragger>
          {uploading ? (
            <Flex vertical gap={4} style={{ marginTop: 8 }}>
              <Progress
                percent={uploadPercent}
                size="small"
                status={uploadPercent > 0 && uploadPercent < 100 ? 'active' : 'normal'}
              />
              <Typography.Text type="secondary" style={{ fontSize: 12, textAlign: 'center' }}>
                {uploadPercent > 0 && uploadPercent < 100
                  ? `正在上传 ${uploadPercent}%...`
                  : '正在上传并解析文件...'}
              </Typography.Text>
            </Flex>
          ) : null}
        </Space>
      ) : (
        <Space orientation="vertical" size={16} style={{ width: '100%' }}>
          <Flex justify="space-between" align="center" wrap gap={8}>
            <Space wrap>
              <Typography.Text strong>{run?.fileName}</Typography.Text>
              <Tag color={allCommitted ? 'success' : 'processing'}>
                {allCommitted ? '已完成' : '待处理'}
              </Tag>
              <Tag>当前阶段：{activeStep?.label ?? '—'}</Tag>
            </Space>
            <Space>
              {preview && preview.summary.error > 0 && (
                <Button
                  size="small"
                  icon={<DownloadOutlined />}
                  loading={reportDownloading}
                  onClick={() => {
                    setReportDownloading(true);
                    void downloadErrorReport(run?.id ?? '', activeStepKey ?? undefined)
                      .then(() => message.success('错误报告已下载'))
                      .catch((error: unknown) =>
                        message.error(error instanceof Error ? error.message : '下载失败'),
                      )
                      .finally(() => setReportDownloading(false));
                  }}
                >
                  下载错误报告
                </Button>
              )}
              <Button size="small" onClick={onClose}>
                关闭
              </Button>
            </Space>
          </Flex>

          <Steps
            size="small"
            items={stageItems}
            onChange={(index) => {
              const step = (run?.steps ?? []).filter((s) => s.status !== 'skipped')[index];
              if (step) setActiveStepKey(step.stepKey);
            }}
          />

          {loadingRun ? (
            <Flex justify="center" style={{ padding: 32 }}>
              <Spin description="正在加载导入任务..." />
            </Flex>
          ) : allCommitted ? (
            <Space orientation="vertical" size={12} style={{ width: '100%' }}>
              <Alert type="success" showIcon title="全部阶段已提交完成" />
              <Descriptions
                bordered
                size="small"
                column={2}
                items={(run?.steps ?? [])
                  .filter((step) => step.status !== 'skipped')
                  .map((step) => ({
                    key: step.stepKey,
                    label: step.label,
                    children: step.summary
                      ? `新建 ${step.summary.create} / 更新 ${step.summary.update} / 跳过 ${step.summary.skip} / 失败 ${step.summary.error}`
                      : '—',
                  }))}
              />
              <Button type="primary" onClick={onClose}>
                完成
              </Button>
            </Space>
          ) : activeStep ? (
            <Space orientation="vertical" size={12} style={{ width: '100%' }}>
              {receipt && (
                <Alert
                  type={receipt.status === 'committed' ? 'success' : 'warning'}
                  showIcon
                  title={receipt.message}
                  closable
                  onClose={() => setReceipt(null)}
                />
              )}
              {activeStep.status === 'committed' ? (
                <Alert
                  type="success"
                  showIcon
                  title={`「${activeStep.label}」已提交`}
                  description={
                    activeStep.summary
                      ? `新建 ${activeStep.summary.create} / 更新 ${activeStep.summary.update} / 跳过 ${activeStep.summary.skip} / 失败 ${activeStep.summary.error}`
                      : undefined
                  }
                />
              ) : preview ? (
                <Space orientation="vertical" size={12} style={{ width: '100%' }}>
                  <Flex wrap gap={12} align="center">
                    <Space size={4}>
                      <Tag color="blue">共 {preview.summary.total} 行</Tag>
                      <Tag color="green">有效 {preview.summary.valid}</Tag>
                      <Tag color="red">错误 {preview.summary.error}</Tag>
                      <Tag color="blue">新建 {preview.summary.create}</Tag>
                      <Tag color="orange">更新 {preview.summary.update}</Tag>
                    </Space>
                    <Checkbox
                      checked={onlyErrors}
                      onChange={(e) => setOnlyErrors(e.target.checked)}
                    >
                      只看错误行
                    </Checkbox>
                  </Flex>
                  <Table
                    size="small"
                    rowKey="id"
                    columns={columns}
                    dataSource={previewRows}
                    pagination={{ pageSize: 10, showSizeChanger: false }}
                    scroll={{ x: 900 }}
                  />
                  <Flex justify="end" gap={8}>
                    <Upload
                      accept=".xlsx,.csv"
                      showUploadList={false}
                      beforeUpload={(file) => {
                        void handleReupload(file);
                        return false;
                      }}
                    >
                      <Button icon={<ReloadOutlined />} loading={uploading}>
                        重新上传并保留映射
                      </Button>
                    </Upload>
                    <Button
                      type="primary"
                      icon={<StepForwardOutlined />}
                      loading={commitLoading}
                      onClick={() => void handleCommit()}
                    >
                      确认提交本阶段
                    </Button>
                  </Flex>
                </Space>
              ) : (
                <Space orientation="vertical" size={12} style={{ width: '100%' }}>
                  <Alert
                    type="info"
                    showIcon
                    title={`配置「${activeStep.label}」阶段`}
                    description="选择该阶段使用的工作表，并确认列映射；系统会按“学号/手机号/宿舍号”自动区分新建或更新。"
                  />
                  <Flex align="center" gap={8}>
                    <Typography.Text style={{ width: 120 }}>工作表</Typography.Text>
                    <Select
                      mode="multiple"
                      style={{ minWidth: 320, flex: 1 }}
                      placeholder="选择该阶段的工作表"
                      value={sheetSelection[activeStepKey ?? ''] ?? []}
                      options={sheetOptions.map((name) => ({ value: name, label: name }))}
                      onChange={(values: string[]) =>
                        setSheetSelection((prev) => ({
                          ...prev,
                          [activeStepKey ?? '']: values,
                        }))
                      }
                    />
                  </Flex>
                  {STEP_FIELDS[activeStep.stepKey].map((field) => (
                    <Flex key={field.key} align="center" gap={8}>
                      <Typography.Text style={{ width: 120 }}>
                        {field.label}
                        {field.required ? <span style={{ color: '#ff4d4f' }}> *</span> : null}
                        {field.identity ? <Tag style={{ marginLeft: 4 }}>匹配键</Tag> : null}
                      </Typography.Text>
                      <Select
                        allowClear
                        showSearch
                        style={{ minWidth: 320, flex: 1 }}
                        placeholder="选择对应列（留空则自动识别）"
                        value={mappingDraft[activeStepKey ?? '']?.[field.key]}
                        options={headerOptions.map((header) => ({ value: header, label: header }))}
                        onChange={(value?: string) =>
                          setMappingDraft((prev) => {
                            const current = { ...prev[activeStepKey ?? ''] };
                            if (value) current[field.key] = value;
                            else delete current[field.key];
                            return { ...prev, [activeStepKey ?? '']: current };
                          })
                        }
                      />
                    </Flex>
                  ))}
                  <Flex justify="end">
                    <Button
                      type="primary"
                      loading={previewLoading}
                      onClick={() => void handlePreview()}
                    >
                      开始校验预览
                    </Button>
                  </Flex>
                </Space>
              )}
            </Space>
          ) : (
            <Alert type="warning" showIcon title="当前没有可处理的阶段" />
          )}
        </Space>
      )}
    </Modal>
  );
};
