import { BadRequestException, Injectable, NotFoundException } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { In, Repository } from 'typeorm';
import { Student } from '../entities/student.entity';
import { ClassStudent } from '../entities/class-student.entity';
import { AttendanceRecord } from '../entities/attendance-record.entity';
import { Occupancy } from '../entities/occupancy.entity';
import { PersonalExpense } from '../entities/personal-expense.entity';
import { Bill } from '../entities/bill.entity';
import { Deposit } from '../entities/deposit.entity';
import { StudentProfile } from '../entities/student-profile.entity';
import { StudentEnrollment } from '../entities/student-enrollment.entity';
import { ExamScore } from '../entities/exam-score.entity';
import { LearningRecord } from '../entities/learning-record.entity';
import { ResultArchive } from '../entities/result-archive.entity';
import { ArchiveAttachment } from '../entities/archive-attachment.entity';
import { StudentDingMapping } from '../entities/student-ding-mapping.entity';
import { StudentWallet } from '../entities/student-wallet.entity';
import { RoomInspectionDetail } from '../entities/room-inspection-detail.entity';

@Injectable()
export class StudentsLifecycleService {
  constructor(
    @InjectRepository(Student) private readonly repo: Repository<Student>,
    @InjectRepository(ClassStudent) private readonly classStudentRepo: Repository<ClassStudent>,
    @InjectRepository(AttendanceRecord)
    private readonly attendanceRepo: Repository<AttendanceRecord>,
    @InjectRepository(StudentProfile) private readonly profileRepo: Repository<StudentProfile>,
    @InjectRepository(StudentEnrollment)
    private readonly enrollmentRepo: Repository<StudentEnrollment>,
    @InjectRepository(ExamScore) private readonly examScoreRepo: Repository<ExamScore>,
    @InjectRepository(LearningRecord)
    private readonly learningRecordRepo: Repository<LearningRecord>,
    @InjectRepository(ResultArchive) private readonly resultRepo: Repository<ResultArchive>,
    @InjectRepository(Occupancy) private readonly occupancyRepo: Repository<Occupancy>,
    @InjectRepository(PersonalExpense)
    private readonly personalExpenseRepo: Repository<PersonalExpense>,
    @InjectRepository(Bill) private readonly billRepo: Repository<Bill>,
    @InjectRepository(Deposit) private readonly depositRepo: Repository<Deposit>,
    @InjectRepository(ArchiveAttachment)
    private readonly attachmentRepo: Repository<ArchiveAttachment>,
    @InjectRepository(StudentDingMapping)
    private readonly dingMappingRepo: Repository<StudentDingMapping>,
    @InjectRepository(StudentWallet) private readonly walletRepo: Repository<StudentWallet>,
    @InjectRepository(RoomInspectionDetail)
    private readonly inspectionDetailRepo: Repository<RoomInspectionDetail>,
  ) {}

  private async findOne(id: number) {
    const student = await this.repo.findOne({
      where: { id },
      relations: ['occupancies', 'occupancies.room'],
    });
    if (!student) throw new NotFoundException('学生不存在');
    return student;
  }

  async getArchiveExportMaps(studentIds: number[]) {
    if (studentIds.length === 0) {
      return {
        profiles: new Map<number, StudentProfile>(),
        results: new Map<number, ResultArchive>(),
      };
    }
    const [profiles, results] = await Promise.all([
      this.profileRepo.find({ where: { studentId: In(studentIds) } }),
      this.resultRepo.find({ where: { studentId: In(studentIds) } }),
    ]);
    return {
      profiles: new Map(profiles.map((profile) => [profile.studentId, profile])),
      results: new Map(results.map((result) => [result.studentId, result])),
    };
  }

  async batchRemove(ids: number[]) {
    if (!ids || ids.length === 0) throw new BadRequestException('请选择要归档的学生');
    const students = await this.repo.find({ where: { id: In(ids) } });
    const skipped: string[] = [];
    const targetIds: number[] = [];
    for (const s of students) {
      if (s.status === 'archived') skipped.push(s.name);
      else targetIds.push(s.id);
    }
    let affected = 0;
    if (targetIds.length > 0) {
      const result = await this.repo
        .createQueryBuilder()
        .update()
        .set({ status: 'archived' })
        .where('id IN (:...ids)', { ids: targetIds })
        .execute();
      affected = result.affected || 0;
    }
    const message =
      skipped.length > 0
        ? `成功归档 ${affected} 人；${skipped.length} 人已是归档状态被跳过（${skipped.slice(0, 5).join('、')}${skipped.length > 5 ? '…' : ''}）`
        : `已批量归档 ${affected} 人（数据已保留，可随时恢复）`;
    return { message, archived: affected, skipped: skipped.length };
  }

  async restore(id: number) {
    const student = await this.findOne(id);
    if (student.status !== 'archived') {
      throw new BadRequestException('该学生未被归档');
    }
    await this.repo.update(id, { status: 'active' });
    return { message: '已恢复' };
  }

