import { BadRequestException } from '@nestjs/common';
import {
  IMPORT_FIELD_ALIASES,
  IMPORT_STEP_KEYS,
  type ColumnMapping,
  type ImportRunSettings,
  type ImportStepKey,
} from '../imports/imports.types';

export interface ConfirmedMappingOptions {
  /** 每个业务阶段允许的表头集合；为空时不校验表头是否存在。 */
  allowedHeadersByStep?: Partial<Record<ImportStepKey, string[]>>;
}

/**
 * 解析并校验用户确认的列映射。
 * 与 start_import_wizard 工具、预检卡 resolve 端点共用，保证两条入口口径一致。
 */
export function parseConfirmedMapping(
  raw: unknown,
  options: ConfirmedMappingOptions = {},
): Partial<Record<ImportStepKey, ColumnMapping>> | undefined {
  if (raw === undefined || raw === null) return undefined;
  if (typeof raw !== 'object' || Array.isArray(raw)) {
    throw new BadRequestException('mapping 参数格式错误');
  }
  const mapping: Partial<Record<ImportStepKey, ColumnMapping>> = {};
  for (const [stepKey, fields] of Object.entries(raw as Record<string, unknown>)) {
    if (!(IMPORT_STEP_KEYS as readonly string[]).includes(stepKey)) {
      throw new BadRequestException(`mapping 包含未知业务类型：${stepKey}`);
    }
    if (fields === undefined || fields === null) continue;
    if (typeof fields !== 'object' || Array.isArray(fields)) {
      throw new BadRequestException(`mapping 中「${stepKey}」的列映射格式错误`);
    }
    const typedStepKey = stepKey as ImportStepKey;
    const allowedFields = new Set(Object.keys(IMPORT_FIELD_ALIASES[typedStepKey]));
    const allowedHeaders = new Set(options.allowedHeadersByStep?.[typedStepKey] ?? []);
    const columnMapping: ColumnMapping = {};
    for (const [field, header] of Object.entries(fields as Record<string, unknown>)) {
      if (typeof field !== 'string' || !field.trim() || field.length > 50) continue;
      if (!allowedFields.has(field)) {
        throw new BadRequestException(`mapping 中「${stepKey}」包含未知字段：${field}`);
      }
      if (typeof header !== 'string' || !header.trim()) continue;
      const headerName = header.slice(0, 200);
      if (allowedHeaders.size > 0 && !allowedHeaders.has(headerName)) {
        throw new BadRequestException(
          `「${stepKey}」列映射「${headerName}」不在工作表表头中`,
        );
      }
      columnMapping[field] = headerName;
    }
    mapping[typedStepKey] = columnMapping;
  }
  return mapping;
}

/** 从包含顶层 organization/updateExisting/duplicatePolicy/skipUnmatched 的对象解析策略设置。 */
export function parseConfirmedSettings(parsedRecord: Record<string, unknown>): ImportRunSettings {
  const settings: ImportRunSettings = {};
  if (parsedRecord.organization !== undefined && parsedRecord.organization !== null) {
    if (typeof parsedRecord.organization !== 'string') {
      throw new BadRequestException('organization 必须是字符串');
    }
    const organization = parsedRecord.organization.trim().slice(0, 100);
    if (organization) settings.organization = organization;
  }
  if (parsedRecord.updateExisting !== undefined) {
    if (typeof parsedRecord.updateExisting !== 'boolean') {
      throw new BadRequestException('updateExisting 必须是布尔值');
    }
    settings.updateExisting = parsedRecord.updateExisting;
  }
  if (parsedRecord.duplicatePolicy !== undefined) {
    if (parsedRecord.duplicatePolicy !== 'error' && parsedRecord.duplicatePolicy !== 'skip') {
      throw new BadRequestException('duplicatePolicy 只能是 error 或 skip');
    }
    settings.duplicatePolicy = parsedRecord.duplicatePolicy;
  }
  if (parsedRecord.skipUnmatched !== undefined) {
    if (typeof parsedRecord.skipUnmatched !== 'boolean') {
      throw new BadRequestException('skipUnmatched 必须是布尔值');
    }
    settings.skipUnmatched = parsedRecord.skipUnmatched;
  }
  return settings;
}

/** 解析 resolve 端点嵌套的 settings 参数（缺省为空对象）。 */
export function parseNestedSettings(raw: unknown): ImportRunSettings {
  if (raw === undefined || raw === null) return {};
  if (typeof raw !== 'object' || Array.isArray(raw)) {
    throw new BadRequestException('settings 参数格式错误');
  }
  return parseConfirmedSettings(raw as Record<string, unknown>);
}

export function isExcelAttachment(attachment: {
  mimeType: string;
  originalName: string;
}): boolean {
  return (
    attachment.mimeType.includes('spreadsheetml') ||
    attachment.mimeType.includes('excel') ||
    attachment.mimeType.includes('csv') ||
    /\.(xlsx|csv)$/i.test(attachment.originalName)
  );
}
