/**
 * API数据传输对象
 *
 * 功能描述：
 * - 定义HTTP API的请求和响应数据格式
 * - 提供数据验证规则和类型约束
 * - 支持Swagger API文档自动生成
 * - 实现统一的API数据交换标准
 *
 * 职责分离：
 * - 请求验证：HTTP请求数据的格式验证
 * - 类型安全：TypeScript类型约束和检查
 * - 文档生成：Swagger API文档的自动生成
 * - 数据转换：前端和后端数据格式的标准化
 *
 * 最近修改：
 * - 2026-01-08: 功能新增 - 创建API DTO，支持位置广播系统
 *
 * @author moyin
 * @version 1.0.0
 * @since 2026-01-08
 * @lastModified 2026-01-08
 */

import { IsString, IsNumber, IsOptional, IsBoolean, IsArray, Length, Min, Max, IsEnum } from 'class-validator';
import { Type, Transform } from 'class-transformer';
import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger';

/**
 * 创建会话DTO
 */
export class CreateSessionDto {
  @ApiProperty({
    description: '会话ID',
    example: 'session_12345',
    minLength: 1,
    maxLength: 100
  })
  @IsString({ message: '会话ID必须是字符串' })
  @Length(1, 100, { message: '会话ID长度必须在1-100个字符之间' })
  sessionId: string;

  @ApiPropertyOptional({
    description: '会话名称',
    example: '我的游戏会话'
  })
  @IsOptional()
  @IsString({ message: '会话名称必须是字符串' })
  @Length(1, 200, { message: '会话名称长度必须在1-200个字符之间' })
  name?: string;

  @ApiPropertyOptional({
    description: '会话描述',
    example: '这是一个多人游戏会话'
  })
  @IsOptional()
  @IsString({ message: '会话描述必须是字符串' })
  @Length(0, 500, { message: '会话描述长度不能超过500个字符' })
  description?: string;

  @ApiPropertyOptional({
    description: '最大用户数',
    example: 100,
    minimum: 1,
    maximum: 1000
  })
  @IsOptional()
  @IsNumber({}, { message: '最大用户数必须是数字' })
  @Min(1, { message: '最大用户数不能小于1' })
  @Max(1000, { message: '最大用户数不能超过1000' })
  @Type(() => Number)
  maxUsers?: number;

  @ApiPropertyOptional({
    description: '是否允许观察者',
    example: true
  })
  @IsOptional()
  @IsBoolean({ message: '允许观察者必须是布尔值' })
  allowObservers?: boolean;

  @ApiPropertyOptional({
    description: '会话密码',
    example: 'password123'
  })
  @IsOptional()
  @IsString({ message: '会话密码必须是字符串' })
  @Length(1, 50, { message: '会话密码长度必须在1-50个字符之间' })
  password?: string;

  @ApiPropertyOptional({
    description: '允许的地图列表',
    example: ['plaza', 'forest', 'mountain'],
    type: [String]
  })
  @IsOptional()
  @IsArray({ message: '允许的地图必须是数组' })
  @IsString({ each: true, message: '地图ID必须是字符串' })
  allowedMaps?: string[];

  @ApiPropertyOptional({
    description: '广播范围（像素）',
    example: 1000,
    minimum: 0,
    maximum: 10000
  })
  @IsOptional()
  @IsNumber({}, { message: '广播范围必须是数字' })
  @Min(0, { message: '广播范围不能小于0' })
  @Max(10000, { message: '广播范围不能超过10000' })
  @Type(() => Number)
  broadcastRange?: number;

  @ApiPropertyOptional({
    description: '扩展元数据',
    example: { theme: 'dark', language: 'zh-CN' }
  })
  @IsOptional()
  metadata?: Record<string, any>;
}

/**
 * 加入会话DTO
 */
export class JoinSessionDto {
  @ApiProperty({
    description: '会话ID',
    example: 'session_12345'
  })
  @IsString({ message: '会话ID必须是字符串' })
  @Length(1, 100, { message: '会话ID长度必须在1-100个字符之间' })
  sessionId: string;

  @ApiPropertyOptional({
    description: '会话密码',
    example: 'password123'
  })
  @IsOptional()
  @IsString({ message: '会话密码必须是字符串' })
  password?: string;

  @ApiPropertyOptional({
    description: '初始位置',
    example: {
      mapId: 'plaza',
      x: 100,
      y: 200
    }
  })
  @IsOptional()
  initialPosition?: {
    mapId: string;
    x: number;
    y: number;
  };
}