  private async assertNoStudentReferences(studentId: number) {
    const [
      occupancyCount,
      personalExpenseCount,
      billCount,
      depositCount,
      classMemberCount,
      profileCount,
      enrollmentCount,
      examScoreCount,
      learningRecordCount,
      attachmentCount,
      resultCount,
      attendanceCount,
      dingMappingCount,
      walletCount,
      inspectionDetailCount,
    ] = await Promise.all([
      this.occupancyRepo.count({ where: { studentId } }),
      this.personalExpenseRepo.count({ where: { studentId } }),
      this.billRepo.count({ where: { studentId } }),
      this.depositRepo.count({ where: { studentId } }),
      this.classStudentRepo.count({ where: { studentId } }),
      this.profileRepo.count({ where: { studentId } }),
      this.enrollmentRepo.count({ where: { studentId } }),
      this.examScoreRepo.count({ where: { studentId } }),
      this.learningRecordRepo.count({ where: { studentId } }),
      this.attachmentRepo.count({ where: { studentId } }),
      this.resultRepo.count({ where: { studentId } }),
      this.attendanceRepo.count({ where: { studentId } }),
      this.dingMappingRepo.count({ where: { studentId } }),
      this.walletRepo.count({ where: { studentId } }),
      this.inspectionDetailRepo.count({ where: { studentId } }),
    ]);
    const refs: Array<[string, number]> = [
      ['入住记录', occupancyCount],
      ['个人费用', personalExpenseCount],
      ['账单', billCount],
      ['押金', depositCount],
      ['班级成员', classMemberCount],
      ['档案信息', profileCount],
      ['报名记录', enrollmentCount],
      ['考试成绩', examScoreCount],
      ['学习记录', learningRecordCount],
      ['档案附件', attachmentCount],
      ['录取结果', resultCount],
      ['考勤记录', attendanceCount],
      ['钉钉映射', dingMappingCount],
      ['学生钱包', walletCount],
      ['查寝明细', inspectionDetailCount],
    ];
    const references = refs.filter(([, count]) => count > 0);
    if (references.length > 0) {
      const names = references.map(([name]) => name).join('、');
      throw new BadRequestException(`该学生存在关联数据（${names}），无法永久删除`);
    }
  }

  async purge(id: number) {
    const student = await this.findOne(id);
    if (student.status !== 'archived') {
      throw new BadRequestException('仅已归档学生可以永久删除，请先归档');
    }
    await this.assertNoStudentReferences(id);
    await this.repo.delete(id);
    return { message: '已永久删除学生（不可恢复）' };
  }

  async batchPurge(ids: number[]) {
    const uniqueIds = [...new Set(ids || [])];
    if (uniqueIds.length === 0) throw new BadRequestException('请选择要永久删除的学生');
    if (uniqueIds.some((id) => !Number.isInteger(id) || id <= 0)) {
      throw new BadRequestException('学生 ID 无效');
    }
    const students = await this.repo.find({ where: { id: In(uniqueIds) } });
    if (students.length !== uniqueIds.length) throw new NotFoundException('部分学生不存在');

    const deleted: number[] = [];
    const skipped: string[] = [];
    for (const student of students) {
      if (student.status !== 'archived') {
        skipped.push(`${student.name}（未归档）`);
        continue;
      }
      try {
        await this.assertNoStudentReferences(student.id);
      } catch {
        skipped.push(`${student.name}（存在关联数据）`);
        continue;
      }
      await this.repo.delete(student.id);
      deleted.push(student.id);
    }
    const message =
      skipped.length > 0
        ? `已永久删除 ${deleted.length} 人；${skipped.length} 人被跳过（${skipped.slice(0, 5).join('、')}${skipped.length > 5 ? '…' : ''}）`
        : `已永久删除 ${deleted.length} 名学生（不可恢复）`;
    return { message, deleted: deleted.length, skipped: skipped.length };
  }

  async batchRestore(ids: number[]) {
    const uniqueIds = [...new Set(ids || [])];
    if (uniqueIds.length === 0) throw new BadRequestException('请选择要恢复的学生');
    if (uniqueIds.some((id) => !Number.isInteger(id) || id <= 0)) {
      throw new BadRequestException('学生 ID 无效');
    }
    const students = await this.repo.find({ where: { id: In(uniqueIds) } });
    if (students.length !== uniqueIds.length) throw new NotFoundException('部分学生不存在');

    const targetIds = students
      .filter((student) => student.status === 'archived')
      .map((student) => student.id);
    const skipped = students.length - targetIds.length;
    let restored = 0;
    if (targetIds.length > 0) {
      const result = await this.repo
        .createQueryBuilder()
        .update()
        .set({ status: 'active' })
        .where('id IN (:...ids)', { ids: targetIds })
        .execute();
      restored = result.affected || 0;
    }
    return { message: `已批量恢复 ${restored} 名学生`, restored, skipped };
  }
}
