import { Injectable, Logger, NotFoundException } 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,
} from '../entities';

const PRESET_PERMISSIONS: Array<{ code: string; name: string; group: string }> = [
  { code: 'dashboard:view', name: '查看数据面板', group: 'dashboard' },
  { code: 'notification:view', name: '查看通知', group: 'notification' },
  { code: 'student:view', name: '查看学生管理', group: 'student' },
  { code: 'student:basic-view', name: '查看学生基础信息', group: 'student-scope' },
  { code: 'teacher-workspace:view', name: '查看教师工作台', group: 'teacher-workspace' },
  { code: 'teacher:view', name: '查看教师', group: 'teacher' },
  { code: 'teacher:edit', name: '编辑教师', group: 'teacher' },
  { code: 'student:create', name: '新增学生', group: 'student' },
  { code: 'student:edit', name: '编辑学生', group: 'student' },
  { code: 'student:delete', name: '归档学生', group: 'student' },
  { code: 'student:import', name: '导入学生', group: 'student' },
  { code: 'student:export', name: '导出学生', group: 'student' },
  { code: 'exam:view', name: '查看和录入考试成绩', group: 'exam' },
  { code: 'room:view', name: '查看宿舍', group: 'room' },
  { code: 'room:inspect', name: '宿舍查寝', group: 'room' },
  { code: 'room:create', name: '新增宿舍', group: 'room' },
  { code: 'room:edit', name: '编辑宿舍', group: 'room' },
  { code: 'room:delete', name: '归档宿舍', group: 'room' },
  { code: 'occupancy:view', name: '查看入住', group: 'occupancy' },
  { code: 'occupancy:checkin', name: '办理入住', group: 'occupancy' },
  { code: 'occupancy:checkout', name: '办理退宿', group: 'occupancy' },
  { code: 'occupancy:transfer', name: '调换宿舍', group: 'occupancy' },
  { code: 'occupancy:delete', name: '归档入住记录', group: 'occupancy' },
  { code: 'expense:view', name: '查看费用', group: 'expense' },
  { code: 'expense:create', name: '录入费用', group: 'expense' },
  { code: 'expense:edit', name: '编辑费用', group: 'expense' },
  { code: 'expense:delete', name: '归档费用', group: 'expense' },
  { code: 'bill:view', name: '查看账单', group: 'bill' },
  { code: 'bill:generate', name: '生成账单', group: 'bill' },
  { code: 'bill:confirm', name: '确认账单', group: 'bill' },
  { code: 'bill:delete', name: '归档账单', group: 'bill' },
  { code: 'bill:export-excel', name: '导出 Excel', group: 'bill' },
  { code: 'bill:export-pdf', name: '导出 PDF', group: 'bill' },
  { code: 'deposit:view', name: '查看押金', group: 'deposit' },
  { code: 'deposit:create', name: '新增押金', group: 'deposit' },
  { code: 'deposit:edit', name: '编辑押金', group: 'deposit' },
  { code: 'deposit:delete', name: '归档押金', group: 'deposit' },
  { code: 'deposit:refund', name: '直接退还押金', group: 'deposit' },
  { code: 'wallet:view', name: '查看学生余额', group: 'wallet' },
  { code: 'wallet:edit', name: '充值和调账', group: 'wallet' },
  { code: 'classroom:view', name: '查看教室', group: 'classroom' },
  { code: 'classroom:create', name: '新增教室', group: 'classroom' },
  { code: 'classroom:edit', name: '编辑教室', group: 'classroom' },
  { code: 'classroom:delete', name: '归档教室', group: 'classroom' },
  { code: 'organization:view', name: '查看机构', group: 'organization' },
  { code: 'organization:create', name: '新增机构', group: 'organization' },
  { code: 'organization:edit', name: '编辑机构', group: 'organization' },
  { code: 'organization:delete', name: '归档机构', group: 'organization' },
  { code: 'rental:view', name: '查看租赁订单', group: 'rental' },
  { code: 'rental:create', name: '新增租赁订单', group: 'rental' },
  { code: 'rental:edit', name: '编辑租赁订单', group: 'rental' },
  { code: 'rental:delete', name: '归档租赁订单', group: 'rental' },
  { code: 'log:view', name: '查看操作日志', group: 'log' },
  { code: 'log:create', name: '写入操作日志', group: 'log' },
  { code: 'user:view', name: '查看用户', group: 'user' },
  { code: 'user:create', name: '创建用户', group: 'user' },
  { code: 'user:edit', name: '编辑用户', group: 'user' },
  { code: 'user:reset-password', name: '重置密码', group: 'user' },
  { code: 'role:view', name: '查看角色', group: 'role' },
  { code: 'role:create', name: '创建角色', group: 'role' },
  { code: 'role:edit', name: '编辑角色', group: 'role' },
  { code: 'role:delete', name: '停用角色', group: 'role' },
  { code: 'class:view', name: '查看班级', group: 'class' },
  { code: 'class:create', name: '创建班级', group: 'class' },
  { code: 'class:edit', name: '编辑班级', group: 'class' },
  { code: 'class:delete', name: '归档班级', group: 'class' },
  { code: 'schedule:view', name: '查看排课', group: 'schedule' },
  { code: 'schedule:create', name: '创建排课', group: 'schedule' },
  { code: 'schedule:edit', name: '编辑排课', group: 'schedule' },
  { code: 'schedule:delete', name: '停用排课', group: 'schedule' },
  { code: 'attendance:view', name: '查看考勤', group: 'attendance' },
  { code: 'attendance:create', name: '新增考勤', group: 'attendance' },
  { code: 'attendance:edit', name: '编辑全部考勤', group: 'attendance' },
  { code: 'attendance:self-edit', name: '编辑任教班级考勤', group: 'attendance-scope' },
  { code: 'attendance:export', name: '导出考勤', group: 'attendance' },
  { code: 'sync:trigger', name: '触发数据同步', group: 'sync' },
  { code: 'sync:read', name: '查看同步状态', group: 'sync' },
  { code: 'integration:trigger', name: '触发集成', group: 'integration' },
  { code: 'integration:read', name: '查看集成状态', group: 'integration' },
  { code: 'ai:config:read', name: '查看 AI 配置', group: 'ai' },
  { code: 'ai:config:write', name: '修改 AI 配置', group: 'ai' },
  { code: 'ai:config:test', name: '测试 AI 连接', group: 'ai' },
];

