/**
 * 位置广播业务服务
 * 
 * 功能描述：
 * - 提供位置广播系统的主要业务逻辑
 * - 协调会话管理和位置更新的业务流程
 * - 处理业务规则验证和权限检查
 * - 为控制器层提供统一的业务接口
 * 
 * 职责分离：
 * - 业务逻辑：实现位置广播的核心业务规则
 * - 数据协调：协调核心服务层的数据操作
 * - 权限验证：处理用户权限和业务规则验证
 * - 异常处理：统一的业务异常处理和转换
 * 
 * 技术实现：
 * - 依赖注入：使用核心服务层提供的基础功能
 * - 业务验证：实现复杂的业务规则和数据验证
 * - 事务管理：确保数据操作的一致性
 * - 性能优化：批量操作和缓存策略
 * 
 * 最近修改：
 * - 2026-01-08: 代码重构 - 提取魔法数字为常量，优化代码质量 (修改者: moyin)
 * 
 * @author moyin
 * @version 1.2.0
 * @since 2026-01-08
 * @lastModified 2026-01-08
 */

import { Injectable, Inject, Logger, BadRequestException, NotFoundException, ForbiddenException } from '@nestjs/common';
import { Position } from '../../../core/location_broadcast_core/position.interface';
import { GameSession, SessionUser, SessionStatus } from '../../../core/location_broadcast_core/session.interface';

/**
 * 加入会话请求DTO
 */
export interface JoinSessionRequest {
  /** 用户ID */
  userId: string;
  /** 会话ID */
  sessionId: string;
  /** Socket连接ID */
  socketId: string;
  /** 初始位置（可选） */
  initialPosition?: {
    mapId: string;
    x: number;
    y: number;
  };
  /** 会话密码（可选） */
  password?: string;
}

/**
 * 加入会话响应DTO
 */
export interface JoinSessionResponse {
  /** 是否成功 */
  success: boolean;
  /** 会话信息 */
  session: GameSession;
  /** 会话中的用户列表 */
  users: SessionUser[];
  /** 其他用户的位置信息 */
  positions: Position[];
  /** 响应消息 */
  message: string;
}

/**
 * 位置更新请求DTO
 */
export interface PositionUpdateRequest {
  /** 用户ID */
  userId: string;
  /** 位置信息 */
  position: {
    mapId: string;
    x: number;
    y: number;
    timestamp?: number;
    metadata?: Record<string, any>;
  };
}

/**
 * 位置更新响应DTO
 */
export interface PositionUpdateResponse {
  /** 是否成功 */
  success: boolean;
  /** 更新后的位置 */
  position: Position;
  /** 需要广播的用户列表 */
  broadcastTargets: string[];
  /** 响应消息 */
  message: string;
}

/**
 * 会话统计信息DTO
 */
export interface SessionStatsResponse {
  /** 会话ID */
  sessionId: string;
  /** 在线用户数 */
  onlineUsers: number;
  /** 总用户数 */
  totalUsers: number;
  /** 活跃地图列表 */
  activeMaps: string[];
  /** 会话创建时间 */
  createdAt: number;
  /** 最后活动时间 */
  lastActivity: number;
}

@Injectable()
export class LocationBroadcastService {
  private readonly logger = new Logger(LocationBroadcastService.name);

  /** 坐标最大值 */
  private static readonly MAX_COORDINATE = 999999;
  /** 坐标最小值 */
  private static readonly MIN_COORDINATE = -999999;
  /** 默认会话配置 */
  private static readonly DEFAULT_MAX_USERS = 100;
  private static readonly DEFAULT_TIMEOUT_SECONDS = 3600;
  private static readonly DEFAULT_BROADCAST_RANGE = 1000;
  /** 会话ID最大长度 */
  private static readonly MAX_SESSION_ID_LENGTH = 100;

  constructor(
    @Inject('ILocationBroadcastCore')
    private readonly locationBroadcastCore: any,
    @Inject('IUserPositionCore')
    private readonly userPositionCore: any,
  ) {}

