import { Injectable } from '@nestjs/common';
import { Role, RtcTokenBuilder } from '../../plugins/shengwang/RtcTokenBuilder2';
import { NacosConfigService } from '../../plugins/nacos/nacos-config.service';
import { EntityManager } from '@mikro-orm/core';
import type { TokenResponseDto } from './meeting.dto';
import { MeetingUser } from '@/entities/MeetingUser';
import { MeetingRedisService } from '../websocket/meeting-redis.service';

/**
 * 学生信息接口
 */
export interface StudentInfo {
  /** 学生 ID */
  studentId: number;
  /** 学生姓名 */
  studentName: string;
}

/**
 * 学生详细信息接口 (包含长短 ID)
 */
export interface StudentDetailInfo {
  /** 学生长 ID (数据库 ID) */
  longId: number;
  /** 学生短 ID (声网短 ID) */
  shortId: number;
  /** 学生姓名 */
  studentName: string;
}

@Injectable()
export class MeetingService {
  constructor(
    private readonly em: EntityManager,
    private readonly nacosConfig: NacosConfigService,
    private readonly redis: MeetingRedisService
  ) {}

  /**
   * 通过作业 ID 获取学生详细信息列表 (包含长短 ID)
   * @param homeworkId - 作业 ID
   * @returns 学生详细信息列表，包含长 ID、短 ID 和姓名
   */
  public async getStudentDetailsByHomeworkId(homeworkId: number, roomId?: string): Promise<StudentDetailInfo[]> {
    const id = Number(homeworkId);
    if (!Number.isFinite(id) || id <= 0) {
      return [];
    }

    // 查询学生信息并批量分配缺失的短 ID
    const sql = `
      SELECT u.Id AS longId, u.Name AS studentName, mu.id AS shortId
      FROM icr_homework h
      INNER JOIN scs_studentgroup sg ON h.StudentGroupId = sg.Id
      INNER JOIN scs_studentgroupdetail sgd ON sg.Id = sgd.StudentGroupId
      INNER JOIN scs_user u ON sgd.StudentId = u.Id
      LEFT JOIN meeting_user mu ON u.Id = mu.long_user_Id
      WHERE h.Id = ${id}
    `;

    const rows = await this.em.getConnection().execute(sql);
    const results = rows as Array<{ longId: number | string; studentName: string; shortId: number | null }>;

    // 批量插入缺失的短 ID
    const missingIds = results.filter((row) => row.shortId === null).map((row) => Number(row.longId));
    if (missingIds.length > 0) {
      // 批量插入缺失的短 ID
      await this.em.getConnection().execute(`
        INSERT INTO meeting_user (long_user_Id, created_at)
        VALUES ${missingIds.map((longUserId) => `(${longUserId}, NOW())`).join(',')}
        ON DUPLICATE KEY UPDATE long_user_Id = long_user_Id
      `);

      // 查询新分配的短 ID
      const newUsers = await this.em.getConnection().execute(`
        SELECT long_user_Id, id FROM meeting_user WHERE long_user_Id IN (${missingIds.join(',')})
      `);
      const shortIdMap = new Map<number, number>();
      (newUsers as Array<{ long_user_Id: number; id: number }>).forEach((user) => {
        shortIdMap.set(user.long_user_Id, user.id);
      });

      // 更新缺失的短 ID
      results.forEach((row) => {
        if (row.shortId === null) {
          row.shortId = shortIdMap.get(Number(row.longId)) ?? null;
        }
      });
    }

    // 转换为最终结果
    let finalResults = results.map((row) => ({
      longId: Number(row.longId),
      studentName: row.studentName || '',
      shortId: row.shortId ?? 0,
    }));

    // 如果提供了 roomId，从 Redis 获取排序并应用
    if (roomId) {
      const orderedIds = await this.redis.getStudentOrder(roomId);
      if (orderedIds.length > 0) {
        // 创建映射表
        const studentMap = new Map<number, StudentDetailInfo>();
        finalResults.forEach((student) => {
          studentMap.set(student.longId, student);
        });

        // 按 Redis 中的顺序重新排列
        const orderedResults: StudentDetailInfo[] = [];
        for (const longId of orderedIds) {
          const student = studentMap.get(longId);
          if (student) {
            orderedResults.push(student);
            studentMap.delete(longId); // 移除已添加的学生
          }
        }

        // 添加 Redis 中没有的学生 (新加入的学生)
        finalResults = [...orderedResults, ...Array.from(studentMap.values())];
      }
    }

    return finalResults;
  }

  /**
   * 保存学生排序顺序
   * @param roomId - 房间 ID
   * @param studentLongIds - 学生长 ID 数组 (按排序顺序)
   */
  public async saveStudentOrder(roomId: string, studentLongIds: number[]): Promise<void> {
    await this.redis.setStudentOrder(roomId, studentLongIds);
  }