/**
 * 更新位置DTO
 */
export class UpdatePositionDto {
  @ApiProperty({
    description: '地图ID',
    example: 'plaza'
  })
  @IsString({ message: '地图ID必须是字符串' })
  @Length(1, 50, { message: '地图ID长度必须在1-50个字符之间' })
  mapId: string;

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

  @ApiProperty({
    description: 'Y轴坐标',
    example: 200.3
  })
  @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()
  metadata?: Record<string, any>;
}

/**
 * 会话查询DTO
 */
export class SessionQueryDto {
  @ApiPropertyOptional({
    description: '会话状态',
    example: 'active',
    enum: ['active', 'idle', 'paused', 'ended']
  })
  @IsOptional()
  @IsEnum(['active', 'idle', 'paused', 'ended'], { message: '会话状态值无效' })
  status?: string;

  @ApiPropertyOptional({
    description: '最小用户数',
    example: 1,
    minimum: 0
  })
  @IsOptional()
  @IsNumber({}, { message: '最小用户数必须是数字' })
  @Min(0, { message: '最小用户数不能小于0' })
  @Type(() => Number)
  minUsers?: number;

  @ApiPropertyOptional({
    description: '最大用户数',
    example: 100,
    minimum: 1
  })
  @IsOptional()
  @IsNumber({}, { message: '最大用户数必须是数字' })
  @Min(1, { message: '最大用户数不能小于1' })
  @Type(() => Number)
  maxUsers?: number;

  @ApiPropertyOptional({
    description: '只显示公开会话',
    example: true
  })
  @IsOptional()
  @IsBoolean({ message: '公开会话标志必须是布尔值' })
  @Transform(({ value }) => value === 'true' || value === true)
  publicOnly?: boolean;

  @ApiPropertyOptional({
    description: '创建者ID',
    example: 'user123'
  })
  @IsOptional()
  @IsString({ message: '创建者ID必须是字符串' })
  creatorId?: string;

  @ApiPropertyOptional({
    description: '分页偏移',
    example: 0,
    minimum: 0
  })
  @IsOptional()
  @IsNumber({}, { message: '分页偏移必须是数字' })
  @Min(0, { message: '分页偏移不能小于0' })
  @Type(() => Number)
  offset?: number;

  @ApiPropertyOptional({
    description: '分页大小',
    example: 10,
    minimum: 1,
    maximum: 100
  })
  @IsOptional()
  @IsNumber({}, { message: '分页大小必须是数字' })
  @Min(1, { message: '分页大小不能小于1' })
  @Max(100, { message: '分页大小不能超过100' })
  @Type(() => Number)
  limit?: number;
}

/**
 * 位置查询DTO
 */
export class PositionQueryDto {
  @ApiPropertyOptional({
    description: '用户ID列表（逗号分隔）',
    example: 'user1,user2,user3'
  })
  @IsOptional()
  @IsString({ message: '用户ID列表必须是字符串' })
  userIds?: string;

  @ApiPropertyOptional({
    description: '地图ID',
    example: 'plaza'
  })
  @IsOptional()
  @IsString({ message: '地图ID必须是字符串' })
  mapId?: string;

  @ApiPropertyOptional({
    description: '会话ID',
    example: 'session_12345'
  })
  @IsOptional()
  @IsString({ message: '会话ID必须是字符串' })
  sessionId?: string;

  @ApiPropertyOptional({
    description: '范围查询中心X坐标',
    example: 100
  })
  @IsOptional()
  @IsNumber({}, { message: '中心X坐标必须是数字' })
  @Type(() => Number)
  centerX?: number;

  @ApiPropertyOptional({
    description: '范围查询中心Y坐标',
    example: 200
  })
  @IsOptional()
  @IsNumber({}, { message: '中心Y坐标必须是数字' })
  @Type(() => Number)
  centerY?: number;

  @ApiPropertyOptional({
    description: '范围查询半径',
    example: 500,
    minimum: 0,
    maximum: 10000
  })
  @IsOptional()
  @IsNumber({}, { message: '查询半径必须是数字' })
  @Min(0, { message: '查询半径不能小于0' })
  @Max(10000, { message: '查询半径不能超过10000' })
  @Type(() => Number)
  radius?: number;