  /**
   * 用户加入会话
   * 
   * 业务逻辑：
   * 1. 验证会话是否存在和可加入
   * 2. 检查用户权限和会话容量
   * 3. 处理用户从其他会话的迁移
   * 4. 设置初始位置（如果提供）
   * 5. 返回完整的会话状态
   * 
   * @param request 加入会话请求
   * @returns 加入会话响应
   */
  async joinSession(request: JoinSessionRequest): Promise<JoinSessionResponse> {
    const startTime = Date.now();
    
    this.logger.log('处理用户加入会话业务逻辑', {
      operation: 'joinSession',
      userId: request.userId,
      sessionId: request.sessionId,
      socketId: request.socketId,
      hasInitialPosition: !!request.initialPosition,
      timestamp: new Date().toISOString()
    });

    try {
      // 1. 验证请求参数
      this.validateJoinSessionRequest(request);

      // 2. 检查用户是否已在其他会话中
      await this.handleUserSessionMigration(request.userId, request.sessionId);

      // 3. 将用户添加到会话
      await this.locationBroadcastCore.addUserToSession(
        request.sessionId,
        request.userId,
        request.socketId
      );

      // 4. 设置初始位置（如果提供）
      if (request.initialPosition) {
        const position: Position = {
          userId: request.userId,
          x: request.initialPosition.x,
          y: request.initialPosition.y,
          mapId: request.initialPosition.mapId,
          timestamp: Date.now(),
          metadata: {}
        };

        await this.locationBroadcastCore.setUserPosition(request.userId, position);
      }

      // 5. 获取会话完整状态
      const [sessionUsers, sessionPositions] = await Promise.all([
        this.locationBroadcastCore.getSessionUsers(request.sessionId),
        this.locationBroadcastCore.getSessionPositions(request.sessionId)
      ]);

      // 6. 构建会话信息
      const session: GameSession = {
        sessionId: request.sessionId,
        users: sessionUsers,
        createdAt: Date.now(), // 这里应该从实际存储中获取
        lastActivity: Date.now(),
        status: SessionStatus.ACTIVE,
        config: {
          maxUsers: LocationBroadcastService.DEFAULT_MAX_USERS,
          timeoutSeconds: LocationBroadcastService.DEFAULT_TIMEOUT_SECONDS,
          allowObservers: true,
          requirePassword: false,
          broadcastRange: LocationBroadcastService.DEFAULT_BROADCAST_RANGE
        },
        metadata: {}
      };

      const duration = Date.now() - startTime;

      this.logger.log('用户加入会话业务处理成功', {
        operation: 'joinSession',
        userId: request.userId,
        sessionId: request.sessionId,
        userCount: sessionUsers.length,
        positionCount: sessionPositions.length,
        duration,
        timestamp: new Date().toISOString()
      });

      return {
        success: true,
        session,
        users: sessionUsers,
        positions: sessionPositions,
        message: '成功加入会话'
      };

    } catch (error) {
      const duration = Date.now() - startTime;
      
      this.logger.error('用户加入会话业务处理失败', {
        operation: 'joinSession',
        userId: request.userId,
        sessionId: request.sessionId,
        error: error instanceof Error ? error.message : String(error),
        duration,
        timestamp: new Date().toISOString()
      }, error instanceof Error ? error.stack : undefined);

      throw error;
    }
  }

  /**
   * 用户离开会话
   * 
   * 业务逻辑：
   * 1. 验证用户是否在指定会话中
   * 2. 处理位置数据的持久化
   * 3. 从会话中移除用户
   * 4. 清理相关缓存数据
   * 5. 返回操作结果
   * 
   * @param userId 用户ID
   * @param sessionId 会话ID
   * @param reason 离开原因
   * @returns 操作是否成功
   */
  async leaveSession(userId: string, sessionId: string, reason: string = 'user_left'): Promise<boolean> {
    const startTime = Date.now();
    
    this.logger.log('处理用户离开会话业务逻辑', {
      operation: 'leaveSession',
      userId,
      sessionId,
      reason,
      timestamp: new Date().toISOString()
    });

    try {
      // 1. 验证参数
      if (!userId || !sessionId) {
        throw new BadRequestException('用户ID和会话ID不能为空');
      }

      // 2. 获取用户当前位置并持久化
      const currentPosition = await this.locationBroadcastCore.getUserPosition(userId);
      if (currentPosition) {
        await this.userPositionCore.saveUserPosition(userId, currentPosition);
      }

      // 3. 从会话中移除用户
      await this.locationBroadcastCore.removeUserFromSession(sessionId, userId);

      const duration = Date.now() - startTime;

      this.logger.log('用户离开会话业务处理成功', {
        operation: 'leaveSession',
        userId,
        sessionId,
        reason,
        hadPosition: !!currentPosition,
        duration,
        timestamp: new Date().toISOString()
      });

      return true;

    } catch (error) {
      const duration = Date.now() - startTime;
      
      this.logger.error('用户离开会话业务处理失败', {
        operation: 'leaveSession',
        userId,
        sessionId,
        reason,
        error: error instanceof Error ? error.message : String(error),
        duration,
        timestamp: new Date().toISOString()
      }, error instanceof Error ? error.stack : undefined);

      throw error;
    }
  }

