import { BadRequestException } from '@nestjs/common';
import type {
  AiReview,
  AiReviewSection,
  AiReviewSectionType,
} from './entities/ai-review.entity';

export const MAX_TITLE = 50;
export const MAX_SUMMARY = 500;
export const MAX_SECTIONS = 20;
export const MAX_SECTION_TITLE = 50;
export const MAX_COLUMNS = 30;
export const MAX_COLUMN_KEY = 50;
export const MAX_COLUMN_TITLE = 50;
export const MAX_ROWS = 500;
export const MAX_CELL_LENGTH = 200;
export const MAX_ISSUES = 50;
export const MAX_ISSUE_LENGTH = 200;
export const MAX_SECTIONS_JSON_BYTES = 12 * 1024 * 1024;
export const MAX_SECTION_JSON_BYTES = Math.floor(MAX_SECTIONS_JSON_BYTES / MAX_SECTIONS);
export const MAX_CAPACITY = 200;

export const DATE_RE = /^\d{4}-\d{2}-\d{2}$/;
export const COLUMN_KEY_RE = /^[a-zA-Z0-9_]{1,50}$/;
export const SECTION_KEY_RE = /^[a-zA-Z0-9_]{1,50}$/;
export const SECTION_TYPES = new Set<AiReviewSectionType>([
  'students',
  'rooms',
  'transfers',
  'checkins',
]);
export const SECTION_ORDER: AiReviewSectionType[] = ['students', 'rooms', 'transfers', 'checkins'];
export const SECTION_DEPENDENCIES: Record<AiReviewSectionType, AiReviewSectionType[]> = {
  students: [],
  rooms: [],
  transfers: ['students', 'rooms'],
  checkins: [],
};
export const SCHEMA_KEYS = new Set(['title', 'summary', 'sections']);
export const SECTION_KEYS_ALLOWED = new Set([
  'key',
  'type',
  'title',
  'kind',
  'sheet',
  'columns',
  'rows',
  'issues',
]);
export const COLUMN_KEYS_ALLOWED = new Set(['key', 'title']);

/**
 * Column-key aliases the model may produce when parsing workbooks.
 * Keys are normalized to canonical names per section so the import
 * logic only deals with one vocabulary.
 */
export const SECTION_ALIASES: Record<AiReviewSectionType, Record<string, string>> = {
  students: {
    org: 'organization',
    organizationName: 'organization',
    orgName: 'organization',
  },
  rooms: {
    roomNo: 'roomNumber',
    number: 'roomNumber',
  },
  transfers: {
    fromRoom: 'oldRoom',
    currentRoom: 'oldRoom',
    sourceRoom: 'oldRoom',
    toRoom: 'newRoom',
    targetRoom: 'newRoom',
    destRoom: 'newRoom',
    date: 'transferDate',
    changeDate: 'transferDate',
    moveDate: 'transferDate',
    mobile: 'studentPhone',
    phone: 'studentPhone',
  },
  checkins: {
    studentName: 'name',
    mobile: 'phone',
    roomNo: 'roomNumber',
    room: 'roomNumber',
    date: 'checkInDate',
    inDate: 'checkInDate',
    checkinDate: 'checkInDate',
    outDate: 'checkOutDate',
    checkoutDate: 'checkOutDate',
  },
};

/**
 * Excel 表头 → 规范列名。与 SECTION_ALIASES 合并使用；
 * 键会被归一化（去空格/下划线/大小写），因此同时覆盖中文与英文写法。
 */
export const SECTION_HEADER_ALIASES: Record<AiReviewSectionType, Record<string, string>> = {
  students: {
    姓名: 'name',
    学生姓名: 'name',
    name: 'name',
    手机号: 'phone',
    电话: 'phone',
    联系电话: 'phone',
    phone: 'phone',
    mobile: 'phone',
    学号: 'studentNo',
    学生编号: 'studentNo',
    studentNo: 'studentNo',
    studentno: 'studentNo',
    性别: 'gender',
    gender: 'gender',
    机构: 'organization',
    所属机构: 'organization',
    校区: 'organization',
    组织: 'organization',
    organization: 'organization',
  },
  rooms: {
    房间号: 'roomNumber',
    宿舍号: 'roomNumber',
    房号: 'roomNumber',
    roomNumber: 'roomNumber',
    roomnumber: 'roomNumber',
    容量: 'capacity',
    床位数: 'capacity',
    床位: 'capacity',
    capacity: 'capacity',
    楼栋: 'building',
    楼号: 'building',
    building: 'building',
    楼层: 'floor',
    floor: 'floor',
    房型: 'roomType',
    房间类型: 'roomType',
    roomType: 'roomType',
  },
  transfers: {
    学号: 'studentNo',
    studentNo: 'studentNo',
    studentno: 'studentNo',
    手机号: 'studentPhone',
    学生手机号: 'studentPhone',
    电话: 'studentPhone',
    phone: 'studentPhone',
    studentPhone: 'studentPhone',
    原宿舍: 'oldRoom',
    原房间: 'oldRoom',
    oldRoom: 'oldRoom',
    目标宿舍: 'newRoom',
    新宿舍: 'newRoom',
    newRoom: 'newRoom',
    换宿日期: 'transferDate',
    日期: 'transferDate',
    transferDate: 'transferDate',
  },
  checkins: {
    姓名: 'name',
    学生姓名: 'name',
    name: 'name',
    手机号: 'phone',
    电话: 'phone',
    phone: 'phone',
    mobile: 'phone',
    学号: 'studentNo',
    studentNo: 'studentNo',
    宿舍号: 'roomNumber',
    房间号: 'roomNumber',
    roomNumber: 'roomNumber',
    楼栋: 'building',
    building: 'building',
    性别: 'gender',
    gender: 'gender',
    入住时间: 'checkInDate',
    入住日期: 'checkInDate',
    checkInDate: 'checkInDate',
    计费起始日: 'billingStartDate',
    计费开始日: 'billingStartDate',
    退宿日期: 'checkOutDate',
    退宿时间: 'checkOutDate',
    离宿时间: 'checkOutDate',
    入住类型: 'stayType',
    住宿类型: 'stayType',
  },
};

