import { Injectable, Logger, NotFoundException } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository, In } from 'typeorm';
import * as bcrypt from 'bcryptjs';
import { User, Class, ClassStudent, ClassTeacher, ClassSchedule, Student, AttendanceSession, Permission } from '../entities';
import { Role } from '../entities/role.entity';

@Injectable()
export class RbacUserService {
  private readonly logger = new Logger(RbacUserService.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 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;
  }

  private async resolveRoles(roleIds: number[]): Promise<Role[]> {
    const uniqueIds = [...new Set(roleIds)];
    const roles = uniqueIds.length > 0 ? await this.roleRepo.findByIds(uniqueIds) : [];
    if (roles.length !== uniqueIds.length) {
      const foundIds = new Set(roles.map((role) => role.id));
      const missingIds = uniqueIds.filter((id) => !foundIds.has(id));
      throw new Error(`角色不存在: ${missingIds.join(',')}`);
    }
    return roles;
  }

  async findAllUsers(isArchived = false) {
    const users = await this.userRepo.find({
      where: { isArchived },
      relations: ['roles'],
      order: { createdAt: 'DESC' },
    });
    const userIds = users.map((u) => u.id);
    const students = await this.studentRepo.find({
      where: { userId: In(userIds) },
      select: ['userId', 'status'],
    });
    const statusMap = new Map(students.map((s) => [s.userId, s.status]));
    return users.map((u) => ({
      id: u.id,
      username: u.username,
      name: u.name,
      isArchived: u.isArchived,
      studentStatus: statusMap.get(u.id) || null,
      lastLoginAt: u.lastLoginAt,
      createdAt: u.createdAt,
      updatedAt: u.updatedAt,
      roles: u.roles?.map((r) => ({ id: r.id, code: r.code, name: r.name })) || [],
      profile: u.profile || {},
    }));
  }

  async createUser(dto: { username: string; password: string; name: string; roleIds?: number[] }) {
    const exists = await this.userRepo.findOne({ where: { username: dto.username } });
    if (exists) throw new Error('用户名已存在');
    const hash = await bcrypt.hash(dto.password, 10);
    const user = this.userRepo.create({
      username: dto.username,
      passwordHash: hash,
      name: dto.name,
    });
    if (dto.roleIds && dto.roleIds.length > 0) {
      user.roles = await this.resolveRoles(dto.roleIds);
    }
    await this.userRepo.save(user);
    return { message: '用户创建成功' };
  }

  async updateUser(id: number, dto: { username?: string; name?: string; roleIds?: number[] }) {
    const user = await this.userRepo.findOne({ where: { id }, relations: ['roles'] });
    if (!user) throw new Error('用户不存在');
    if (dto.username !== undefined && dto.username !== user.username) {
      const exists = await this.userRepo.findOne({ where: { username: dto.username } });
      if (exists) throw new Error('用户名已存在');
      user.username = dto.username;
    }
    if (dto.name !== undefined) user.name = dto.name;
    if (dto.roleIds !== undefined) {
      user.roles = dto.roleIds.length > 0 ? await this.resolveRoles(dto.roleIds) : [];
    }
    await this.userRepo.save(user);
    return { message: '更新成功' };
  }

  async resetPassword(id: number, newPassword: string) {
    const user = await this.userRepo.findOne({ where: { id } });
    if (!user) throw new Error('用户不存在');
    user.passwordHash = await bcrypt.hash(newPassword, 10);
    await this.userRepo.save(user);
    return { message: '密码已重置' };
  }

  async archiveUser(id: number) {
    const user = await this.userRepo.findOne({ where: { id } });
    if (!user) throw new Error('用户不存在');
    if (user.username === 'admin') throw new Error('不能归档默认管理员');
    await this.userRepo.update(id, { isArchived: true });
    return { message: '用户已归档' };
  }

  async restoreUser(id: number) {
    const user = await this.userRepo.findOne({ where: { id } });
    if (!user) throw new Error('用户不存在');
    await this.userRepo.update(id, { isArchived: false, isActive: true });
    return { message: '用户已恢复' };
  }