  /**
   * 更新用户位置
   * 
   * 业务逻辑：
   * 1. 验证位置数据的有效性
   * 2. 检查用户权限和地图限制
   * 3. 更新Redis缓存中的位置
   * 4. 确定需要广播的目标用户
   * 5. 可选：触发位置历史记录
   * 
   * @param request 位置更新请求
   * @returns 位置更新响应
   */
  async updatePosition(request: PositionUpdateRequest): Promise<PositionUpdateResponse> {
    const startTime = Date.now();
    
    this.logger.debug('处理位置更新业务逻辑', {
      operation: 'updatePosition',
      userId: request.userId,
      mapId: request.position.mapId,
      x: request.position.x,
      y: request.position.y,
      timestamp: new Date().toISOString()
    });

    try {
      // 1. 验证位置数据
      this.validatePositionData(request.position);

      // 2. 构建位置对象
      const position: Position = {
        userId: request.userId,
        x: request.position.x,
        y: request.position.y,
        mapId: request.position.mapId,
        timestamp: request.position.timestamp || Date.now(),
        metadata: request.position.metadata || {}
      };

      // 3. 更新位置缓存
      await this.locationBroadcastCore.setUserPosition(request.userId, position);

      // 获取需要广播的目标用户
      const broadcastTargets = await this.getBroadcastTargets(request.userId, position.mapId);

      // 5. 可选：保存位置历史（每隔一定时间或距离）
      if (this.shouldSavePositionHistory(position)) {
        try {
          await this.userPositionCore.savePositionHistory(request.userId, position);
        } catch (error) {
          // 历史记录保存失败不影响主流程
          this.logger.warn('位置历史记录保存失败', {
            userId: request.userId,
            error: error instanceof Error ? error.message : String(error)
          });
        }
      }

      const duration = Date.now() - startTime;

      this.logger.debug('位置更新业务处理成功', {
        operation: 'updatePosition',
        userId: request.userId,
        mapId: position.mapId,
        broadcastTargetCount: broadcastTargets.length,
        duration,
        timestamp: new Date().toISOString()
      });

      return {
        success: true,
        position,
        broadcastTargets,
        message: '位置更新成功'
      };

    } catch (error) {
      const duration = Date.now() - startTime;
      
      this.logger.error('位置更新业务处理失败', {
        operation: 'updatePosition',
        userId: request.userId,
        mapId: request.position.mapId,
        error: error instanceof Error ? error.message : String(error),
        duration,
        timestamp: new Date().toISOString()
      }, error instanceof Error ? error.stack : undefined);

      throw error;
    }
  }

  /**
   * 获取会话统计信息
   * 
   * @param sessionId 会话ID
   * @returns 会话统计信息
   */
  async getSessionStats(sessionId: string): Promise<SessionStatsResponse> {
    try {
      const [sessionUsers, sessionPositions] = await Promise.all([
        this.locationBroadcastCore.getSessionUsers(sessionId),
        this.locationBroadcastCore.getSessionPositions(sessionId)
      ]);

      // 统计活跃地图
      const activeMaps = [...new Set(sessionPositions.map(pos => pos.mapId as string))];

      return {
        sessionId,
        onlineUsers: sessionUsers.length,
        totalUsers: sessionUsers.length, // 这里可以从数据库获取历史总数
        activeMaps: activeMaps as string[],
        createdAt: Date.now(), // 这里应该从实际存储中获取
        lastActivity: Date.now()
      };

    } catch (error) {
      this.logger.error('获取会话统计信息失败', {
        operation: 'getSessionStats',
        sessionId,
        error: error instanceof Error ? error.message : String(error)
      });

      throw new NotFoundException('会话不存在或获取统计信息失败');
    }
  }

  /**
   * 获取地图中的所有用户位置
   * 
   * @param mapId 地图ID
   * @returns 位置列表
   */
  async getMapPositions(mapId: string): Promise<Position[]> {
    try {
      return await this.locationBroadcastCore.getMapPositions(mapId);
    } catch (error) {
      this.logger.error('获取地图位置信息失败', {
        operation: 'getMapPositions',
        mapId,
        error: error instanceof Error ? error.message : String(error)
      });

      return [];
    }
  }

