/**
 * WebSocket消息数据传输对象
 *
 * 功能描述：
 * - 定义WebSocket通信的消息格式和验证规则
 * - 提供客户端和服务端之间的数据交换标准
 * - 支持位置广播系统的实时通信需求
 * - 实现消息类型的统一管理和验证
 *
 * 职责分离：
 * - 消息格式：定义WebSocket消息的标准结构
 * - 数据验证：使用class-validator进行输入验证
 * - 类型安全：提供TypeScript类型约束
 * - 接口规范：统一的消息交换格式
 *
 * 最近修改：
 * - 2026-01-08: 功能新增 - 创建WebSocket消息DTO，支持位置广播系统
 *
 * @author moyin
 * @version 1.0.0
 * @since 2026-01-08
 * @lastModified 2026-01-08
 */

import { IsString, IsNumber, IsNotEmpty, IsOptional, IsObject, Length } from 'class-validator';
import { Type } from 'class-transformer';
import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger';

/**
 * 加入会话消息DTO
 *
 * 职责：
 * - 定义用户加入游戏会话的请求数据
 * - 验证会话ID和认证token的格式
 * - 支持可选的初始位置设置
 */
export class JoinSessionMessage {
  /**
   * 消息类型标识
   */
  @ApiProperty({
    description: '消息类型',
    example: 'join_session',
    enum: ['join_session']
  })
  @IsString({ message: '消息类型必须是字符串' })
  @IsOptional()
  type?: 'join_session' = 'join_session';

  /**
   * 游戏会话ID
   */
  @ApiProperty({
    description: '游戏会话ID',
    example: 'session_12345',
    minLength: 1,
    maxLength: 100
  })
  @IsString({ message: '会话ID必须是字符串' })
  @IsNotEmpty({ message: '会话ID不能为空' })
  @Length(1, 100, { message: '会话ID长度必须在1-100个字符之间' })
  sessionId: string;

  /**
   * JWT认证token
   */
  @ApiProperty({
    description: 'JWT认证token',
    example: 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...'
  })
  @IsString({ message: 'Token必须是字符串' })
  @IsNotEmpty({ message: 'Token不能为空' })
  token: string;

  /**
   * 会话密码（可选）
   */
  @ApiPropertyOptional({
    description: '会话密码（如果会话需要密码）',
    example: 'password123'
  })
  @IsOptional()
  @IsString({ message: '会话密码必须是字符串' })
  password?: string;

  /**
   * 初始位置（可选）
   */
  @ApiPropertyOptional({
    description: '用户初始位置',
    example: {
      mapId: 'plaza',
      x: 100,
      y: 200
    }
  })
  @IsOptional()
  @IsObject({ message: '初始位置必须是对象格式' })
  initialPosition?: {
    mapId: string;
    x: number;
    y: number;
  };
}

/**
 * 离开会话消息DTO
 *
 * 职责：
 * - 定义用户离开游戏会话的请求数据
 * - 支持主动离开和被动断开的区分
 * - 提供离开原因的记录
 */
export class LeaveSessionMessage {
  /**
   * 消息类型标识
   */
  @ApiProperty({
    description: '消息类型',
    example: 'leave_session',
    enum: ['leave_session']
  })
  @IsString({ message: '消息类型必须是字符串' })
  @IsOptional()
  type?: 'leave_session' = 'leave_session';

  /**
   * 游戏会话ID
   */
  @ApiProperty({
    description: '游戏会话ID',
    example: 'session_12345'
  })
  @IsString({ message: '会话ID必须是字符串' })
  @IsNotEmpty({ message: '会话ID不能为空' })
  sessionId: string;

  /**
   * 离开原因（可选）
   */
  @ApiPropertyOptional({
    description: '离开原因',
    example: 'user_left',
    enum: ['user_left', 'connection_lost', 'kicked', 'error']
  })
  @IsOptional()
  @IsString({ message: '离开原因必须是字符串' })
  reason?: string;
}

/**
 * 位置更新消息DTO
 *
 * 职责：
 * - 定义用户位置更新的请求数据
 * - 验证位置坐标和地图ID的有效性
 * - 支持位置元数据的扩展
 */
