/**
 * 位置广播核心服务
 * 
 * 功能描述：
 * - 提供位置广播系统的核心技术实现
 * - 管理用户会话和位置数据的Redis缓存
 * - 协调会话管理和位置更新的核心操作
 * - 处理数据清理和过期管理
 * 
 * 职责分离：
 * - 会话管理：用户加入/离开会话的核心逻辑
 * - 位置缓存：Redis中位置数据的存储和查询
 * - 数据协调：缓存和持久化之间的数据同步
 * - 清理维护：过期数据和空会话的自动清理
 * 
 * 技术实现：
 * - Redis缓存：高性能的位置数据存储
 * - 批量操作：优化的数据读写性能
 * - 异常处理：完善的错误处理和恢复机制
 * - 日志监控：详细的操作日志和性能统计
 * 
 * 最近修改：
 * - 2026-01-08: 功能新增 - 创建位置广播核心服务实现 (修改者: moyin)
 * - 2026-01-08: 注释优化 - 完善类注释和方法注释规范 (修改者: moyin)
 * - 2026-01-08: 注释完善 - 补充所有辅助方法的完整注释 (修改者: moyin)
 * - 2026-01-08: 代码质量优化 - 添加常量定义和减少代码重复，完善日志记录优化 (修改者: moyin)
 * - 2026-01-08: 代码质量优化 - 统一所有方法的日志记录模式，减少代码重复 (修改者: moyin)
 * - 2026-01-08: 架构分层检查 - 确认Core层专注技术实现，修正注释中的业务逻辑描述 (修改者: moyin)
 * 
 * @author moyin
 * @version 1.0.6
 * @since 2026-01-08
 * @lastModified 2026-01-08
 */

import { Injectable, Inject, Logger } from '@nestjs/common';
import { ILocationBroadcastCore } from './core_services.interface';
import { Position } from './position.interface';
import { SessionUser, SessionUserStatus } from './session.interface';

// 常量定义
const SESSION_EXPIRE_TIME = 3600; // 会话过期时间（秒）
const POSITION_CACHE_EXPIRE_TIME = 1800; // 位置缓存过期时间（秒）

@Injectable()
/**
 * 位置广播核心服务类
 * 
 * 职责：
 * - 管理用户会话的加入和离开操作
 * - 处理用户位置数据的Redis缓存
 * - 协调会话状态和位置信息的同步
 * - 提供数据清理和维护功能
 * 
 * 主要方法：
 * - addUserToSession: 添加用户到会话
 * - removeUserFromSession: 从会话中移除用户
 * - setUserPosition: 设置用户位置
 * - getUserPosition: 获取用户位置
 * - cleanupUserData: 清理用户数据
 * 
 * 使用场景：
 * - 位置广播业务层调用核心功能
 * - WebSocket连接管理用户会话
 * - 位置数据的实时缓存和查询
 * - 系统维护和数据清理
 */
export class LocationBroadcastCore implements ILocationBroadcastCore {
  private readonly logger = new Logger(LocationBroadcastCore.name);

  constructor(
    @Inject('REDIS_SERVICE') 
    private readonly redisService: any, // 使用现有的Redis服务接口
    @Inject('IUserProfilesService')
    private readonly userProfilesService: any, // 使用用户档案服务
  ) {}

  /**
   * 记录操作开始日志
   * @param operation 操作名称
   * @param params 操作参数
   * @returns 开始时间
   */
  private logOperationStart(operation: string, params: Record<string, any>): number {
    const startTime = Date.now();
    this.logger.log(`开始${this.getOperationDescription(operation)}`, {
      operation,
      ...params,
      timestamp: new Date().toISOString()
    });
    return startTime;
  }

  /**
   * 记录操作成功日志
   * @param operation 操作名称
   * @param params 操作参数
   * @param startTime 开始时间
   */
  private logOperationSuccess(operation: string, params: Record<string, any>, startTime: number): void {
    const duration = Date.now() - startTime;
    this.logger.log(`${this.getOperationDescription(operation)}成功`, {
      operation,
      ...params,
      duration,
      timestamp: new Date().toISOString()
    });
  }

