import { EntityManager } from 'typeorm';
import { Room } from '../entities/room.entity';
import { Student } from '../entities/student.entity';
import { Occupancy } from '../entities/occupancy.entity';
import { applyString, optionalNumber, parseDateValue, textValue } from './imports.helpers';
import type { ImportLookups } from './imports.lookups';
import type {
  CellValue,
  ImportRowAction,
  ImportStepKey,
} from './imports.types';

const PHONE_RE = /^1[3-9]\d{9}$/;

/** 预览批次内的动态状态，用于阻止同一文件中产生重复的在住/换宿记录。 */
export interface ImportBatchState {
  checkinStudentIds: Set<number>;
  transferStudentIds: Set<number>;
}

export interface ValidatedRow {
  errors: string[];
  action: ImportRowAction | null;
  matchKey: string | null;
  targetId: number | null;
  normalized: Record<string, CellValue>;
  resolvedIds: Record<string, number>;
}

export function validateRow(
  stepKey: ImportStepKey,
  fields: Record<string, CellValue>,
  lookups: ImportLookups,
  batchState?: ImportBatchState,
): ValidatedRow {
  const errors: string[] = [];
  let action: ImportRowAction | null = null;
  let matchKey: string | null = null;
  let targetId: number | null = null;
  const normalized: Record<string, CellValue> = { ...fields };
  const resolvedIds: Record<string, number> = {};

  if (stepKey === 'students') {
    const name = textValue(fields.name);
    if (!name) errors.push('姓名不能为空');
    normalized.name = name;
    const phone = textValue(fields.phone);
    if (phone && !PHONE_RE.test(phone)) errors.push('手机号格式不正确');
    normalized.phone = phone;
    const genderRaw = textValue(fields.gender);
    let gender = genderRaw;
    if (genderRaw === '男' || genderRaw === '男性') gender = 'male';
    if (genderRaw === '女' || genderRaw === '女性') gender = 'female';
    if (genderRaw && !['male', 'female', '男', '女'].includes(genderRaw)) {
      errors.push('性别只能是男/女');
    }
    normalized.gender = gender;
    const statusRaw = textValue(fields.status);
    if (statusRaw && !['active', 'inactive', 'archived'].includes(statusRaw)) {
      errors.push('状态只能是 active/inactive/archived');
    }
    normalized.status = statusRaw || 'active';
    normalized.studentNo = textValue(fields.studentNo);
    normalized.idNumber = textValue(fields.idNumber);
    normalized.ethnicity = textValue(fields.ethnicity);
    normalized.emergencyContact = textValue(fields.emergencyContact);
    normalized.emergencyPhone = textValue(fields.emergencyPhone);
    const orgName = textValue(fields.organization);
    normalized.organization = orgName;
    if (orgName) {
      const org = lookups.organizations.get(orgName);
      if (!org) errors.push(`未找到校区：${orgName}`);
      else resolvedIds._organizationId = org.id;
    }
    const studentNo = textValue(fields.studentNo);
    let matched = studentNo ? lookups.studentsByNo.get(studentNo) : undefined;
    if (!matched && phone) matched = lookups.studentsByPhone.get(phone);
    if (matched) {
      action = 'update';
      targetId = matched.id;
      matchKey =
        studentNo && lookups.studentsByNo.get(studentNo) === matched
          ? studentNo
          : phone || studentNo || null;
    } else {
      action = 'create';
      matchKey = studentNo || phone || null;
    }
  }

  if (stepKey === 'rooms') {
    const roomNumber = textValue(fields.roomNumber);
    if (!roomNumber) errors.push('宿舍号不能为空');
    normalized.roomNumber = roomNumber;
    normalized.building = textValue(fields.building);
    normalized.roomType = textValue(fields.roomType);
    const capacity = Number(fields.capacity);
    if (!Number.isInteger(capacity) || capacity <= 0 || capacity > 999) {
      errors.push('容量必须是 1-999 的整数');
    }
    normalized.capacity = capacity;
    if (fields.floor !== null && fields.floor !== undefined && fields.floor !== '') {
      const floor = Number(fields.floor);
      if (!Number.isInteger(floor) || floor < 0) errors.push('楼层必须是大于等于 0 的整数');
      normalized.floor = floor;
    }
    const rentalCategory = textValue(fields.rentalCategory);
    if (rentalCategory && !['short', 'long'].includes(rentalCategory)) {
      errors.push('租期类型只能是 short/long');
    }
    normalized.rentalCategory = rentalCategory || 'short';
    const monthlyRate = Number(fields.monthlyRate ?? 0);
    if (!Number.isFinite(monthlyRate) || monthlyRate < 0)
      errors.push('月租必须是大于等于 0 的数字');
    normalized.monthlyRate = monthlyRate;
    const matched = roomNumber ? lookups.roomsByNumber.get(roomNumber) : undefined;
    if (matched) {
      action = 'update';
      targetId = matched.id;
      matchKey = roomNumber;
    } else {
      action = 'create';
      matchKey = roomNumber || null;
    }
  }

  if (stepKey === 'checkins' || stepKey === 'transfers') {
    const studentNo = textValue(fields.studentNo);
    const phone = textValue(fields.phone);
    const name = textValue(fields.name);
    let student: Student | undefined;
    if (studentNo) {
      student = lookups.studentsByNo.get(studentNo);
      if (!student && phone) student = lookups.studentsByPhone.get(phone);
      if (!student) errors.push('未找到匹配学生：请先完成“学生档案”阶段，或核对学号/手机号');
    } else if (phone) {
      student = lookups.studentsByPhone.get(phone);
      if (!student) errors.push('未找到匹配学生：请先完成“学生档案”阶段，或核对手机号');
    } else {
      errors.push('缺少学生标识：请映射“学号”或“手机号”');
    }
    if (student && name && student.name !== name) {
      errors.push(`姓名与手机号不匹配（档案姓名：${student.name}）`);
    }
    if (student) resolvedIds._studentId = student.id;
    const roomNumber = textValue(fields.roomNumber);
    const room = roomNumber ? lookups.roomsByNumber.get(roomNumber) : undefined;
    if (stepKey === 'checkins') {
      if (!roomNumber) errors.push('宿舍号不能为空');
      if (roomNumber && !room) {
        errors.push('未找到宿舍：请先完成“宿舍档案”阶段，或核对宿舍号');
      }
      const checkInDate = parseDateValue(fields.checkInDate);
      if (!checkInDate) errors.push('入住日期格式不正确（应为 YYYY-MM-DD）');
      normalized.checkInDate = checkInDate;
      normalized.stayType = textValue(fields.stayType) || 'short';
      if (student) {
        const active = lookups.activeOccupancies.get(student.id) ?? [];
        const batchCheckedIn = batchState?.checkinStudentIds.has(student.id) ?? false;
        const batchTransferred = batchState?.transferStudentIds.has(student.id) ?? false;
        if (batchCheckedIn || batchTransferred) {
          errors.push('该学生本次文件中已有在住记录，请勿重复导入');
        } else if (active.some((o) => o.roomId === room?.id)) {
          errors.push('该学生已有该宿舍的在住记录');
        } else if (active.length > 0) {
          errors.push('该学生已有在住记录：如需换宿请使用“换宿记录”阶段');
        }
      }
      if (errors.length === 0 && student && room) {
        action = 'create';
        matchKey = `${student.id}|${room.id}`;
        resolvedIds._roomId = room.id;
      }
    } else {
      const oldRoomNumber = textValue(fields.oldRoom);
      const newRoomNumber = textValue(fields.newRoom);
      const oldRoom = oldRoomNumber ? lookups.roomsByNumber.get(oldRoomNumber) : undefined;
      const newRoom = newRoomNumber ? lookups.roomsByNumber.get(newRoomNumber) : undefined;
      if (!oldRoomNumber) errors.push('原宿舍不能为空');
      if (oldRoomNumber && !oldRoom) {
        errors.push('未找到原宿舍：请先完成“宿舍档案”阶段，或核对宿舍号');
      }
      if (!newRoomNumber) errors.push('新宿舍不能为空');
      if (newRoomNumber && !newRoom) {
        errors.push('未找到新宿舍：请先完成“宿舍档案”阶段，或核对宿舍号');
      }
      if (oldRoom && newRoom && oldRoom.id === newRoom.id) errors.push('原宿舍和新宿舍不能相同');
      const transferDate = parseDateValue(fields.transferDate);
      if (!transferDate) errors.push('换宿日期格式不正确（应为 YYYY-MM-DD）');
      normalized.transferDate = transferDate;
      normalized.reason = textValue(fields.reason);
      if (student) {
        const active = lookups.activeOccupancies.get(student.id) ?? [];
        const batchCheckedIn = batchState?.checkinStudentIds.has(student.id) ?? false;
        const batchTransferred = batchState?.transferStudentIds.has(student.id) ?? false;
        const oldOccupancy = active.find((o) => o.roomId === oldRoom?.id);
        if (batchTransferred) {
          errors.push('该学生本次文件中已有换宿记录，请勿重复换宿');
        } else if (batchCheckedIn) {
          errors.push('该学生的入住记录来自本次文件，请先提交入住阶段后再换宿');
        } else if (!oldOccupancy) {
          errors.push('未找到该学生在原宿舍的在住记录：请先完成“入住记录”阶段');
        } else {
          targetId = oldOccupancy.id;
        }
      }
      if (errors.length === 0 && student && oldRoom && newRoom) {
        action = 'create';
        matchKey = `${student.id}|${oldRoom.id}->${newRoom.id}`;
        resolvedIds._newRoomId = newRoom.id;
      }
    }
  }

  return { errors, action, matchKey, targetId, normalized, resolvedIds };
}