  /**
   * 清理用户数据
   * 
   * @param userId 用户ID
   * @returns 清理是否成功
   */
  async cleanupUserData(userId: string): Promise<boolean> {
    try {
      await this.locationBroadcastCore.cleanupUserData(userId);
      return true;
    } catch (error) {
      this.logger.error('清理用户数据失败', {
        operation: 'cleanupUserData',
        userId,
        error: error instanceof Error ? error.message : String(error)
      });

      return false;
    }
  }

  /**
   * 验证加入会话请求
   * 
   * @param request 加入会话请求
   * @private
   */
  private validateJoinSessionRequest(request: JoinSessionRequest): void {
    if (!request.userId) {
      throw new BadRequestException('用户ID不能为空');
    }

    if (!request.sessionId) {
      throw new BadRequestException('会话ID不能为空');
    }

    if (!request.socketId) {
      throw new BadRequestException('Socket连接ID不能为空');
    }

    // 验证会话ID格式
    if (request.sessionId.length > LocationBroadcastService.MAX_SESSION_ID_LENGTH) {
      throw new BadRequestException(`会话ID长度不能超过${LocationBroadcastService.MAX_SESSION_ID_LENGTH}个字符`);
    }

    // 验证初始位置（如果提供）
    if (request.initialPosition) {
      this.validatePositionData(request.initialPosition);
    }
  }

  /**
   * 验证位置数据
   * 
   * @param position 位置数据
   * @private
   */
  private validatePositionData(position: { mapId: string; x: number; y: number }): void {
    if (!position.mapId) {
      throw new BadRequestException('地图ID不能为空');
    }

    if (typeof position.x !== 'number' || typeof position.y !== 'number') {
      throw new BadRequestException('位置坐标必须是数字');
    }

    if (!isFinite(position.x) || !isFinite(position.y)) {
      throw new BadRequestException('位置坐标必须是有效的数字');
    }

    // 可以添加更多的位置验证规则，比如地图边界检查
    if (position.x > LocationBroadcastService.MAX_COORDINATE || position.x < LocationBroadcastService.MIN_COORDINATE ||
        position.y > LocationBroadcastService.MAX_COORDINATE || position.y < LocationBroadcastService.MIN_COORDINATE) {
      throw new BadRequestException('位置坐标超出允许范围');
    }
  }

  /**
   * 处理用户会话迁移
   * 
   * @param userId 用户ID
   * @param newSessionId 新会话ID
   * @private
   */
  private async handleUserSessionMigration(userId: string, newSessionId: string): Promise<void> {
    try {
      // 这里可以实现用户从旧会话迁移到新会话的逻辑
      // 目前简单处理：清理用户的所有会话数据
      await this.locationBroadcastCore.cleanupUserData(userId);
    } catch (error) {
      this.logger.warn('用户会话迁移处理失败', {
        userId,
        newSessionId,
        error: error instanceof Error ? error.message : String(error)
      });
      // 迁移失败不阻止加入新会话
    }
  }

  /**
   * 获取需要广播的目标用户
   * 
   * @param userId 当前用户ID
   * @param mapId 地图ID
   * @returns 目标用户ID列表
   * @private
   */
  private async getBroadcastTargets(userId: string, mapId: string): Promise<string[]> {
    try {
      // 获取同地图的所有用户位置
      const mapPositions = await this.locationBroadcastCore.getMapPositions(mapId);
      
      // 排除当前用户，返回其他用户的ID
      return mapPositions
        .filter(pos => pos.userId !== userId)
        .map(pos => pos.userId as string);

    } catch (error) {
      this.logger.warn('获取广播目标失败', {
        userId,
        mapId,
        error: error instanceof Error ? error.message : String(error)
      });

      return [];
    }
  }

  /**
   * 判断是否应该保存位置历史
   * 
   * @param position 位置信息
   * @returns 是否应该保存
   * @private
   */
  private shouldSavePositionHistory(position: Position): boolean {
    // 简单策略：每隔30秒保存一次历史记录
    // 实际项目中可以根据移动距离、时间间隔等更复杂的规则
    const now = Date.now();
    const lastSaveKey = `lastHistorySave:${position.userId}`;
    
    // 这里应该使用缓存来记录上次保存时间
    // 为了简化，暂时返回false，可以后续优化
    return false;
  }
}