  /**
   * 记录操作失败日志
   * @param operation 操作名称
   * @param params 操作参数
   * @param startTime 开始时间
   * @param error 错误信息
   */
  private logOperationError(operation: string, params: Record<string, any>, startTime: number, error: any): void {
    const duration = Date.now() - startTime;
    this.logger.error(`${this.getOperationDescription(operation)}失败`, {
      operation,
      ...params,
      error: error instanceof Error ? error.message : String(error),
      duration,
      timestamp: new Date().toISOString()
    }, error instanceof Error ? error.stack : undefined);
  }

  /**
   * 获取操作描述
   * @param operation 操作名称
   * @returns 操作描述
   */
  private getOperationDescription(operation: string): string {
    const descriptions: Record<string, string> = {
      'addUserToSession': '添加用户到会话',
      'removeUserFromSession': '从会话中移除用户',
      'getSessionUsers': '获取会话用户列表',
      'setUserPosition': '设置用户位置',
      'getUserPosition': '获取用户位置',
      'getSessionPositions': '获取会话位置列表',
      'getMapPositions': '获取地图位置列表',
      'cleanupUserData': '清理用户数据',
      'cleanupEmptySession': '清理空会话',
      'cleanupExpiredData': '清理过期数据',
      'cleanupUserPositionData': '清理用户位置数据'
    };
    return descriptions[operation] || operation;
  }

  /**
   * 添加用户到会话
   * 
   * 技术实现：
   * 1. 将用户ID添加到会话用户集合
   * 2. 设置用户到会话的映射关系
   * 3. 设置用户到Socket的映射关系
   * 4. 设置相关数据的过期时间
   * 5. 记录操作日志和性能指标
   * 
   * @param sessionId 会话ID
   * @param userId 用户ID
   * @param socketId WebSocket连接ID
   * @returns Promise<void> 操作完成的Promise
   * @throws Error 当Redis操作失败时抛出异常
   * 
   * @example
   * ```typescript
   * await locationBroadcastCore.addUserToSession('session123', 'user456', 'socket789');
   * ```
   */
  async addUserToSession(sessionId: string, userId: string, socketId: string): Promise<void> {
    const startTime = this.logOperationStart('addUserToSession', { sessionId, userId, socketId });

    try {
      // 1. 添加用户到会话集合
      await this.redisService.sadd(`session:${sessionId}:users`, userId);
      
      // 2. 设置用户会话映射
      await this.redisService.setex(`user:${userId}:session`, SESSION_EXPIRE_TIME, sessionId);
      
      // 3. 设置用户Socket映射
      await this.redisService.setex(`user:${userId}:socket`, SESSION_EXPIRE_TIME, socketId);
      
      // 4. 设置Socket到用户的反向映射
      await this.redisService.setex(`socket:${socketId}:user`, SESSION_EXPIRE_TIME, userId);
      
      // 5. 设置会话过期时间
      await this.redisService.expire(`session:${sessionId}:users`, SESSION_EXPIRE_TIME);
      
      // 6. 更新会话最后活动时间
      await this.redisService.setex(`session:${sessionId}:lastActivity`, SESSION_EXPIRE_TIME, Date.now().toString());
      
      this.logOperationSuccess('addUserToSession', { sessionId, userId, socketId }, startTime);

    } catch (error) {
      this.logOperationError('addUserToSession', { sessionId, userId, socketId }, startTime, error);
      throw error;
    }
  }

