import { Injectable, Logger } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository, In } from 'typeorm';
import * as bcrypt from 'bcryptjs';
import { Permission, Role, User, Class, ClassStudent, ClassTeacher, ClassSchedule, Student, AttendanceSession } from '../entities';
import {
  PRESET_ROLES,
  PRESET_PERMISSIONS,
  DEPRECATED_PERMISSION_CODES,
  DEPRECATED_PERMISSION_CODE_SET,
  getChinaDateParts,
} from './rbac-presets';

@Injectable()
export class RbacService {
  private readonly logger = new Logger(RbacService.name);

  constructor(
    @InjectRepository(Permission) private permRepo: Repository<Permission>,
    @InjectRepository(Role) private roleRepo: Repository<Role>,
    @InjectRepository(User) private userRepo: Repository<User>,
    @InjectRepository(Class) private classRepo: Repository<Class>,
    @InjectRepository(ClassStudent) private classStudentRepo: Repository<ClassStudent>,
    @InjectRepository(ClassTeacher) private classTeacherRepo: Repository<ClassTeacher>,
    @InjectRepository(ClassSchedule) private classScheduleRepo: Repository<ClassSchedule>,
    @InjectRepository(Student) private studentRepo: Repository<Student>,
    @InjectRepository(AttendanceSession)
    private attendanceSessionRepo: Repository<AttendanceSession>,
  ) {}

  async findAllRoles(): Promise<Role[]> {
    return this.roleRepo.find({
      relations: ['permissions'],
      order: { id: 'ASC' },
    });
  }

  async findRoleById(id: number): Promise<Role> {
    return this.roleRepo.findOneOrFail({ where: { id }, relations: ['permissions'] });
  }

  private async resolvePermissions(permissionIds: number[]): Promise<Permission[]> {
    const uniqueIds = [...new Set(permissionIds)];
    const permissions = uniqueIds.length > 0 ? await this.permRepo.findByIds(uniqueIds) : [];
    if (permissions.length !== uniqueIds.length) {
      const foundIds = new Set(permissions.map((permission) => permission.id));
      const missingIds = uniqueIds.filter((id) => !foundIds.has(id));
      throw new Error(`权限不存在: ${missingIds.join(',')}`);
    }
    return permissions;
  }

  async getTeacherWorkspace(userId: number) {
    // Find all classes where this user is a teacher
    const teacherAssignments = await this.classTeacherRepo.find({
      where: { userId },
      relations: ['class'],
    });

    const classIds = [...new Set(teacherAssignments.map((t) => t.classId))];

    if (classIds.length === 0) {
      return { assignedClasses: [], todaySchedules: [], myStudents: [] };
    }

    const assignedClasses = teacherAssignments.map((t) => ({
      classId: t.classId,
      className: t.class?.name || '',
      classCode: t.class?.code || '',
      roleType: t.roleType,
      subject: t.subject,
    }));

    // Get today's China business date and day of week (1=Monday, 7=Sunday)
    const { date: todayStr, weekDay: adjustedWeekDay } = getChinaDateParts();

    const todaySchedules = await this.classScheduleRepo
      .createQueryBuilder('cs')
      .where('cs.classId IN (:...classIds)', { classIds })
      .andWhere('cs.weekDay = :weekDay', { weekDay: adjustedWeekDay })
      .andWhere('cs.startDate <= :today', { today: todayStr })
      .andWhere('cs.endDate >= :today', { today: todayStr })
      .andWhere('cs.status = :status', { status: 'active' })
      .orderBy('cs.startTime', 'ASC')
      .getMany();

    const classStudents = await this.classStudentRepo.find({
      where: { classId: In(classIds), status: 'active' },
      relations: ['student', 'class'],
    });

    const myStudents = classStudents.map((cs) => ({
      studentId: cs.studentId,
      studentName: cs.student?.name || '',
      studentNo: cs.student?.studentNo || '',
      className: cs.class?.name || '',
      classId: cs.classId,
      joinDate: cs.joinDate,
    }));

    return {
      assignedClasses,
      todaySchedules: todaySchedules.map((s) => ({
        id: s.id,
        classId: s.classId,
        classroomId: s.classroomId,
        teacherId: s.teacherId,
        weekDay: s.weekDay,
        startTime: s.startTime,
        endTime: s.endTime,
        subject: s.subject,
        scheduleType: s.scheduleType,
      })),
      myStudents,
    };
  }
}

@Injectable()
export class RbacSeedService {
  private readonly logger = new Logger(RbacSeedService.name);

  constructor(
    @InjectRepository(Permission) private permRepo: Repository<Permission>,
    @InjectRepository(Role) private roleRepo: Repository<Role>,
    @InjectRepository(User) private userRepo: Repository<User>,
  ) {}

  private async findLegacyPresetRole(preset: (typeof PRESET_ROLES)[number]): Promise<Role | null> {
    for (const code of preset.legacyCodes ?? []) {
      const role = await this.roleRepo.findOne({ where: { code } });
      if (role) return role;
    }
    for (const name of preset.legacyNames ?? []) {
      const role = await this.roleRepo.findOne({ where: { name } });
      if (role) return role;
    }
    return null;
  }