  async purgeUser(id: number, currentUserId: number) {
    const user = await this.userRepo.findOne({ where: { id } });
    if (!user) throw new Error('用户不存在');
    if (!user.isArchived) throw new Error('仅已归档用户可以永久删除，请先归档');
    if (user.id === currentUserId) throw new Error('不能永久删除当前登录用户');
    if (user.username === 'admin') throw new Error('不能永久删除默认管理员');

    const [studentCount, classTeacherCount, scheduleCount, sessionStarted, sessionCompleted] =
      await Promise.all([
        this.studentRepo.count({ where: { userId: id } }),
        this.classTeacherRepo.count({ where: { userId: id } }),
        this.classScheduleRepo.count({ where: { teacherId: id } }),
        this.attendanceSessionRepo.count({ where: { startedBy: id } }),
        this.attendanceSessionRepo.count({ where: { completedBy: id } }),
      ]);
    const classHeadCount = await this.classRepo.count({
      where: [{ headTeacherId: id }, { lifeTeacherId: id }, { academicTeacherId: id }],
    });
    const references: string[] = [];
    if (studentCount > 0) references.push('关联学生');
    if (classTeacherCount > 0) references.push('任教班级');
    if (scheduleCount > 0) references.push('排课');
    if (classHeadCount > 0) references.push('班主任班级');
    if (sessionStarted > 0 || sessionCompleted > 0) references.push('考勤课次操作记录');
    if (references.length > 0) {
      throw new Error(`该用户存在关联数据（${references.join('、')}），无法永久删除`);
    }
    await this.userRepo.delete(id);
    return { message: '用户已永久删除（不可恢复）' };
  }

  async markAsStaff(userId: number) {
    const student = await this.studentRepo.findOne({ where: { userId } });
    if (!student) throw new Error('该用户没有学员记录');
    await this.studentRepo.update(student.id, { status: 'staff' });
    this.logger.log(`User ${userId} Student ${student.id} marked as staff`);
    return { message: '已标记为教职工' };
  }

  async markAsStudent(userId: number) {
    const student = await this.studentRepo.findOne({ where: { userId } });
    if (!student) throw new Error('该用户没有学员记录');
    await this.studentRepo.update(student.id, { status: 'active' });
    this.logger.log(`User ${userId} Student ${student.id} restored to student`);
    return { message: '已恢复为学员' };
  }

  async getUserProfile(id: number) {
    const user = await this.userRepo.findOne({ where: { id } });
    if (!user) throw new Error('用户不存在');
    return {
      id: user.id,
      username: user.username,
      name: user.name,
      profile: user.profile || {},
    };
  }

  async updateUserProfile(
    id: number,
    dto: { subjects?: string[]; joinedAt?: string; qualifications?: string },
  ) {
    const user = await this.userRepo.findOne({ where: { id } });
    if (!user) throw new Error('用户不存在');
    const current = user.profile || {};
    user.profile = {
      subjects: dto.subjects !== undefined ? dto.subjects : current.subjects,
      joinedAt: dto.joinedAt !== undefined ? dto.joinedAt : current.joinedAt,
      qualifications:
        dto.qualifications !== undefined ? dto.qualifications : current.qualifications,
    };
    await this.userRepo.save(user);
    return { message: '资料已更新', profile: user.profile };
  }


  async getTeachers(query?: { search?: string; page?: number; pageSize?: number }) {
    const page = query?.page || 1;
    const pageSize = query?.pageSize || 20;
    const teacherRoleCodes = ['teacher'];
    const teacherRoleNames = ['任课老师', '老师'];

    const qb = this.userRepo
      .createQueryBuilder('u')
      .leftJoinAndSelect('u.roles', 'role')
      .where('(role.code IN (:...roleCodes) OR role.name IN (:...roleNames))', {
        roleCodes: teacherRoleCodes,
        roleNames: teacherRoleNames,
      })
      .andWhere('u.isArchived = :isArchived', { isArchived: false });

    if (query?.search) {
      qb.andWhere('(u.name LIKE :s OR u.username LIKE :s)', { s: `%${query.search}%` });
    }

    const total = await qb.getCount();
    const users = await qb
      .orderBy('u.name', 'ASC')
      .skip((page - 1) * pageSize)
      .take(pageSize)
      .getMany();

    const userIds = users.map((user) => user.id);
    const assignments =
      userIds.length > 0
        ? await this.classTeacherRepo.find({ where: { userId: In(userIds) }, relations: ['class'] })
        : [];
    const assignmentsByUser = new Map<number, ClassTeacher[]>();
    for (const assignment of assignments) {
      const list = assignmentsByUser.get(assignment.userId) || [];
      list.push(assignment);
      assignmentsByUser.set(assignment.userId, list);
    }

    const list = users.map((u) => ({
      id: u.id,
      username: u.username,
      name: u.name,
      profile: u.profile,
      lastLoginAt: u.lastLoginAt,
      roles: u.roles || [],
      classAssignments: (assignmentsByUser.get(u.id) || []).map((assignment) => ({
        id: assignment.id,
        classId: assignment.classId,
        roleType: assignment.roleType,
        subject: assignment.subject,
        className: assignment.class?.name || null,
      })),
    }));

    return { list, total };
  }

  async updateTeacherProfile(
    id: number,
    profile: { subjects?: string[]; joinedAt?: string; qualifications?: string },
  ) {
    const user = await this.userRepo.findOne({ where: { id } });
    if (!user) throw new NotFoundException('用户不存在');
    user.profile = { ...user.profile, ...profile };
    return this.userRepo.save(user);
  }
}