export async function writeRow(
  manager: EntityManager,
  stepKey: ImportStepKey,
  action: ImportRowAction,
  fields: Record<string, CellValue>,
  targetId: number | null,
): Promise<number | null> {
  if (stepKey === 'students') {
    const studentRepo = manager.getRepository(Student);
    if (action === 'create') {
      const student = studentRepo.create({
        name: textValue(fields.name),
        studentNo: textValue(fields.studentNo) || undefined,
        phone: textValue(fields.phone) || undefined,
        idNumber: textValue(fields.idNumber) || undefined,
        gender: textValue(fields.gender) || undefined,
        ethnicity: textValue(fields.ethnicity) || undefined,
        emergencyContact: textValue(fields.emergencyContact) || undefined,
        emergencyPhone: textValue(fields.emergencyPhone) || undefined,
        status: textValue(fields.status) || 'active',
        organizationId: optionalNumber(fields._organizationId) ?? undefined,
      });
      await studentRepo.save(student);
      return student.id;
    }
    if (!targetId) throw new Error('缺少待更新学生记录');
    const student = await studentRepo.findOneBy({ id: targetId });
    if (!student) throw new Error('待更新的学生记录不存在');
    applyString(student, 'name', fields.name);
    applyString(student, 'studentNo', fields.studentNo);
    applyString(student, 'phone', fields.phone);
    applyString(student, 'idNumber', fields.idNumber);
    applyString(student, 'gender', fields.gender);
    applyString(student, 'ethnicity', fields.ethnicity);
    applyString(student, 'emergencyContact', fields.emergencyContact);
    applyString(student, 'emergencyPhone', fields.emergencyPhone);
    applyString(student, 'status', fields.status);
    const orgId = optionalNumber(fields._organizationId);
    if (orgId !== null) {
      student.organizationId = orgId;
    }
    await studentRepo.save(student);
    return student.id;
  }

  if (stepKey === 'rooms') {
    const roomRepo = manager.getRepository(Room);
    if (action === 'create') {
      const room = roomRepo.create({
        roomNumber: textValue(fields.roomNumber),
        building: textValue(fields.building) || undefined,
        floor: optionalNumber(fields.floor) ?? undefined,
        capacity: Number(fields.capacity),
        status: 'available',
        roomType: textValue(fields.roomType) || undefined,
        rentalCategory: textValue(fields.rentalCategory) || 'short',
        monthlyRate: Number(fields.monthlyRate ?? 0),
      });
      await roomRepo.save(room);
      return room.id;
    }
    if (!targetId) throw new Error('缺少待更新宿舍记录');
    const room = await roomRepo.findOneBy({ id: targetId });
    if (!room) throw new Error('待更新的宿舍记录不存在');
    applyString(room, 'roomNumber', fields.roomNumber);
    applyString(room, 'building', fields.building);
    applyString(room, 'roomType', fields.roomType);
    applyString(room, 'rentalCategory', fields.rentalCategory);
    if (fields.floor !== null && fields.floor !== undefined && fields.floor !== '') {
      room.floor = Number(fields.floor);
    }
    if (fields.capacity !== null && fields.capacity !== undefined && fields.capacity !== '') {
      room.capacity = Number(fields.capacity);
    }
    if (
      fields.monthlyRate !== null &&
      fields.monthlyRate !== undefined &&
      fields.monthlyRate !== ''
    ) {
      room.monthlyRate = Number(fields.monthlyRate);
    }
    await roomRepo.save(room);
    return room.id;
  }

  if (stepKey === 'checkins') {
    const studentId = optionalNumber(fields._studentId);
    const roomId = optionalNumber(fields._roomId);
    if (!studentId || !roomId) throw new Error('缺少学生或宿舍 ID');
    const occupancy = manager.getRepository(Occupancy).create({
      studentId,
      roomId,
      checkInDate: String(fields.checkInDate),
      billingStartDate: String(fields.checkInDate),
      status: 'active',
      stayType: textValue(fields.stayType) || 'short',
    });
    await manager.save(Occupancy, occupancy);
    return occupancy.id;
  }

  if (stepKey === 'transfers') {
    if (!targetId) throw new Error('缺少原入住记录');
    const studentId = optionalNumber(fields._studentId);
    const newRoomId = optionalNumber(fields._newRoomId);
    if (!studentId || !newRoomId) throw new Error('缺少学生或新宿舍 ID');
    const oldOccupancy = await manager.getRepository(Occupancy).findOneBy({ id: targetId });
    if (!oldOccupancy) throw new Error('原入住记录不存在');
    oldOccupancy.checkOutDate = String(fields.transferDate);
    oldOccupancy.status = 'archived';
    await manager.save(Occupancy, oldOccupancy);
    const newOccupancy = manager.getRepository(Occupancy).create({
      studentId,
      roomId: newRoomId,
      checkInDate: String(fields.transferDate),
      billingStartDate: String(fields.transferDate),
      status: 'active',
      stayType: oldOccupancy.stayType,
    });
    await manager.save(Occupancy, newOccupancy);
    return newOccupancy.id;
  }
  return null;
}