  @ApiPropertyOptional({
    description: '分页偏移',
    example: 0,
    minimum: 0
  })
  @IsOptional()
  @IsNumber({}, { message: '分页偏移必须是数字' })
  @Min(0, { message: '分页偏移不能小于0' })
  @Type(() => Number)
  offset?: number;

  @ApiPropertyOptional({
    description: '分页大小',
    example: 50,
    minimum: 1,
    maximum: 1000
  })
  @IsOptional()
  @IsNumber({}, { message: '分页大小必须是数字' })
  @Min(1, { message: '分页大小不能小于1' })
  @Max(1000, { message: '分页大小不能超过1000' })
  @Type(() => Number)
  limit?: number;
}

/**
 * 更新会话配置DTO
 */
export class UpdateSessionConfigDto {
  @ApiPropertyOptional({
    description: '最大用户数',
    example: 150,
    minimum: 1,
    maximum: 1000
  })
  @IsOptional()
  @IsNumber({}, { message: '最大用户数必须是数字' })
  @Min(1, { message: '最大用户数不能小于1' })
  @Max(1000, { message: '最大用户数不能超过1000' })
  @Type(() => Number)
  maxUsers?: number;

  @ApiPropertyOptional({
    description: '是否允许观察者',
    example: false
  })
  @IsOptional()
  @IsBoolean({ message: '允许观察者必须是布尔值' })
  allowObservers?: boolean;

  @ApiPropertyOptional({
    description: '会话密码',
    example: 'newpassword123'
  })
  @IsOptional()
  @IsString({ message: '会话密码必须是字符串' })
  @Length(0, 50, { message: '会话密码长度不能超过50个字符' })
  password?: string;

  @ApiPropertyOptional({
    description: '允许的地图列表',
    example: ['plaza', 'forest'],
    type: [String]
  })
  @IsOptional()
  @IsArray({ message: '允许的地图必须是数组' })
  @IsString({ each: true, message: '地图ID必须是字符串' })
  allowedMaps?: string[];

  @ApiPropertyOptional({
    description: '广播范围（像素）',
    example: 1500,
    minimum: 0,
    maximum: 10000
  })
  @IsOptional()
  @IsNumber({}, { message: '广播范围必须是数字' })
  @Min(0, { message: '广播范围不能小于0' })
  @Max(10000, { message: '广播范围不能超过10000' })
  @Type(() => Number)
  broadcastRange?: number;

  @ApiPropertyOptional({
    description: '是否公开',
    example: true
  })
  @IsOptional()
  @IsBoolean({ message: '公开标志必须是布尔值' })
  isPublic?: boolean;

  @ApiPropertyOptional({
    description: '自动清理时间（分钟）',
    example: 120,
    minimum: 1,
    maximum: 1440
  })
  @IsOptional()
  @IsNumber({}, { message: '自动清理时间必须是数字' })
  @Min(1, { message: '自动清理时间不能小于1分钟' })
  @Max(1440, { message: '自动清理时间不能超过1440分钟（24小时）' })
  @Type(() => Number)
  autoCleanupMinutes?: number;
}

/**
 * 通用API响应DTO
 */
export class ApiResponseDto<T = any> {
  @ApiProperty({
    description: '操作是否成功',
    example: true
  })
  success: boolean;

  @ApiPropertyOptional({
    description: '响应数据'
  })
  data?: T;

  @ApiPropertyOptional({
    description: '响应消息',
    example: '操作成功'
  })
  message?: string;

  @ApiPropertyOptional({
    description: '错误信息',
    example: '参数验证失败'
  })
  error?: string;

  @ApiPropertyOptional({
    description: '响应时间戳',
    example: 1641024000000
  })
  timestamp?: number;
}

/**
 * 分页响应DTO
 */
export class PaginatedResponseDto<T = any> {
  @ApiProperty({
    description: '数据列表',
    type: 'array'
  })
  items: T[];

  @ApiProperty({
    description: '总记录数',
    example: 100
  })
  total: number;

  @ApiProperty({
    description: '当前页码',
    example: 1
  })
  page: number;

  @ApiProperty({
    description: '每页大小',
    example: 10
  })
  pageSize: number;

  @ApiProperty({
    description: '总页数',
    example: 10
  })
  totalPages: number;

  @ApiProperty({
    description: '是否有下一页',
    example: true
  })
  hasNext: boolean;

  @ApiProperty({
    description: '是否有上一页',
    example: false
  })
  hasPrev: boolean;
}