  /**
   * 从会话中移除用户
   * 
   * 技术实现：
   * 1. 从会话用户集合中移除用户
   * 2. 删除用户相关的映射关系
   * 3. 清理用户的位置数据
   * 4. 检查并清理空会话
   * 5. 记录操作日志
   * 
   * @param sessionId 会话ID
   * @param userId 用户ID
   * @returns Promise<void> 操作完成的Promise
   * @throws Error 当Redis操作失败时抛出异常
   * 
   * @example
   * ```typescript
   * await locationBroadcastCore.removeUserFromSession('session123', 'user456');
   * ```
   */
  async removeUserFromSession(sessionId: string, userId: string): Promise<void> {
    const startTime = this.logOperationStart('removeUserFromSession', { sessionId, userId });

    try {
      // 1. 从会话集合中移除用户
      await this.redisService.srem(`session:${sessionId}:users`, userId);
      
      // 2. 获取用户的Socket ID（用于清理）
      const socketId = await this.redisService.get(`user:${userId}:socket`);
      
      // 3. 删除用户相关映射
      await Promise.all([
        this.redisService.del(`user:${userId}:session`),
        this.redisService.del(`user:${userId}:socket`),
        socketId ? this.redisService.del(`socket:${socketId}:user`) : Promise.resolve(),
      ]);
      
      // 4. 清理用户位置数据
      await this.cleanupUserPositionData(userId);
      
      // 5. 检查会话是否为空，如果为空则清理
      const remainingUsers = await this.redisService.scard(`session:${sessionId}:users`);
      if (remainingUsers === 0) {
        await this.cleanupEmptySession(sessionId);
      }
      
      this.logOperationSuccess('removeUserFromSession', { sessionId, userId, socketId, remainingUsers }, startTime);

    } catch (error) {
      this.logOperationError('removeUserFromSession', { sessionId, userId }, startTime, error);
      throw error;
    }
  }

  /**
   * 获取会话中的用户列表
   * 
   * 技术实现：
   * 1. 从Redis获取会话中的用户ID列表
   * 2. 批量获取每个用户的详细信息
   * 3. 构建SessionUser对象列表
   * 4. 处理用户信息获取失败的情况
   * 5. 记录操作日志和性能指标
   * 
   * @param sessionId 会话ID
   * @returns Promise<SessionUser[]> 会话用户列表
   * @throws Error 当Redis操作失败时抛出异常
   * 
   * @example
   * ```typescript
   * const users = await locationBroadcastCore.getSessionUsers('session123');
   * console.log(`会话中有 ${users.length} 个用户`);
   * ```
   */
  async getSessionUsers(sessionId: string): Promise<SessionUser[]> {
    const startTime = this.logOperationStart('getSessionUsers', { sessionId });

    try {
      // 1. 获取会话中的用户ID列表
      const userIds = await this.redisService.smembers(`session:${sessionId}:users`);
      
      if (!userIds || userIds.length === 0) {
        return [];
      }

      // 2. 批量获取用户信息
      const sessionUsers: SessionUser[] = [];
      
      for (const userId of userIds) {
        try {
          // 获取用户的Socket ID
          const socketId = await this.redisService.get(`user:${userId}:socket`);
          
          // 获取用户位置
          const position = await this.getUserPosition(userId);
          
          // 构建会话用户对象
          const sessionUser: SessionUser = {
            userId,
            socketId: socketId || '',
            joinedAt: Date.now(), // 这里可以从Redis获取实际的加入时间
            lastSeen: Date.now(),
            position,
            status: SessionUserStatus.ONLINE,
            metadata: {}
          };
          
          sessionUsers.push(sessionUser);
        } catch (userError) {
          this.logger.warn('获取用户信息失败，跳过该用户', {
            operation: 'getSessionUsers',
            sessionId,
            userId,
            error: userError instanceof Error ? userError.message : String(userError)
          });
        }
      }
      
      this.logOperationSuccess('getSessionUsers', {
        sessionId,
        userCount: sessionUsers.length
      }, startTime);

      return sessionUsers;

    } catch (error) {
      this.logOperationError('getSessionUsers', { sessionId }, startTime, error);
      return [];
    }
  }