  async seedData(): Promise<void> {
    const restoredLegacyUsers = await this.userRepo.update({ isActive: false }, { isActive: true });
    if (restoredLegacyUsers.affected) {
      this.logger.log(
        `已恢复 ${restoredLegacyUsers.affected} 个旧版禁用账号，账号状态现统一由归档管理`,
      );
    }

    for (const p of PRESET_PERMISSIONS) {
      const exists = await this.permRepo.findOne({ where: { code: p.code } });
      if (!exists) {
        await this.permRepo.save(this.permRepo.create(p));
      }
    }
    const deprecatedUserDeletePermission = await this.permRepo.findOne({
      where: { code: 'user:delete' },
    });
    const allPerms = (await this.permRepo.find()).filter(
      (permission) =>
        permission.code !== 'user:delete' && !DEPRECATED_PERMISSION_CODE_SET.has(permission.code),
    );

    for (const r of PRESET_ROLES) {
      const exists =
        (await this.roleRepo.findOne({ where: { code: r.code } })) ||
        (await this.roleRepo.findOne({ where: { name: r.name } })) ||
        (await this.findLegacyPresetRole(r));
      if (!exists) {
        await this.roleRepo.save(
          this.roleRepo.create({
            name: r.name,
            code: r.code,
            description: r.description,
            isSystem: r.isSystem,
          }),
        );
      }
    }
    const allRoles = await this.roleRepo.find({ relations: ['permissions', 'users'] });

    if (deprecatedUserDeletePermission) {
      for (const role of allRoles) {
        const permissions = role.permissions ?? [];
        if (permissions.some((permission) => permission.id === deprecatedUserDeletePermission.id)) {
          role.permissions = permissions.filter(
            (permission) => permission.id !== deprecatedUserDeletePermission.id,
          );
          await this.roleRepo.save(role);
        }
      }
      await this.permRepo.remove(deprecatedUserDeletePermission);
    }

    const deprecatedPermissions = await this.permRepo.find({
      where: { code: In([...DEPRECATED_PERMISSION_CODES]) },
    });
    if (deprecatedPermissions.length > 0) {
      const deprecatedIds = new Set(deprecatedPermissions.map((permission) => permission.id));
      for (const role of allRoles) {
        const permissions = role.permissions ?? [];
        if (permissions.some((permission) => deprecatedIds.has(permission.id))) {
          role.permissions = permissions.filter((permission) => !deprecatedIds.has(permission.id));
          await this.roleRepo.save(role);
        }
      }
      await this.permRepo.remove(deprecatedPermissions);
      this.logger.log(`已清理废弃权限点: ${deprecatedPermissions.map((p) => p.code).join(', ')}`);
    }

    for (const preset of PRESET_ROLES) {
      const matchesPreset = (role: Role) =>
        role.name === preset.name ||
        role.code === preset.code ||
        preset.legacyNames?.includes(role.name) ||
        preset.legacyCodes?.includes(role.code);
      const candidates = allRoles.filter(matchesPreset);
      const role = candidates.find((candidate) => candidate.code === preset.code) ?? candidates[0];
      if (!role) continue;

      const duplicateRoles = candidates.filter((candidate) => candidate.id !== role.id);
      if (duplicateRoles.length > 0) {
        for (const duplicate of duplicateRoles) {
          for (const relatedUser of duplicate.users ?? []) {
            const user = await this.userRepo.findOne({
              where: { id: relatedUser.id },
              relations: ['roles'],
            });
            if (!user) continue;
            const remainingRoles = (user.roles ?? []).filter(
              (assignedRole) => assignedRole.id !== duplicate.id && assignedRole.id !== role.id,
            );
            user.roles = [...remainingRoles, role];
            await this.userRepo.save(user);
          }
          await this.roleRepo.remove(duplicate);
        }
      }

      if (
        role.code !== preset.code ||
        role.name !== preset.name ||
        role.description !== preset.description
      ) {
        role.code = preset.code;
        role.name = preset.name;
        role.description = preset.description;
        role.isSystem = preset.isSystem;
        role.status = 1;
        await this.roleRepo.save(role);
      }

      let perms: Permission[];
      if (preset.permissionGroups.length === 0) {
        // 超管：全部权限
        perms = allPerms;
      } else {
        // 按 group 匹配 + 额外权限（如老师的 student:view）
        const byGroup = allPerms.filter((p) => preset.permissionGroups.includes(p.group));
        const byExtra = preset.extraPermissions
          ? allPerms.filter((p) => preset.extraPermissions!.includes(p.code))
          : [];
        perms = [...byGroup, ...byExtra].filter(
          (p, i, arr) => arr.findIndex((x) => x.id === p.id) === i,
        );
      }

      // 系统预置角色必须严格遵循职责矩阵；额外授权请创建自定义角色叠加。
      const currentIds = role.permissions.map((permission) => permission.id).sort((a, b) => a - b);
      const targetIds = perms.map((permission) => permission.id).sort((a, b) => a - b);
      if (currentIds.join(',') !== targetIds.join(',')) {
        role.permissions = perms;
        await this.roleRepo.save(role);
      }
    }

    const count = await this.userRepo.count();
    if (count === 0) {
      const adminPassword = process.env.ADMIN_PASSWORD || 'admin123';
      const hash = await bcrypt.hash(adminPassword, 10);
      const adminUser = this.userRepo.create({
        username: 'admin',
        passwordHash: hash,
        name: '管理员',
      });
      const superAdminRole = allRoles.find((r) => r.code === 'super_admin');
      if (superAdminRole) {
        adminUser.roles = [superAdminRole];
      }
      await this.userRepo.save(adminUser);
      this.logger.log(
        `已创建默认管理员: admin / ${adminPassword === 'admin123' ? 'admin123 (请尽快修改!)' : '******'}`,
      );
    }

    this.logger.log(`种子数据初始化完成: ${allPerms.length} 权限点, ${allRoles.length} 角色`);
  }

}