export class PositionUpdateMessage {
  /**
   * 消息类型标识
   */
  @ApiProperty({
    description: '消息类型',
    example: 'position_update',
    enum: ['position_update']
  })
  @IsString({ message: '消息类型必须是字符串' })
  @IsOptional()
  type?: 'position_update' = 'position_update';

  /**
   * 地图ID
   */
  @ApiProperty({
    description: '地图ID',
    example: 'plaza',
    minLength: 1,
    maxLength: 50
  })
  @IsString({ message: '地图ID必须是字符串' })
  @IsNotEmpty({ message: '地图ID不能为空' })
  @Length(1, 50, { message: '地图ID长度必须在1-50个字符之间' })
  mapId: string;

  /**
   * X轴坐标
   */
  @ApiProperty({
    description: 'X轴坐标',
    example: 100.5,
    type: 'number'
  })
  @IsNumber({}, { message: 'X坐标必须是数字' })
  @Type(() => Number)
  x: number;

  /**
   * Y轴坐标
   */
  @ApiProperty({
    description: 'Y轴坐标',
    example: 200.3,
    type: 'number'
  })
  @IsNumber({}, { message: 'Y坐标必须是数字' })
  @Type(() => Number)
  y: number;

  /**
   * 时间戳（可选，服务端会自动设置）
   */
  @ApiPropertyOptional({
    description: '位置更新时间戳',
    example: 1641024000000
  })
  @IsOptional()
  @IsNumber({}, { message: '时间戳必须是数字' })
  @Type(() => Number)
  timestamp?: number;

  /**
   * 扩展元数据（可选）
   */
  @ApiPropertyOptional({
    description: '位置扩展元数据',
    example: {
      speed: 5.2,
      direction: 'north'
    }
  })
  @IsOptional()
  @IsObject({ message: '元数据必须是对象格式' })
  metadata?: Record<string, any>;
}

/**
 * 心跳消息DTO
 *
 * 职责：
 * - 定义WebSocket连接的心跳检测消息
 * - 维持连接活跃状态
 * - 检测连接质量和延迟
 */
export class HeartbeatMessage {
  /**
   * 消息类型标识
   */
  @ApiProperty({
    description: '消息类型',
    example: 'heartbeat',
    enum: ['heartbeat']
  })
  @IsString({ message: '消息类型必须是字符串' })
  @IsOptional()
  type?: 'heartbeat' = 'heartbeat';

  /**
   * 客户端时间戳
   */
  @ApiProperty({
    description: '客户端发送时间戳',
    example: 1641024000000
  })
  @IsNumber({}, { message: '时间戳必须是数字' })
  @Type(() => Number)
  timestamp: number;

  /**
   * 序列号（可选）
   */
  @ApiPropertyOptional({
    description: '心跳序列号',
    example: 1
  })
  @IsOptional()
  @IsNumber({}, { message: '序列号必须是数字' })
  @Type(() => Number)
  sequence?: number;
}

/**
 * 通用WebSocket消息DTO
 *
 * 职责：
 * - 定义所有WebSocket消息的基础结构
 * - 提供消息类型的统一管理
 * - 支持消息的路由和处理
 */
export class WebSocketMessage {
  /**
   * 消息类型
   */
  @ApiProperty({
    description: '消息类型',
    example: 'join_session',
    enum: ['join_session', 'leave_session', 'position_update', 'heartbeat']
  })
  @IsString({ message: '消息类型必须是字符串' })
  @IsNotEmpty({ message: '消息类型不能为空' })
  type: string;

  /**
   * 消息数据
   */
  @ApiProperty({
    description: '消息数据',
    example: {}
  })
  @IsObject({ message: '消息数据必须是对象格式' })
  data: any;

  /**
   * 消息ID（可选）
   */
  @ApiPropertyOptional({
    description: '消息唯一标识',
    example: 'msg_12345'
  })
  @IsOptional()
  @IsString({ message: '消息ID必须是字符串' })
  messageId?: string;

  /**
   * 时间戳
   */
  @ApiProperty({
    description: '消息时间戳',
    example: 1641024000000
  })
  @IsNumber({}, { message: '时间戳必须是数字' })
  @Type(() => Number)
  timestamp: number;
}