import { Body, Controller, Delete, Get, HttpCode, HttpStatus, Param, ParseIntPipe, Post, Query } from '@nestjs/common';
import { ApiParam, ApiQuery, ApiTags } from '@nestjs/swagger';
import { FailResult, OkResult } from '../../common/dto/result.dto';
import { MeetingService } from './meeting.service';
import { NonEmptyStringPipe } from '@/common/pipes/non-empty-string.pipe';
import { Uint32Pipe } from '@/common/pipes/uint32.pipe';
import { ApiCustomOkResponse } from '@/common/decorators/swagger.decorator';
import { BlacklistUserDto, SaveStudentOrderDto, StudentDetailResponseDto, TokenResponseDto } from './meeting.dto';
import { MeetingRedisService } from '../websocket/meeting-redis.service';
import { MeetingWebSocketGateway } from '../websocket/meeting.websocket';

/**
 * 会议控制器
 * 处理会议相关的 API 请求
 */
@ApiTags('Meeting')
@Controller('meeting')
export class MeetingController {
  public constructor(
    private readonly meetingService: MeetingService,
    private readonly redis: MeetingRedisService,
    private readonly websocketGateway: MeetingWebSocketGateway // 注入 WebSocket Gateway
  ) {}

  /**
   * 生成声网 RTC Token
   */
  @Get('get-token/:channelName/:uid')
  @HttpCode(HttpStatus.OK)
  @ApiCustomOkResponse({
    summary: '声网 RTC Token',
    model: TokenResponseDto,
    apiDescription: '声网 RTC Token 信息',
    resDescription: '声网 RTC Token 信息包含appid',
  })
  @ApiParam({ name: 'channelName', description: '需要加入的频道名(格式: `n_课程名称`)', example: 'n_1234567890' })
  @ApiParam({ name: 'uid', description: '用户的短 UID(0~4294967295)', example: '1001' })
  public async getToken(@Param('channelName', NonEmptyStringPipe) channelName: string, @Param('uid', Uint32Pipe) uid: number) {
    const result = this.meetingService.generateToken(channelName, uid);
    if (!result) {
      return new FailResult('生成 Token 失败');
    }
    return new OkResult(result);
  }

  /**
   * 查询房间黑名单（包含短 UID 和名称的数组）
   */
  @Get('blacklist/:roomId')
  @HttpCode(HttpStatus.OK)
  @ApiParam({ name: 'roomId', description: '服务端房间 ID（Socket.IO 房间名）' })
  @ApiCustomOkResponse({
    summary: '房间黑名单',
    model: [BlacklistUserDto],
    apiDescription: '房间黑名单（包含短 UID 和名称的数组）',
    resDescription: '房间黑名单（包含短 UID 和名称的数组）',
  })
  public async getBlacklist(@Param('roomId', NonEmptyStringPipe) roomId: string) {
    const list = await this.redis.getBlacklist(roomId);
    return new OkResult(list);
  }

  /**
   * 从房间黑名单移除指定用户 (短 UID)
   */
  @Delete('blacklist/:roomId/:shortUid')
  @HttpCode(HttpStatus.OK)
  @ApiParam({ name: 'roomId', description: '服务端房间 ID（Socket.IO 房间名）' })
  @ApiParam({ name: 'shortUid', description: '用户短 UID', example: '1001' })
  @ApiCustomOkResponse({
    summary: '从房间黑名单移除用户',
    model: Boolean,
    apiDescription: '从房间黑名单移除指定用户（短 UID）',
    resDescription: '是否成功移除用户',
  })
  public async removeFromBlacklist(@Param('roomId', NonEmptyStringPipe) roomId: string, @Param('shortUid', Uint32Pipe) shortUid: number) {
    await this.redis.removeFromBlacklist(roomId, shortUid);
    return new OkResult(true);
  }

  /**
   * 通过作业 ID 查询学生详细信息列表
   */
  @Get('students')
  @HttpCode(HttpStatus.OK)
  @ApiQuery({ name: 'homeworkId', description: '作业 ID', example: '1234567890' })
  @ApiQuery({ name: 'roomId', description: '房间 ID (可选，用于获取 Redis 中的排序)', required: false, example: 'n_1234567890' })
  @ApiCustomOkResponse({
    summary: '学生详细信息列表',
    model: [StudentDetailResponseDto],
    apiDescription: '学生详细信息列表，包含长 ID、短 ID (可能为 null) 和姓名',
    resDescription: '学生详细信息列表',
  })
  public async getStudentDetails(@Query('homeworkId', ParseIntPipe) homeworkId: number, @Query('roomId', NonEmptyStringPipe) roomId?: string) {
    const students = await this.meetingService.getStudentDetailsByHomeworkId(homeworkId, roomId);
    return new OkResult(students);
  }

  /**
   * 保存学生排序顺序
   */
  @Post('student-order')
  @HttpCode(HttpStatus.OK)
  @ApiCustomOkResponse({
    summary: '保存学生排序顺序',
    model: Boolean,
    apiDescription: '保存学生排序顺序到 Redis 并发送给监控端',
    resDescription: '是否成功保存',
  })
  public async saveStudentOrder(@Body() dto: SaveStudentOrderDto) {
    // 1. 保存到 Redis
    await this.meetingService.saveStudentOrder(dto.roomId, dto.studentShortIds);

    // 2. 获取房间内所有 socket 并过滤出监控端 (monitor 模式)
    const ns = this.websocketGateway.getServer();
    if (ns) {
      const sockets = await ns.in(dto.roomId).fetchSockets();
      const monitorSocketIds = sockets.filter((socket) => socket.data.joinMode === 'monitor').map((socket) => socket.id);

      if (monitorSocketIds.length > 0) {
        // 发送给所有监控端的 socket
        ns.to(monitorSocketIds).emit('message', {
          type: 'sev_student_order_changed',
          data: {
            fromRoomId: dto.roomId,
            studentShortIds: dto.studentShortIds,
          },
        });
      }
    }

    return new OkResult(true);
  }
}
