/**
 * 会话管理服务单元测试
 * 
 * 功能描述：
 * - 测试会话管理服务的核心功能
 * - 验证会话创建、查询、配置等业务逻辑
 * - 确保权限验证和数据验证的正确性
 * - 提供完整的测试覆盖率
 * 
 * 测试范围：
 * - 会话创建和配置管理
 * - 会话查询和详情获取
 * - 权限验证和访问控制
 * - 数据验证和错误处理
 * 
 * @author moyin
 * @version 1.0.0
 * @since 2026-01-08
 * @lastModified 2026-01-08
 */

import { Test, TestingModule } from '@nestjs/testing';
import { BadRequestException, NotFoundException, ConflictException, ForbiddenException } from '@nestjs/common';
import { LocationSessionService, CreateSessionRequest, SessionQueryRequest } from './location_session.service';
import { GameSession, SessionUser, SessionUserStatus, SessionStatus } from '../../../core/location_broadcast_core/session.interface';

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

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

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

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

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

  describe('createSession', () => {
    const mockCreateRequest: CreateSessionRequest = {
      sessionId: 'session123',
      creatorId: 'user456',
      name: '测试会话',
      description: '这是一个测试会话',
      maxUsers: 50,
      allowObservers: true,
      broadcastRange: 1000,
    };

    it('应该成功创建会话', async () => {
      // 模拟会话不存在（返回空用户列表）
      mockLocationBroadcastCore.getSessionUsers.mockResolvedValue([]);

      const result = await service.createSession(mockCreateRequest);

      expect(result.sessionId).toBe(mockCreateRequest.sessionId);
      expect(result.users).toEqual([]);
      expect(result.status).toBe(SessionStatus.ACTIVE);
      expect(result.config.maxUsers).toBe(mockCreateRequest.maxUsers);
      expect(result.config.allowObservers).toBe(mockCreateRequest.allowObservers);
      expect(result.metadata.name).toBe(mockCreateRequest.name);
      expect(result.metadata.description).toBe(mockCreateRequest.description);
      expect(result.metadata.creatorId).toBe(mockCreateRequest.creatorId);
    });

    it('应该在会话ID已存在时抛出冲突异常', async () => {
      // 模拟会话已存在（返回非空用户列表）
      const existingUsers: SessionUser[] = [
        {
          userId: 'user789',
          socketId: 'socket123',
          joinedAt: Date.now(),
          lastSeen: Date.now(),
          status: SessionUserStatus.ONLINE,
          metadata: {},
        },
      ];
      mockLocationBroadcastCore.getSessionUsers.mockResolvedValue(existingUsers);

      await expect(service.createSession(mockCreateRequest)).rejects.toThrow(ConflictException);
    });

    it('应该验证必填参数', async () => {
      const invalidRequests = [
        { ...mockCreateRequest, sessionId: '' },
        { ...mockCreateRequest, creatorId: '' },
      ];

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

    it('应该验证参数范围', async () => {
      const invalidRequests = [
        { ...mockCreateRequest, maxUsers: 0 },
        { ...mockCreateRequest, maxUsers: 1001 },
        { ...mockCreateRequest, broadcastRange: -1 },
        { ...mockCreateRequest, broadcastRange: 10001 },
      ];

      // 为每个无效请求设置Mock返回值
      mockLocationBroadcastCore.getSessionUsers.mockResolvedValue([]);

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

    it('应该正确处理可选参数', async () => {
      const minimalRequest: CreateSessionRequest = {
        sessionId: 'session123',
        creatorId: 'user456',
      };

      mockLocationBroadcastCore.getSessionUsers.mockResolvedValue([]);

      const result = await service.createSession(minimalRequest);

      expect(result.config.maxUsers).toBe(100); // 默认值
      expect(result.config.allowObservers).toBe(true); // 默认值
      expect(result.config.broadcastRange).toBe(1000); // 默认值
      expect(result.metadata.name).toBe(minimalRequest.sessionId); // 默认使用sessionId
    });

    it('应该正确设置密码相关配置', async () => {
      const requestWithPassword = {
        ...mockCreateRequest,
        password: 'secret123',
      };

      mockLocationBroadcastCore.getSessionUsers.mockResolvedValue([]);

      const result = await service.createSession(requestWithPassword);

      expect(result.config.requirePassword).toBe(true);
      expect(result.config.password).toBe('secret123');
      expect(result.metadata.isPublic).toBe(false);
    });
  });

  describe('getSessionDetail', () => {
    const mockUsers: SessionUser[] = [
      {
        userId: 'user1',
        socketId: 'socket1',
        joinedAt: Date.now() - 60000,
        lastSeen: Date.now(),
        status: SessionUserStatus.ONLINE,
        metadata: {},
      },
      {
        userId: 'user2',
        socketId: 'socket2',
        joinedAt: Date.now() - 30000,
        lastSeen: Date.now() - 5000,
        status: SessionUserStatus.AWAY,
        metadata: {},
      },
    ];

    const mockPositions = [
      { userId: 'user1', x: 100, y: 200, mapId: 'plaza', timestamp: Date.now(), metadata: {} },
      { userId: 'user2', x: 150, y: 250, mapId: 'forest', timestamp: Date.now(), metadata: {} },
    ];

    it('应该返回完整的会话详情', async () => {
      mockLocationBroadcastCore.getSessionUsers.mockResolvedValue(mockUsers);
      mockLocationBroadcastCore.getSessionPositions.mockResolvedValue(mockPositions);

      const result = await service.getSessionDetail('session123', 'user456');

      expect(result.session).toBeDefined();
      expect(result.session.sessionId).toBe('session123');
      expect(result.users).toEqual(mockUsers);
      expect(result.onlineCount).toBe(1); // 只有一个在线用户
      expect(result.activeMaps).toEqual(['plaza', 'forest']);
    });

    it('应该在会话不存在时抛出异常', async () => {
      mockLocationBroadcastCore.getSessionUsers.mockResolvedValue([]);

      await expect(service.getSessionDetail('nonexistent', 'user456')).rejects.toThrow(NotFoundException);
    });

    it('应该正确统计在线用户数', async () => {
      const allOnlineUsers = mockUsers.map(user => ({ ...user, status: SessionUserStatus.ONLINE }));
      mockLocationBroadcastCore.getSessionUsers.mockResolvedValue(allOnlineUsers);
      mockLocationBroadcastCore.getSessionPositions.mockResolvedValue(mockPositions);

      const result = await service.getSessionDetail('session123');

      expect(result.onlineCount).toBe(2);
    });
  });

  describe('querySessions', () => {
    it('应该返回空的会话列表（当前实现）', async () => {
      const query: SessionQueryRequest = {
        status: SessionStatus.ACTIVE,
        minUsers: 1,
        maxUsers: 100,
        offset: 0,
        limit: 10,
      };

      const result = await service.querySessions(query);

      expect(result.sessions).toEqual([]);
      expect(result.total).toBe(0);
      expect(result.page).toBe(1);
      expect(result.pageSize).toBe(10);
    });

    it('应该正确计算分页信息', async () => {
      const query: SessionQueryRequest = {
        offset: 20,
        limit: 5,
      };

      const result = await service.querySessions(query);

      expect(result.page).toBe(5); // (20 / 5) + 1
      expect(result.pageSize).toBe(5);
    });
  });

  describe('updateSessionConfig', () => {
    it('应该成功更新会话配置', async () => {
      const mockUsers: SessionUser[] = [
        {
          userId: 'user1',
          socketId: 'socket1',
          joinedAt: Date.now(),
          lastSeen: Date.now(),
          status: SessionUserStatus.ONLINE,
          metadata: {},
        },
      ];

      mockLocationBroadcastCore.getSessionUsers.mockResolvedValue(mockUsers);
      mockLocationBroadcastCore.getSessionPositions.mockResolvedValue([]);

      const newConfig = {
        maxUsers: 150,
        allowObservers: false,
        broadcastRange: 1500,
      };

      const result = await service.updateSessionConfig('session123', newConfig, 'user456');

      expect(result).toBeDefined();
      expect(result.sessionId).toBe('session123');
    });

    it('应该验证配置参数', async () => {
      const invalidConfigs = [
        { maxUsers: 0 },
        { maxUsers: 1001 },
        { broadcastRange: -1 },
        { broadcastRange: 10001 },
        { autoCleanupMinutes: 0 },
        { autoCleanupMinutes: 1441 },
      ];

      for (const config of invalidConfigs) {
        await expect(service.updateSessionConfig('session123', config, 'user456')).rejects.toThrow(BadRequestException);
      }
    });
  });

  describe('endSession', () => {
    it('应该成功结束会话', async () => {
      const mockUsers: SessionUser[] = [
        {
          userId: 'user1',
          socketId: 'socket1',
          joinedAt: Date.now(),
          lastSeen: Date.now(),
          status: SessionUserStatus.ONLINE,
          metadata: {},
        },
        {
          userId: 'user2',
          socketId: 'socket2',
          joinedAt: Date.now(),
          lastSeen: Date.now(),
          status: SessionUserStatus.ONLINE,
          metadata: {},
        },
      ];

      mockLocationBroadcastCore.getSessionUsers.mockResolvedValue(mockUsers);
      mockLocationBroadcastCore.removeUserFromSession.mockResolvedValue(undefined);
      mockLocationBroadcastCore.cleanupEmptySession.mockResolvedValue(undefined);

      const result = await service.endSession('session123', 'user456', 'manual_end');

      expect(result).toBe(true);
      expect(mockLocationBroadcastCore.removeUserFromSession).toHaveBeenCalledTimes(2);
      expect(mockLocationBroadcastCore.cleanupEmptySession).toHaveBeenCalledWith('session123');
    });

    it('应该在移除用户失败时继续处理其他用户', async () => {
      const mockUsers: SessionUser[] = [
        { userId: 'user1', socketId: 'socket1', joinedAt: Date.now(), lastSeen: Date.now(), status: SessionUserStatus.ONLINE, metadata: {} },
        { userId: 'user2', socketId: 'socket2', joinedAt: Date.now(), lastSeen: Date.now(), status: SessionUserStatus.ONLINE, metadata: {} },
      ];

      mockLocationBroadcastCore.getSessionUsers.mockResolvedValue(mockUsers);
      mockLocationBroadcastCore.removeUserFromSession
        .mockResolvedValueOnce(undefined) // 第一个用户成功
        .mockRejectedValueOnce(new Error('移除失败')); // 第二个用户失败
      mockLocationBroadcastCore.cleanupEmptySession.mockResolvedValue(undefined);

      const result = await service.endSession('session123', 'user456');

      expect(result).toBe(true);
      expect(mockLocationBroadcastCore.removeUserFromSession).toHaveBeenCalledTimes(2);
      expect(mockLocationBroadcastCore.cleanupEmptySession).toHaveBeenCalled();
    });
  });

  describe('validateSessionPassword', () => {
    it('应该返回验证成功（当前实现）', async () => {
      const result = await service.validateSessionPassword('session123', 'password');

      expect(result).toBe(true);
    });

    it('应该处理验证失败的情况', async () => {
      // 当前实现总是返回true，这里测试异常处理
      const result = await service.validateSessionPassword('session123', '');

      expect(result).toBe(true);
    });
  });

  describe('canUserJoinSession', () => {
    it('应该允许用户加入活跃会话', async () => {
      const mockUsers: SessionUser[] = [
        {
          userId: 'user1',
          socketId: 'socket1',
          joinedAt: Date.now(),
          lastSeen: Date.now(),
          status: SessionUserStatus.ONLINE,
          metadata: {},
        },
      ];

      mockLocationBroadcastCore.getSessionUsers.mockResolvedValue(mockUsers);
      mockLocationBroadcastCore.getSessionPositions.mockResolvedValue([]);

      const result = await service.canUserJoinSession('session123', 'user2');

      expect(result.canJoin).toBe(true);
    });

    it('应该拒绝用户加入已满的会话', async () => {
      // 创建一个满员的用户列表（假设最大用户数为100）
      const mockUsers: SessionUser[] = Array.from({ length: 100 }, (_, i) => ({
        userId: `user${i}`,
        socketId: `socket${i}`,
        joinedAt: Date.now(),
        lastSeen: Date.now(),
        status: SessionUserStatus.ONLINE,
        metadata: {},
      }));

      mockLocationBroadcastCore.getSessionUsers.mockResolvedValue(mockUsers);
      mockLocationBroadcastCore.getSessionPositions.mockResolvedValue([]);

      const result = await service.canUserJoinSession('session123', 'newuser');

      expect(result.canJoin).toBe(false);
      expect(result.reason).toBe('会话已满');
    });

    it('应该拒绝已在会话中的用户重复加入', async () => {
      const mockUsers: SessionUser[] = [
        {
          userId: 'user1',
          socketId: 'socket1',
          joinedAt: Date.now(),
          lastSeen: Date.now(),
          status: SessionUserStatus.ONLINE,
          metadata: {},
        },
      ];

      mockLocationBroadcastCore.getSessionUsers.mockResolvedValue(mockUsers);
      mockLocationBroadcastCore.getSessionPositions.mockResolvedValue([]);

      const result = await service.canUserJoinSession('session123', 'user1');

      expect(result.canJoin).toBe(false);
      expect(result.reason).toBe('用户已在会话中');
    });

    it('应该在检查失败时返回拒绝', async () => {
      mockLocationBroadcastCore.getSessionUsers.mockRejectedValue(new Error('检查失败'));

      const result = await service.canUserJoinSession('session123', 'user1');

      expect(result.canJoin).toBe(false);
      expect(result.reason).toBe('权限检查失败');
    });
  });

  describe('私有方法测试', () => {
    describe('validateCreateSessionRequest', () => {
      it('应该验证会话ID长度', async () => {
        const longSessionId = 'a'.repeat(101);
        const request: CreateSessionRequest = {
          sessionId: longSessionId,
          creatorId: 'user123',
        };

        mockLocationBroadcastCore.getSessionUsers.mockResolvedValue([]);

        await expect(service.createSession(request)).rejects.toThrow(BadRequestException);
      });
    });

    describe('validateSessionOperatorPermission', () => {
      it('应该通过权限验证（当前实现）', async () => {
        // 当前实现不进行实际的权限验证，这里测试不抛出异常
        const mockUsers: SessionUser[] = [
          {
            userId: 'user456',
            socketId: 'socket123',
            joinedAt: Date.now(),
            lastSeen: Date.now(),
            status: SessionUserStatus.ONLINE,
            metadata: {}
          }
        ];
        mockLocationBroadcastCore.getSessionUsers.mockResolvedValue(mockUsers);
        mockLocationBroadcastCore.getSessionPositions.mockResolvedValue([]);

        await expect(service.updateSessionConfig('session123', {}, 'user456')).resolves.toBeDefined();
      });
    });
  });
});