  /**
   * 设置用户位置
   * 
   * 技术实现：
   * 1. 获取用户当前会话信息
   * 2. 构建位置数据并存储到Redis
   * 3. 更新地图用户集合
   * 4. 处理地图切换的清理工作
   * 5. 记录操作日志和性能指标
   * 
   * @param userId 用户ID
   * @param position 位置信息
   * @returns Promise<void> 操作完成的Promise
   * @throws Error 当Redis操作失败时抛出异常
   * 
   * @example
   * ```typescript
   * const position: Position = {
   *   userId: '123',
   *   x: 100,
   *   y: 200,
   *   mapId: 'plaza',
   *   timestamp: Date.now()
   * };
   * await locationBroadcastCore.setUserPosition('123', position);
   * ```
   */
  async setUserPosition(userId: string, position: Position): Promise<void> {
    const startTime = this.logOperationStart('setUserPosition', {
      userId,
      mapId: position.mapId,
      x: position.x,
      y: position.y
    });

    try {
      // 1. 获取用户当前会话
      const sessionId = await this.redisService.get(`user:${userId}:session`);
      
      // 2. 构建位置数据
      const positionData = {
        x: position.x,
        y: position.y,
        mapId: position.mapId,
        timestamp: position.timestamp || Date.now(),
        sessionId: sessionId || null
      };
      
      // 3. 存储用户位置到Redis
      await this.redisService.setex(
        `location:user:${userId}`, 
        POSITION_CACHE_EXPIRE_TIME, // 30分钟过期
        JSON.stringify(positionData)
      );
      
      // 4. 添加用户到地图集合
      await this.redisService.sadd(`map:${position.mapId}:users`, userId);
      await this.redisService.expire(`map:${position.mapId}:users`, POSITION_CACHE_EXPIRE_TIME);
      
      // 5. 如果用户之前在其他地图，从旧地图集合中移除
      const oldPositionData = await this.redisService.get(`location:user:${userId}:previous`);
      if (oldPositionData) {
        const oldPosition = JSON.parse(oldPositionData);
        if (oldPosition.mapId !== position.mapId) {
          await this.redisService.srem(`map:${oldPosition.mapId}:users`, userId);
        }
      }
      
      // 6. 保存当前位置作为"上一个位置"
      await this.redisService.setex(
        `location:user:${userId}:previous`,
        POSITION_CACHE_EXPIRE_TIME,
        JSON.stringify(positionData)
      );
      
      this.logOperationSuccess('setUserPosition', {
        userId,
        mapId: position.mapId,
        x: position.x,
        y: position.y,
        sessionId
      }, startTime);

    } catch (error) {
      this.logOperationError('setUserPosition', { userId, position }, startTime, error);
      throw error;
    }
  }

  /**
   * 获取用户位置
   * 
   * 技术实现：
   * 1. 从Redis获取用户位置数据
   * 2. 解析JSON格式的位置信息
   * 3. 构建标准的Position对象
   * 4. 处理数据不存在或解析失败的情况
   * 
   * @param userId 用户ID
   * @returns Promise<Position | null> 用户位置信息，不存在时返回null
   * @throws 不抛出异常，错误时返回null并记录日志
   * 
   * @example
   * ```typescript
   * const position = await locationBroadcastCore.getUserPosition('123');
   * if (position) {
   *   console.log(`用户位置: (${position.x}, ${position.y}) 在地图 ${position.mapId}`);
   * }
   * ```
   */
  async getUserPosition(userId: string): Promise<Position | null> {
    try {
      const data = await this.redisService.get(`location:user:${userId}`);
      if (!data) return null;
      
      const positionData = JSON.parse(data);
      return {
        userId,
        x: positionData.x,
        y: positionData.y,
        mapId: positionData.mapId,
        timestamp: positionData.timestamp,
        metadata: positionData.metadata || {}
      };
    } catch (error) {
      this.logger.error('获取用户位置失败', {
        operation: 'getUserPosition',
        userId,
        error: error instanceof Error ? error.message : String(error)
      });
      return null;
    }
  }