  /**
   * 通过作业 ID 获取学生列表
   * @param homeworkId - 作业 ID
   * @returns 学生列表，包含学生 ID 和姓名
   */
  public async getStudentsByHomeworkId(homeworkId: number): Promise<StudentInfo[]> {
    const id = Number(homeworkId);
    if (!Number.isFinite(id) || id <= 0) {
      return [];
    }

    // 使用单条 SQL JOIN 查询 (性能最优)
    const sql = `
      SELECT 
        u.Id AS studentId,
        u.Name AS studentName
      FROM icr_homework h
      INNER JOIN scs_studentgroup sg ON h.StudentGroupId = sg.Id
      INNER JOIN scs_studentgroupdetail sgd ON sg.Id = sgd.StudentGroupId
      INNER JOIN scs_user u ON sgd.StudentId = u.Id
      WHERE h.Id = ${id}
    `;

    const rows = await this.em.getConnection().execute(sql);
    return (rows as Array<{ studentId: number | string; studentName: string }>).map((row) => ({
      studentId: Number(row.studentId),
      studentName: row.studentName || '',
    }));
  }

  /**
   * 通过作业 ID 查询老师的长 ID
   * @param homeworkId - 作业 ID（JoinRoomData.homeworkId）
   * @returns 老师长 ID，未查询到返回 null
   */
  public async getTeacherLongUserIdByHomeworkId(homeworkId: number): Promise<number | null> {
    const id = Number(homeworkId);
    if (!Number.isFinite(id) || id <= 0) {
      return null;
    }
    const sql = 'SELECT B.TeacherId AS teacherId FROM icr_homework AS A LEFT JOIN scs_studentgroup AS B ON A.StudentGroupId = B.Id WHERE A.Id = ? LIMIT 1';
    const rows = (await this.em.getConnection().execute(sql, [id])) as Array<{ teacherId?: number | string }>;
    const teacherIdRaw = rows?.[0]?.teacherId;
    const teacherId = teacherIdRaw === undefined ? NaN : Number(teacherIdRaw);
    if (!Number.isFinite(teacherId) || teacherId <= 0) {
      return null;
    }
    return teacherId;
  }

  /**
   * 通过作业 ID 获取老师的短 UID
   * @param homeworkId - 作业 ID（JoinRoomData.homeworkId）
   * @returns 老师短 UID，未查询到返回 null
   */
  public async getTeacherShortUidByHomeworkId(homeworkId: number): Promise<number | null> {
    const teacherLongUserId = await this.getTeacherLongUserIdByHomeworkId(homeworkId);
    if (!teacherLongUserId) {
      return null;
    }
    return await this.getOrAssignShortUid(teacherLongUserId);
  }

  /**
   * 获取或分配用户的短 UID
   * @param longUserId - 用户的真实 ID（bigint）
   * @returns 分配的短 UID（数据库自增 ID）
   */
  public async getOrAssignShortUid(longUserId: number | string): Promise<number> {
    const userId = typeof longUserId === 'string' ? BigInt(longUserId) : BigInt(longUserId);

    // 尝试获取已存在的记录
    const existingUser = await this.em.findOne(MeetingUser, { longUserId: Number(userId) });

    if (existingUser) {
      // 复用已有的短 UID
      console.log(`[MeetingService] 用户 ${longUserId} 复用短 UID: ${existingUser.id}`);
      return Number(existingUser.id);
    }

    // 创建新记录（数据库自增 ID 会自动分配）
    try {
      const newUser = this.em.create(MeetingUser, { longUserId: Number(userId), createdAt: new Date() });
      await this.em.flush();
      console.log(`[MeetingService] 为用户 ${longUserId} 分配短 UID: ${newUser.id}`);
      return Number(newUser.id);
    } catch (error: any) {
      // 处理并发导致的唯一索引冲突 (MySQL Error 1062: Duplicate entry)
      if (error.code === 'ER_DUP_ENTRY' || error.message?.includes('Duplicate entry')) {
        console.warn(`[MeetingService] 检测到并发创建冲突，重新获取用户 ${longUserId}`);
        this.em.clear(); // 清除当前上下文，防止缓存干扰
        const retryUser = await this.em.findOne(MeetingUser, { longUserId: Number(userId) });
        if (retryUser) {
          return Number(retryUser.id);
        }
      }
      throw error;
    }
  }

  public generateToken(channelName: string, uid: number): TokenResponseDto | null {
    // 从 Nacos 配置获取 appId 和 appCertificate
    const agoraConfig = this.nacosConfig.getDefaultAgoraAppConfig();
    const now = Date.now();
    const role = Role.PUBLISHER;
    const tokenExpirationInSecond = 60 * 60 * 24; // 24 小时
    const privilegeExpirationInSecond = tokenExpirationInSecond;
    const token = RtcTokenBuilder.buildTokenWithUid(
      agoraConfig.appId,
      agoraConfig.appCertificate,
      channelName, // 使用清理后的频道名
      uid,
      role,
      tokenExpirationInSecond,
      privilegeExpirationInSecond
    );

    if (!token) {
      return null;
    }
    const expiresAt = now + tokenExpirationInSecond * 1000;
    return { appid: agoraConfig.appId, rtcToken: token, expiresAt };
  }
}
