import { BadRequestException } from '@nestjs/common';
import {
  IMPORT_FIELD_ALIASES,
  IMPORT_STEP_IDENTITY_FIELDS,
  IMPORT_STEP_LABELS,
  IMPORT_STEP_ORDER,
  IMPORT_STEP_REQUIRED_FIELDS,
} from './imports.types';
import type {
  ColumnMapping,
  ImportStageRequest,
  ImportStageSuggestion,
  ImportStepKey,
} from './imports.types';
import { headerMatches } from './imports.helpers';
import type { ImportSheetData } from './imports.workbook';

export function suggestMapping(headers: string[], stepKey: ImportStepKey): ColumnMapping {
  const mapping: ColumnMapping = {};
  for (const [field, aliases] of Object.entries(IMPORT_FIELD_ALIASES[stepKey])) {
    const found = headers.find((header) => aliases.some((alias) => headerMatches(header, alias)));
    if (found) mapping[field] = found;
  }
  return mapping;
}

export function suggestStep(headers: string[]): ImportStageSuggestion | null {
  let best: ImportStageSuggestion | null = null;
  for (const stepKey of IMPORT_STEP_ORDER) {
    const mapping = suggestMapping(headers, stepKey);
    if (Object.keys(mapping).length === 0) continue;
    const identity = IMPORT_STEP_IDENTITY_FIELDS[stepKey].filter(
      (field) => mapping[field],
    ).length;
    const required = IMPORT_STEP_REQUIRED_FIELDS[stepKey].filter(
      (field) => mapping[field],
    ).length;
    const score = Object.keys(mapping).length + identity * 3 + required * 2;
    if (!best || score > best.matchedFields) {
      best = { stepKey, mapping, matchedFields: score };
    }
  }
  return best;
}

export function autoAssignedSheets(
  sheets: ImportSheetData[],
  stepKey: ImportStepKey,
): string[] {
  return sheets
    .filter((sheet) => suggestStep(sheet.headers)?.stepKey === stepKey)
    .map((sheet) => sheet.name);
}

export function resolveAssignedSheets(
  stages: ImportStageRequest[],
  stepKey: ImportStepKey,
  available: string[],
): string[] {
  const names = stages
    .filter((stage) => stage.stepKey === stepKey && stage.sheet)
    .map((stage) => stage.sheet as string);
  const missing = names.filter((name) => !available.includes(name));
  if (missing.length > 0) {
    throw new BadRequestException(`工作表不存在：${missing.join('、')}`);
  }
  return [...new Set(names)];
}

export function assertMapping(
  stepKey: ImportStepKey,
  mapping: ColumnMapping,
  sheetsData: ImportSheetData[],
  usedSheets: string[],
): void {
  const required = IMPORT_STEP_REQUIRED_FIELDS[stepKey];
  const missingRequired = required.filter((field) => !mapping[field]);
  if (missingRequired.length > 0) {
    const labels: Record<string, string> = {
      name: '姓名',
      roomNumber: '宿舍号',
      capacity: '容量',
      checkInDate: '入住日期',
      oldRoom: '原宿舍',
      newRoom: '新宿舍',
      transferDate: '换宿日期',
    };
    throw new BadRequestException(
      `阶段「${IMPORT_STEP_LABELS[stepKey]}」缺少必需列映射：${missingRequired
        .map((field) => labels[field] ?? field)
        .join('、')}`,
    );
  }
  if (stepKey === 'students' || stepKey === 'checkins' || stepKey === 'transfers') {
    const hasIdentity = IMPORT_STEP_IDENTITY_FIELDS[stepKey].some((field) => mapping[field]);
    if (!hasIdentity) {
      throw new BadRequestException('请至少映射“学号”或“手机号”列用于匹配学生');
    }
  }
  const usedHeaders = new Set(
    usedSheets.flatMap((name) => sheetsData.find((s) => s.name === name)?.headers ?? []),
  );
  const missingHeaders = Object.values(mapping).filter((header) => !usedHeaders.has(header));
  if (missingHeaders.length > 0) {
    throw new BadRequestException(`映射的列不存在于所选工作表：${missingHeaders.join('、')}`);
  }
}