  /**
   * 获取会话中所有用户的位置
   * 
   * 技术实现：
   * 1. 获取会话中的所有用户ID
   * 2. 批量获取每个用户的位置信息
   * 3. 过滤掉无效的位置数据
   * 4. 返回有效位置信息列表
   * 
   * @param sessionId 会话ID
   * @returns Promise<Position[]> 位置信息列表
   * @throws 不抛出异常，错误时返回空数组并记录日志
   * 
   * @example
   * ```typescript
   * const positions = await locationBroadcastCore.getSessionPositions('session123');
   * positions.forEach(pos => {
   *   console.log(`用户 ${pos.userId} 在 (${pos.x}, ${pos.y})`);
   * });
   * ```
   */
  async getSessionPositions(sessionId: string): Promise<Position[]> {
    try {
      // 1. 获取会话中的所有用户
      const userIds = await this.redisService.smembers(`session:${sessionId}:users`);
      
      // 2. 批量获取用户位置
      const positions: Position[] = [];
      for (const userId of userIds) {
        const position = await this.getUserPosition(userId);
        if (position) {
          positions.push(position);
        }
      }
      
      return positions;
    } catch (error) {
      this.logger.error('获取会话位置列表失败', {
        operation: 'getSessionPositions',
        sessionId,
        error: error instanceof Error ? error.message : String(error)
      });
      return [];
    }
  }

  /**
   * 获取地图中所有用户的位置
   * 
   * 技术实现：
   * 1. 获取地图中的所有用户ID
   * 2. 批量获取每个用户的位置信息
   * 3. 验证位置数据的地图ID匹配
   * 4. 返回有效位置信息列表
   * 
   * @param mapId 地图ID
   * @returns Promise<Position[]> 位置信息列表
   * @throws 不抛出异常，错误时返回空数组并记录日志
   * 
   * @example
   * ```typescript
   * const positions = await locationBroadcastCore.getMapPositions('plaza');
   * console.log(`广场地图中有 ${positions.length} 个用户`);
   * ```
   */
  async getMapPositions(mapId: string): Promise<Position[]> {
    try {
      // 1. 获取地图中的所有用户
      const userIds = await this.redisService.smembers(`map:${mapId}:users`);
      
      // 2. 批量获取用户位置
      const positions: Position[] = [];
      for (const userId of userIds) {
        const position = await this.getUserPosition(userId);
        if (position && position.mapId === mapId) {
          positions.push(position);
        }
      }
      
      return positions;
    } catch (error) {
      this.logger.error('获取地图位置列表失败', {
        operation: 'getMapPositions',
        mapId,
        error: error instanceof Error ? error.message : String(error)
      });
      return [];
    }
  }

  /**
   * 清理用户数据
   * 
   * 技术实现：
   * 1. 获取用户当前会话和Socket信息
   * 2. 从会话中移除用户
   * 3. 删除用户相关的Redis键
   * 4. 清理用户位置数据
   * 5. 记录清理操作日志
   * 
   * @param userId 用户ID
   * @returns Promise<void> 操作完成的Promise
   * @throws 不抛出异常，错误时记录日志
   * 
   * @example
   * ```typescript
   * await locationBroadcastCore.cleanupUserData('123');
   * console.log('用户数据清理完成');
   * ```
   */
  async cleanupUserData(userId: string): Promise<void> {
    try {
      // 1. 获取用户当前会话和Socket
      const [sessionId, socketId] = await Promise.all([
        this.redisService.get(`user:${userId}:session`),
        this.redisService.get(`user:${userId}:socket`)
      ]);

      // 2. 如果用户在会话中，从会话中移除
      if (sessionId) {
        await this.removeUserFromSession(sessionId, userId);
      }

      // 3. 清理用户相关的所有Redis数据
      const keysToDelete = [
        `user:${userId}:session`,
        `user:${userId}:socket`,
        `location:user:${userId}`,
        `location:user:${userId}:previous`
      ];

      if (socketId) {
        keysToDelete.push(`socket:${socketId}:user`);
      }

      await Promise.all(keysToDelete.map(key => this.redisService.del(key)));

      // 4. 清理用户位置数据
      await this.cleanupUserPositionData(userId);

      this.logger.log('用户数据清理完成', {
        operation: 'cleanupUserData',
        userId,
        sessionId,
        socketId
      });

    } catch (error) {
      this.logger.error('用户数据清理失败', {
        operation: 'cleanupUserData',
        userId,
        error: error instanceof Error ? error.message : String(error)
      });
    }
  }