export const SECTION_CANONICAL_KEYS: Record<AiReviewSectionType, Set<string>> = {
  students: new Set(['name', 'phone', 'studentNo', 'gender', 'organization']),
  rooms: new Set(['roomNumber', 'capacity', 'building', 'floor', 'roomType']),
  transfers: new Set(['studentNo', 'studentPhone', 'oldRoom', 'newRoom', 'transferDate']),
  checkins: new Set([
    'name',
    'phone',
    'studentNo',
    'roomNumber',
    'checkInDate',
    'billingStartDate',
    'checkOutDate',
    'gender',
    'building',
    'stayType',
  ]),
};

export interface AiReviewSubmitResult {
  students: { created: number; skipped: number; issues: string[] };
  rooms: { created: number; skipped: number; issues: string[] };
  transfers: { completed: number; skipped: number; issues: string[] };
  checkins: { completed: number; skipped: number; issues: string[] };
  message: string;
}

export type AiReviewSectionResult =
  | { created: number; skipped: number; issues: string[] }
  | { completed: number; skipped: number; issues: string[] };

export interface ValidatedReviewSchema {
  title: string;
  summary: string | null;
  sections: AiReviewSection[];
}

export interface AiReviewStepSubmitResult {
  review: AiReview;
  result: AiReviewSectionResult;
  message: string;
}

export function isPlainRecord(value: unknown): value is Record<string, unknown> {
  return Boolean(value) && typeof value === 'object' && !Array.isArray(value);
}

export function requireString(
  value: unknown,
  label: string,
  max: number,
  optional = false,
): string {
  if (value === undefined || value === null) {
    if (optional) return '';
    throw new BadRequestException(`${label}不能为空`);
  }
  if (typeof value !== 'string' || !value.trim()) {
    throw new BadRequestException(`${label}必须是字符串`);
  }
  const trimmed = value.trim();
  if (trimmed.length > max) {
    throw new BadRequestException(`${label}长度不能超过 ${max}`);
  }
  return trimmed;
}

export function assertKeys(raw: Record<string, unknown>, allowed: Set<string>, label: string): void {
  for (const key of Object.keys(raw)) {
    if (!allowed.has(key)) throw new BadRequestException(`${label}包含未知属性: ${key}`);
  }
}

export function toDateString(value: unknown): string | null {
  if (typeof value === 'string' && DATE_RE.test(value.trim())) return value.trim();
  return null;
}

export function normalizePhone(value: unknown): string | null {
  if (typeof value !== 'string') return null;
  const phone = value.replace(/[\s-]/g, '');
  return /^1[3-9]\d{9}$/.test(phone) ? phone : null;
}

export function isSectionType(value: unknown): value is AiReviewSectionType {
  return typeof value === 'string' && SECTION_TYPES.has(value as AiReviewSectionType);
}

export function normalizeSectionType(
  key: string,
  rawType: unknown,
): AiReviewSectionType {
  if (rawType !== undefined && rawType !== null && !isSectionType(rawType)) {
    throw new BadRequestException(`分表业务类型不支持: ${JSON.stringify(rawType)}`);
  }
  if (isSectionType(rawType)) return rawType;
  if (isSectionType(key)) return key;
  const prefix = SECTION_ORDER.find((type) => key.startsWith(`${type}_`));
  if (prefix) return prefix;
  throw new BadRequestException(`分表标识无法解析业务类型: ${key}`);
}

export function sectionStatus(section: AiReviewSection): AiReviewSection['status'] {
  if (
    section.status === 'submitted' ||
    section.status === 'failed' ||
    section.status === 'skipped'
  ) {
    return section.status;
  }
  return 'pending';
}

export function emptySectionResult(key: AiReviewSectionType): AiReviewSectionResult {
  return key === 'transfers' || key === 'checkins'
    ? { completed: 0, skipped: 0, issues: [] }
    : { created: 0, skipped: 0, issues: [] };
}

export function sectionResultMessage(
  key: AiReviewSectionType,
  result: AiReviewSectionResult,
): string {
  if (key === 'students') {
    return `成功导入学生 ${(result as { created: number }).created} 人，跳过 ${result.skipped} 条`;
  }
  if (key === 'rooms') {
    return `成功导入宿舍 ${(result as { created: number }).created} 间，跳过 ${result.skipped} 条`;
  }
  if (key === 'transfers') {
    return `成功换宿 ${(result as { completed: number }).completed} 条，跳过 ${result.skipped} 条`;
  }
  return `成功入住 ${(result as { completed: number }).completed} 条，跳过 ${result.skipped} 条`;
}

export function withInitialSectionState(section: AiReviewSection): AiReviewSection {
  return {
    ...section,
    status: 'pending',
    resultSummary: null,
    submittedAt: null,
  };
}
