/**
 * 位置管理服务单元测试
 * 
 * 功能描述：
 * - 测试位置管理服务的核心功能
 * - 验证位置查询、统计、验证等业务逻辑
 * - 确保数据处理和计算的正确性
 * - 提供完整的测试覆盖率
 * 
 * 测试范围：
 * - 位置数据查询和过滤
 * - 位置统计和分析
 * - 位置验证和计算
 * - 批量操作和性能优化
 * 
 * @author moyin
 * @version 1.0.0
 * @since 2026-01-08
 * @lastModified 2026-01-08
 */

import { Test, TestingModule } from '@nestjs/testing';
import { BadRequestException } from '@nestjs/common';
import { LocationPositionService, PositionQueryRequest, PositionStatsRequest, PositionHistoryRequest } from './location_position.service';
import { Position, PositionHistory } from '../../../core/location_broadcast_core/position.interface';

describe('LocationPositionService', () => {
  let service: LocationPositionService;
  let mockLocationBroadcastCore: any;
  let mockUserPositionCore: any;

  beforeEach(async () => {
    // 创建模拟的核心服务
    mockLocationBroadcastCore = {
      getSessionPositions: jest.fn(),
      getMapPositions: jest.fn(),
      getUserPosition: jest.fn(),
      setUserPosition: jest.fn(),
    };

    mockUserPositionCore = {
      getPositionHistory: jest.fn(),
    };

    const module: TestingModule = await Test.createTestingModule({
      providers: [
        LocationPositionService,
        {
          provide: 'ILocationBroadcastCore',
          useValue: mockLocationBroadcastCore,
        },
        {
          provide: 'IUserPositionCore',
          useValue: mockUserPositionCore,
        },
      ],
    }).compile();

    service = module.get<LocationPositionService>(LocationPositionService);
  });

  afterEach(() => {
    jest.clearAllMocks();
  });

  describe('queryPositions', () => {
    const mockPositions: Position[] = [
      {
        userId: 'user1',
        x: 100,
        y: 200,
        mapId: 'plaza',
        timestamp: Date.now() - 60000,
        metadata: { speed: 5 },
      },
      {
        userId: 'user2',
        x: 150,
        y: 250,
        mapId: 'plaza',
        timestamp: Date.now() - 30000,
        metadata: { speed: 3 },
      },
      {
        userId: 'user3',
        x: 200,
        y: 300,
        mapId: 'forest',
        timestamp: Date.now(),
        metadata: { speed: 7 },
      },
    ];

    it('应该按会话ID查询位置', async () => {
      const request: PositionQueryRequest = {
        sessionId: 'session123',
      };

      mockLocationBroadcastCore.getSessionPositions.mockResolvedValue(mockPositions);

      const result = await service.queryPositions(request);

      expect(result.positions).toEqual(mockPositions);
      expect(result.total).toBe(3);
      expect(result.timestamp).toBeDefined();
      expect(mockLocationBroadcastCore.getSessionPositions).toHaveBeenCalledWith('session123');
    });

    it('应该按地图ID查询位置', async () => {
      const request: PositionQueryRequest = {
        mapId: 'plaza',
      };

      const plazaPositions = mockPositions.filter(p => p.mapId === 'plaza');
      mockLocationBroadcastCore.getMapPositions.mockResolvedValue(plazaPositions);

      const result = await service.queryPositions(request);

      expect(result.positions).toEqual(plazaPositions);
      expect(result.total).toBe(2);
      expect(mockLocationBroadcastCore.getMapPositions).toHaveBeenCalledWith('plaza');
    });

    it('应该按用户ID列表查询位置', async () => {
      const request: PositionQueryRequest = {
        userIds: ['user1', 'user3'],
      };

      // 模拟按用户ID获取位置
      mockLocationBroadcastCore.getUserPosition
        .mockResolvedValueOnce(mockPositions[0]) // user1
        .mockResolvedValueOnce(mockPositions[2]); // user3

      const result = await service.queryPositions(request);

      expect(result.positions).toHaveLength(2);
      expect(result.positions[0].userId).toBe('user1');
      expect(result.positions[1].userId).toBe('user3');
    });

    it('应该应用时间范围过滤', async () => {
      const now = Date.now();
      const request: PositionQueryRequest = {
        sessionId: 'session123',
        timeRange: {
          startTime: now - 45000, // 45秒前
          endTime: now,
        },
      };

      mockLocationBroadcastCore.getSessionPositions.mockResolvedValue(mockPositions);

      const result = await service.queryPositions(request);

      // 应该只返回时间范围内的位置（user2 和 user3）
      expect(result.positions).toHaveLength(2);
      expect(result.positions.every(p => p.timestamp >= request.timeRange!.startTime)).toBe(true);
      expect(result.positions.every(p => p.timestamp <= request.timeRange!.endTime)).toBe(true);
    });

    it('应该应用范围过滤', async () => {
      const request: PositionQueryRequest = {
        sessionId: 'session123',
        range: {
          centerX: 125,
          centerY: 225,
          radius: 50,
        },
      };

      mockLocationBroadcastCore.getSessionPositions.mockResolvedValue(mockPositions);

      const result = await service.queryPositions(request);

      // 应该只返回范围内的位置
      expect(result.positions.length).toBeGreaterThan(0);
      result.positions.forEach(pos => {
        const distance = Math.sqrt(
          Math.pow(pos.x - request.range!.centerX, 2) + 
          Math.pow(pos.y - request.range!.centerY, 2)
        );
        expect(distance).toBeLessThanOrEqual(request.range!.radius);
      });
    });

    it('应该应用分页', async () => {
      const request: PositionQueryRequest = {
        sessionId: 'session123',
        pagination: {
          offset: 1,
          limit: 1,
        },
      };

      mockLocationBroadcastCore.getSessionPositions.mockResolvedValue(mockPositions);

      const result = await service.queryPositions(request);

      expect(result.positions).toHaveLength(1);
      expect(result.total).toBe(3); // 总数不变
      expect(result.positions[0]).toEqual(mockPositions[1]); // 第二个位置
    });

    it('应该验证查询参数', async () => {
      const invalidRequests = [
        { userIds: Array(1001).fill('user') }, // 用户ID过多
        { range: { centerX: 'invalid', centerY: 100, radius: 50 } }, // 无效坐标
        { range: { centerX: 100, centerY: 100, radius: -1 } }, // 负半径
        { pagination: { offset: -1, limit: 10 } }, // 负偏移
        { pagination: { offset: 0, limit: 0 } }, // 无效限制
      ];

      for (const request of invalidRequests) {
        await expect(service.queryPositions(request as any)).rejects.toThrow(BadRequestException);
      }
    });
  });

  describe('getPositionStats', () => {
    const mockPositions: Position[] = [
      { userId: 'user1', x: 100, y: 200, mapId: 'plaza', timestamp: Date.now() - 60000, metadata: {} },
      { userId: 'user2', x: 150, y: 250, mapId: 'plaza', timestamp: Date.now() - 30000, metadata: {} },
      { userId: 'user3', x: 200, y: 300, mapId: 'forest', timestamp: Date.now(), metadata: {} },
    ];

    it('应该返回会话统计信息', async () => {
      const request: PositionStatsRequest = {
        sessionId: 'session123',
      };

      mockLocationBroadcastCore.getSessionPositions.mockResolvedValue(mockPositions);

      const result = await service.getPositionStats(request);

      expect(result.totalUsers).toBe(3);
      expect(result.onlineUsers).toBe(3);
      expect(result.activeMaps).toBe(2);
      expect(result.mapDistribution).toEqual({
        plaza: 2,
        forest: 1,
      });
      expect(result.updateFrequency).toBeGreaterThanOrEqual(0);
      expect(result.timestamp).toBeDefined();
    });

    it('应该返回地图统计信息', async () => {
      const request: PositionStatsRequest = {
        mapId: 'plaza',
      };

      const plazaPositions = mockPositions.filter(p => p.mapId === 'plaza');
      mockLocationBroadcastCore.getMapPositions.mockResolvedValue(plazaPositions);

      const result = await service.getPositionStats(request);

      expect(result.totalUsers).toBe(2);
      expect(result.mapDistribution).toEqual({
        plaza: 2,
      });
    });

    it('应该应用时间范围过滤', async () => {
      const now = Date.now();
      const request: PositionStatsRequest = {
        sessionId: 'session123',
        timeRange: {
          startTime: now - 45000,
          endTime: now,
        },
      };

      mockLocationBroadcastCore.getSessionPositions.mockResolvedValue(mockPositions);

      const result = await service.getPositionStats(request);

      expect(result.totalUsers).toBe(2); // 只有user2和user3在时间范围内
    });
  });

  describe('getPositionHistory', () => {
    const mockHistory: PositionHistory[] = [
      {
        id: 1,
        userId: 'user1',
        x: 100,
        y: 200,
        mapId: 'plaza',
        timestamp: Date.now() - 120000,
        sessionId: 'session123',
        metadata: {},
        createdAt: new Date(),
      },
      {
        id: 2,
        userId: 'user1',
        x: 110,
        y: 210,
        mapId: 'plaza',
        timestamp: Date.now() - 60000,
        sessionId: 'session123',
        metadata: {},
        createdAt: new Date(),
      },
    ];

    it('应该返回用户位置历史', async () => {
      const request: PositionHistoryRequest = {
        userId: 'user1',
        limit: 10,
      };

      mockUserPositionCore.getPositionHistory.mockResolvedValue(mockHistory);

      const result = await service.getPositionHistory(request);

      expect(result).toEqual(mockHistory);
      expect(mockUserPositionCore.getPositionHistory).toHaveBeenCalledWith('user1', 10);
    });

    it('应该应用地图过滤', async () => {
      const request: PositionHistoryRequest = {
        userId: 'user1',
        mapId: 'plaza',
      };

      mockUserPositionCore.getPositionHistory.mockResolvedValue(mockHistory);

      const result = await service.getPositionHistory(request);

      expect(result).toEqual(mockHistory); // 所有记录都是plaza地图
    });

    it('应该应用时间范围过滤', async () => {
      const now = Date.now();
      const request: PositionHistoryRequest = {
        userId: 'user1',
        timeRange: {
          startTime: now - 90000,
          endTime: now,
        },
      };

      mockUserPositionCore.getPositionHistory.mockResolvedValue(mockHistory);

      const result = await service.getPositionHistory(request);

      expect(result).toHaveLength(1); // 只有一个记录在时间范围内
      expect(result[0].timestamp).toBeGreaterThanOrEqual(request.timeRange!.startTime);
    });
  });

  describe('validatePosition', () => {
    it('应该验证有效的位置数据', async () => {
      const validPosition: Position = {
        userId: 'user1',
        x: 100,
        y: 200,
        mapId: 'plaza',
        timestamp: Date.now(),
        metadata: { speed: 5 },
      };

      const result = await service.validatePosition(validPosition);

      expect(result.isValid).toBe(true);
      expect(result.errors).toHaveLength(0);
    });

    it('应该检测无效的位置数据', async () => {
      const invalidPositions = [
        { userId: '', x: 100, y: 200, mapId: 'plaza', timestamp: Date.now(), metadata: {} },
        { userId: 'user1', x: NaN, y: 200, mapId: 'plaza', timestamp: Date.now(), metadata: {} },
        { userId: 'user1', x: 100, y: Infinity, mapId: 'plaza', timestamp: Date.now(), metadata: {} },
        { userId: 'user1', x: 9999999, y: 200, mapId: 'plaza', timestamp: Date.now(), metadata: {} },
        { userId: 'user1', x: 100, y: 200, mapId: '', timestamp: Date.now(), metadata: {} },
      ];

      for (const position of invalidPositions) {
        const result = await service.validatePosition(position as Position);
        expect(result.isValid).toBe(false);
        expect(result.errors.length).toBeGreaterThan(0);
      }
    });

    it('应该检测时间戳警告', async () => {
      const oldPosition: Position = {
        userId: 'user1',
        x: 100,
        y: 200,
        mapId: 'plaza',
        timestamp: Date.now() - 10 * 60 * 1000, // 10分钟前
        metadata: {},
      };

      const result = await service.validatePosition(oldPosition);

      expect(result.isValid).toBe(true);
      expect(result.warnings.length).toBeGreaterThan(0);
      expect(result.warnings[0]).toContain('时间戳与当前时间差异较大');
    });
  });

  describe('calculateDistance', () => {
    it('应该计算同地图位置间的距离', () => {
      const pos1: Position = { userId: 'user1', x: 0, y: 0, mapId: 'plaza', timestamp: Date.now(), metadata: {} };
      const pos2: Position = { userId: 'user2', x: 3, y: 4, mapId: 'plaza', timestamp: Date.now(), metadata: {} };

      const distance = service.calculateDistance(pos1, pos2);

      expect(distance).toBe(5); // 3-4-5直角三角形
    });

    it('应该返回不同地图间的无穷大距离', () => {
      const pos1: Position = { userId: 'user1', x: 0, y: 0, mapId: 'plaza', timestamp: Date.now(), metadata: {} };
      const pos2: Position = { userId: 'user2', x: 3, y: 4, mapId: 'forest', timestamp: Date.now(), metadata: {} };

      const distance = service.calculateDistance(pos1, pos2);

      expect(distance).toBe(Infinity);
    });
  });

  describe('getUsersInRange', () => {
    const mockMapPositions: Position[] = [
      { userId: 'user1', x: 100, y: 100, mapId: 'plaza', timestamp: Date.now(), metadata: {} },
      { userId: 'user2', x: 110, y: 110, mapId: 'plaza', timestamp: Date.now(), metadata: {} }, // 距离约14.14
      { userId: 'user3', x: 200, y: 200, mapId: 'plaza', timestamp: Date.now(), metadata: {} }, // 距离约141.42
    ];

    it('应该返回范围内的用户', async () => {
      const centerPosition: Position = {
        userId: 'center_user',
        x: 100,
        y: 100,
        mapId: 'plaza',
        timestamp: Date.now(),
        metadata: {},
      };

      mockLocationBroadcastCore.getMapPositions.mockResolvedValue(mockMapPositions);

      const result = await service.getUsersInRange(centerPosition, 20);

      expect(result).toHaveLength(2); // Both user1 and user2 are within range
      expect(result.map(r => r.userId)).toContain('user1');
      expect(result.map(r => r.userId)).toContain('user2');
    });

    it('应该排除中心用户自己', async () => {
      const centerPosition: Position = {
        userId: 'user1', // 与mockMapPositions中的第一个用户相同
        x: 100,
        y: 100,
        mapId: 'plaza',
        timestamp: Date.now(),
        metadata: {},
      };

      mockLocationBroadcastCore.getMapPositions.mockResolvedValue(mockMapPositions);

      const result = await service.getUsersInRange(centerPosition, 20);

      expect(result.every(pos => pos.userId !== 'user1')).toBe(true);
    });
  });

  describe('batchUpdatePositions', () => {
    it('应该批量更新有效位置', async () => {
      const positions: Position[] = [
        { userId: 'user1', x: 100, y: 200, mapId: 'plaza', timestamp: Date.now(), metadata: {} },
        { userId: 'user2', x: 150, y: 250, mapId: 'plaza', timestamp: Date.now(), metadata: {} },
      ];

      mockLocationBroadcastCore.setUserPosition.mockResolvedValue(undefined);

      const result = await service.batchUpdatePositions(positions);

      expect(result.success).toBe(2);
      expect(result.failed).toBe(0);
      expect(mockLocationBroadcastCore.setUserPosition).toHaveBeenCalledTimes(2);
    });

    it('应该处理部分失败的情况', async () => {
      const positions: Position[] = [
        { userId: 'user1', x: 100, y: 200, mapId: 'plaza', timestamp: Date.now(), metadata: {} },
        { userId: '', x: 150, y: 250, mapId: 'plaza', timestamp: Date.now(), metadata: {} }, // 无效位置
      ];

      mockLocationBroadcastCore.setUserPosition.mockResolvedValue(undefined);

      const result = await service.batchUpdatePositions(positions);

      expect(result.success).toBe(1);
      expect(result.failed).toBe(1);
      expect(mockLocationBroadcastCore.setUserPosition).toHaveBeenCalledTimes(1);
    });

    it('应该处理核心服务调用失败', async () => {
      const positions: Position[] = [
        { userId: 'user1', x: 100, y: 200, mapId: 'plaza', timestamp: Date.now(), metadata: {} },
      ];

      mockLocationBroadcastCore.setUserPosition.mockRejectedValue(new Error('更新失败'));

      const result = await service.batchUpdatePositions(positions);

      expect(result.success).toBe(0);
      expect(result.failed).toBe(1);
    });
  });
});