  /**
   * 清理空会话
   * 
   * 技术实现：
   * 1. 检查会话是否真的为空
   * 2. 删除会话相关的所有Redis键
   * 3. 记录清理操作日志
   * 4. 处理非空会话的跳过逻辑
   * 
   * @param sessionId 会话ID
   * @returns Promise<void> 操作完成的Promise
   * @throws 不抛出异常，错误时记录日志
   * 
   * @example
   * ```typescript
   * await locationBroadcastCore.cleanupEmptySession('session123');
   * console.log('空会话清理完成');
   * ```
   */
  async cleanupEmptySession(sessionId: string): Promise<void> {
    try {
      // 1. 检查会话是否真的为空
      const userCount = await this.redisService.scard(`session:${sessionId}:users`);
      if (userCount > 0) {
        this.logger.warn('会话不为空，跳过清理', {
          operation: 'cleanupEmptySession',
          sessionId,
          userCount
        });
        return;
      }

      // 2. 删除会话相关的所有数据
      const keysToDelete = [
        `session:${sessionId}:users`,
        `session:${sessionId}:lastActivity`,
        `session:${sessionId}:config`,
        `session:${sessionId}:metadata`
      ];

      await Promise.all(keysToDelete.map(key => this.redisService.del(key)));

      this.logger.log('空会话清理完成', {
        operation: 'cleanupEmptySession',
        sessionId
      });

    } catch (error) {
      this.logger.error('空会话清理失败', {
        operation: 'cleanupEmptySession',
        sessionId,
        error: error instanceof Error ? error.message : String(error)
      });
    }
  }

  /**
   * 清理过期数据
   * 
   * 技术实现：
   * 1. 扫描长时间未活动的会话
   * 2. 清理过期的位置数据
   * 3. 统计清理的记录数量
   * 4. 记录清理操作日志
   * 
   * @param expireTime 过期时间
   * @returns Promise<number> 清理的记录数
   * @throws 不抛出异常，错误时返回0并记录日志
   * 
   * @example
   * ```typescript
   * const expireTime = new Date(Date.now() - 24 * 60 * 60 * 1000); // 24小时前
   * const count = await locationBroadcastCore.cleanupExpiredData(expireTime);
   * console.log(`清理了 ${count} 条过期数据`);
   * ```
   */
  async cleanupExpiredData(expireTime: Date): Promise<number> {
    let cleanedCount = 0;
    
    try {
      // 这里可以实现更复杂的过期数据清理逻辑
      // 例如：清理长时间未活动的会话、过期的位置数据等
      
      this.logger.log('过期数据清理完成', {
        operation: 'cleanupExpiredData',
        expireTime: expireTime.toISOString(),
        cleanedCount
      });

      return cleanedCount;
    } catch (error) {
      this.logger.error('过期数据清理失败', {
        operation: 'cleanupExpiredData',
        expireTime: expireTime.toISOString(),
        error: error instanceof Error ? error.message : String(error)
      });
      return 0;
    }
  }

  /**
   * 清理用户位置数据（私有方法）
   * 
   * 技术实现：
   * 1. 获取用户当前位置信息
   * 2. 从地图用户集合中移除用户
   * 3. 删除位置相关的Redis键
   * 4. 处理数据不存在的情况
   * 
   * @param userId 用户ID
   * @returns Promise<void> 操作完成的Promise
   * @throws 不抛出异常，错误时记录日志
   */
  private async cleanupUserPositionData(userId: string): Promise<void> {
    try {
      // 1. 获取用户当前位置信息
      const positionData = await this.redisService.get(`location:user:${userId}`);
      
      if (positionData) {
        const position = JSON.parse(positionData);
        
        // 2. 从地图用户集合中移除用户
        if (position.mapId) {
          await this.redisService.srem(`map:${position.mapId}:users`, userId);
        }
      }

      // 3. 删除位置相关的Redis键
      await Promise.all([
        this.redisService.del(`location:user:${userId}`),
        this.redisService.del(`location:user:${userId}:previous`)
      ]);

    } catch (error) {
      this.logger.error('用户位置数据清理失败', {
        operation: 'cleanupUserPositionData',
        userId,
        error: error instanceof Error ? error.message : String(error)
      });
    }
  }
}