const DEPRECATED_PERMISSION_CODES = [
  'profile:view',
  'attendance:generate',
  'learning:create',
  'learning:edit',
  'learning:delete',
  'exam:create',
  'exam:edit',
  'exam:delete',
  'department:view',
  'department:edit',
  'department:delete',
  // Legacy permission codes from older admin UI / seed data.
  'student:add',
  'student:update',
  'room:add',
  'room:update',
  'occupancy:add',
  'occupancy:update',
  'attendance:add',
  'attendance:update',
  'attendance:delete',
  'attendance:batch',
  'bill:export',
  'deposit:collect',
  'expense:add',
  'expense:update',
  'class:add',
  'class:update',
  'schedule:add',
  'schedule:update',
  'classroom:add',
  'classroom:update',
  'rental:add',
  'rental:update',
  'role:add',
  'role:update',
  'user:add',
  'user:update',
  'archive:view',
  'archive:import',
  'archive:export',
  'report:generate',
] as const;

const DEPRECATED_PERMISSION_CODE_SET = new Set<string>(DEPRECATED_PERMISSION_CODES);

function getChinaDateParts(date = new Date()): { date: string; weekDay: number } {
  const parts = Object.fromEntries(
    new Intl.DateTimeFormat('en-CA', {
      timeZone: 'Asia/Shanghai',
      year: 'numeric',
      month: '2-digit',
      day: '2-digit',
      weekday: 'short',
    })
      .formatToParts(date)
      .filter((part) => part.type !== 'literal')
      .map((part) => [part.type, part.value]),
  );
  const weekDays: Record<string, number> = { Mon: 1, Tue: 2, Wed: 3, Thu: 4, Fri: 5, Sat: 6, Sun: 7 };
  return {
    date: `${parts.year}-${parts.month}-${parts.day}`,
    weekDay: weekDays[parts.weekday],
  };
}

export const PRESET_ROLES: Array<{
  name: string;
  code: string;
  description: string;
  isSystem: boolean;
  permissionGroups: string[];
  extraPermissions?: string[];
  legacyNames?: string[];
  legacyCodes?: string[];
}> = [
  {
    name: '超级管理员',
    code: 'super_admin',
    description: '系统初始化、应急维护和全局权限处理',
    isSystem: true,
    permissionGroups: [],
    legacyNames: ['超管', 'super_admin'],
  },
  {
    name: '任课老师',
    code: 'teacher',
    description: '查看自己的排课、今日课程和任教班级考勤',
    isSystem: true,
    permissionGroups: ['notification'],
    extraPermissions: [
      'teacher-workspace:view',
      'schedule:view',
      'attendance:view',
      'attendance:create',
      'attendance:self-edit',
    ],
    legacyNames: ['老师'],
  },
  {
    name: '教务管理员',
    code: 'academic',
    description: '管理学生、班级、教师、全局排课和历史考勤',
    isSystem: true,
    permissionGroups: [
      'student',
      'exam',
      'class',
      'schedule',
      'attendance',
      'classroom',
      'dashboard',
      'notification',
    ],
    extraPermissions: [
      'teacher-workspace:view',
      'teacher:view',
      'teacher:edit',
      'sync:read',
      'sync:trigger',
    ],
    legacyNames: ['教务'],
  },
  {
    name: '住宿运营管理员',
    code: 'accommodation_operations',
    description: '管理宿舍、入住、住宿费用、账单、押金和退宿结算',
    isSystem: true,
    permissionGroups: [
      'room',
      'occupancy',
      'expense',
      'bill',
      'deposit',
      'wallet',
      'dashboard',
      'notification',
    ],
    extraPermissions: ['student:basic-view'],
    legacyNames: ['宿管老师', '宿管', '财务'],
    legacyCodes: ['dormitory_supervisor', 'dorm_manager', 'finance'],
  },
  {
    name: '教室运营管理员',
    code: 'classroom_operations',
    description: '管理教室、教室排期、外部机构和租赁订单',
    isSystem: true,
    permissionGroups: ['classroom', 'rental', 'organization', 'notification'],
    legacyNames: ['机构负责人'],
    legacyCodes: ['institution_head'],
  },
  {
    name: '系统管理员',
    code: 'system_admin',
    description: '管理账号、角色、日志、同步和系统配置',
    isSystem: true,
    permissionGroups: [
      'user',
      'role',
      'log',
      'integration',
      'sync',
      'ai',
      'notification',
    ],
  },
];

@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>,
  ) {}

  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> {
    // Step 1: 幂等插入所有权限点（先查后插，兼容 SQLite/MySQL）
    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),
    );

    // Step 2: 幂等插入预置角色
    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(', ')}`);
    }

    // Step 3: 合并旧角色并构建新的职责权限矩阵
    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);
      }
    }

    // Step 4: 初始化 admin 用户
    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} 角色`);
  }

  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;
  }

  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 createRole(dto: {
    name: string;
    description?: string;
    permissionIds?: number[];
  }): Promise<Role> {
    const role = this.roleRepo.create({ name: dto.name, description: dto.description });
    if (dto.permissionIds && dto.permissionIds.length > 0) {
      role.permissions = await this.resolvePermissions(dto.permissionIds);
    }
    return this.roleRepo.save(role);
  }

  async updateRole(
    id: number,
    dto: { name?: string; description?: string; permissionIds?: number[] },
  ): Promise<Role> {
    const role = await this.roleRepo.findOneOrFail({
      where: { id },
      relations: ['permissions'],
    });
    if (dto.name !== undefined && !role.isSystem) role.name = dto.name;
    if (dto.description !== undefined) role.description = dto.description;
    if (dto.permissionIds !== undefined) {
      role.permissions =
        dto.permissionIds.length > 0 ? await this.resolvePermissions(dto.permissionIds) : [];
    }
    return this.roleRepo.save(role);
  }

  async deleteRole(id: number): Promise<{ message: string }> {
    const role = await this.roleRepo.findOneOrFail({ where: { id } });
    if (role.isSystem) throw new Error('系统角色不可停用');
    if (role.status === 0) return { message: '角色已停用' };
    role.status = 0;
    await this.roleRepo.save(role);
    return { message: '角色已停用' };
  }

  async findAllPermissions(): Promise<Permission[]> {
    return this.permRepo.find({ order: { group: 'ASC', code: 'ASC' } });
  }

  async getPermissionTree(): Promise<{ group: string; permissions: Permission[] }[]> {
    const all = await this.findAllPermissions();
    const map = new Map<string, Permission[]>();
    for (const p of all) {
      if (!map.has(p.group)) map.set(p.group, []);
      map.get(p.group)!.push(p);
    }
    return Array.from(map.entries()).map(([group, permissions]) => ({ group, permissions }));
  }

  async getUserPermissions(userId: number): Promise<string[]> {
    const user = await this.userRepo.findOne({
      where: { id: userId },
      relations: ['roles', 'roles.permissions'],
    });
    if (!user || !user.roles) return [];
    const codes = new Set<string>();
    for (const role of user.roles) {
      if (role.status !== 1) continue;
      for (const perm of role.permissions) {
        codes.add(perm.code);
      }
    }
    return Array.from(codes);
  }

  // ---- 用户管理 ----

  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,
      isActive: u.isActive,
      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; isActive?: boolean; 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.isActive !== undefined) user.isActive = dto.isActive;
    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 });
    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 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: [] };
    }

    // Get assigned classes
    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();

    // Get today's schedules for assigned classes
    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();

    // Get students in assigned classes
    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,
    };
  }

  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,
      });

    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,
      isActive: u.isActive,
      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);
  }
}
