/**
 * 位置广播控制器单元测试
 * 
 * 功能描述：
 * - 测试位置广播HTTP API控制器的功能
 * - 验证API端点的请求处理和响应格式
 * - 确保权限验证和错误处理的正确性
 * - 提供完整的API测试覆盖率
 * 
 * 测试范围：
 * - HTTP API端点的功能测试
 * - 请求参数验证和响应格式
 * - 权限控制和安全验证
 * - 异常处理和错误响应
 * 
 * @author moyin
 * @version 1.0.0
 * @since 2026-01-08
 * @lastModified 2026-01-08
 */

import { Test, TestingModule } from '@nestjs/testing';
import { HttpException, HttpStatus } from '@nestjs/common';
import { LocationBroadcastController } from './location_broadcast.controller';
import { LocationBroadcastService } from './services/location_broadcast.service';
import { LocationSessionService } from './services/location_session.service';
import { LocationPositionService } from './services/location_position.service';
import { JwtPayload } from '../../core/login_core/login_core.service';
import { CreateSessionDto, SessionQueryDto, PositionQueryDto, UpdateSessionConfigDto } from './dto/api.dto';
import { GameSession, SessionStatus } from '../../core/location_broadcast_core/session.interface';

describe('LocationBroadcastController', () => {
  let controller: LocationBroadcastController;
  let mockLocationBroadcastService: any;
  let mockLocationSessionService: any;
  let mockLocationPositionService: any;

  const mockUser: JwtPayload = {
    sub: 'user123',
    username: 'testuser',
    role: 1,
    email: 'test@example.com',
    type: 'access',
  };

  const mockAdminUser: JwtPayload = {
    sub: 'admin123',
    username: 'admin',
    role: 2,
    email: 'admin@example.com',
    type: 'access',
  };

  beforeEach(async () => {
    // 创建模拟服务
    mockLocationBroadcastService = {
      cleanupUserData: jest.fn(),
    };

    mockLocationSessionService = {
      createSession: jest.fn(),
      querySessions: jest.fn(),
      getSessionDetail: jest.fn(),
      updateSessionConfig: jest.fn(),
      endSession: jest.fn(),
    };

    mockLocationPositionService = {
      queryPositions: jest.fn(),
      getPositionStats: jest.fn(),
      getPositionHistory: jest.fn(),
    };

    // 创建模拟的LoginCoreService
    const mockLoginCoreService = {
      validateToken: jest.fn(),
      getUserFromToken: jest.fn(),
    };

    const module: TestingModule = await Test.createTestingModule({
      controllers: [LocationBroadcastController],
      providers: [
        {
          provide: LocationBroadcastService,
          useValue: mockLocationBroadcastService,
        },
        {
          provide: LocationSessionService,
          useValue: mockLocationSessionService,
        },
        {
          provide: LocationPositionService,
          useValue: mockLocationPositionService,
        },
        {
          provide: 'LoginCoreService',
          useValue: mockLoginCoreService,
        },
      ],
    })
    .overrideGuard(require('../../business/auth/jwt_auth.guard').JwtAuthGuard)
    .useValue({
      canActivate: jest.fn(() => true),
    })
    .compile();

    controller = module.get<LocationBroadcastController>(LocationBroadcastController);
  });

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

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

    const mockSession: GameSession = {
      sessionId: 'session123',
      users: [],
      createdAt: Date.now(),
      lastActivity: Date.now(),
      status: SessionStatus.ACTIVE,
      config: {
        maxUsers: 50,
        timeoutSeconds: 3600,
        allowObservers: true,
        requirePassword: false,
        broadcastRange: 1000,
      },
      metadata: {
        name: '测试会话',
        description: '这是一个测试会话',
        creatorId: 'user123',
      },
    };

    it('应该成功创建会话', async () => {
      mockLocationSessionService.createSession.mockResolvedValue(mockSession);

      const result = await controller.createSession(mockCreateSessionDto, mockUser);

      expect(result.success).toBe(true);
      expect(result.data.sessionId).toBe('session123');
      expect(result.message).toBe('会话创建成功');
      expect(mockLocationSessionService.createSession).toHaveBeenCalledWith({
        sessionId: mockCreateSessionDto.sessionId,
        creatorId: mockUser.sub,
        name: mockCreateSessionDto.name,
        description: mockCreateSessionDto.description,
        maxUsers: mockCreateSessionDto.maxUsers,
        allowObservers: mockCreateSessionDto.allowObservers,
        broadcastRange: mockCreateSessionDto.broadcastRange,
        metadata: mockCreateSessionDto.metadata,
      });
    });

    it('应该处理会话创建失败', async () => {
      mockLocationSessionService.createSession.mockRejectedValue(new Error('创建失败'));

      await expect(controller.createSession(mockCreateSessionDto, mockUser))
        .rejects.toThrow(HttpException);
    });

    it('应该处理HTTP异常', async () => {
      const httpException = new HttpException('会话ID已存在', HttpStatus.CONFLICT);
      mockLocationSessionService.createSession.mockRejectedValue(httpException);

      await expect(controller.createSession(mockCreateSessionDto, mockUser))
        .rejects.toThrow(httpException);
    });
  });

  describe('querySessions', () => {
    const mockQueryDto: SessionQueryDto = {
      status: 'active',
      minUsers: 1,
      maxUsers: 100,
      offset: 0,
      limit: 10,
    };

    const mockQueryResult = {
      sessions: [],
      total: 0,
      page: 1,
      pageSize: 10,
    };

    it('应该成功查询会话列表', async () => {
      mockLocationSessionService.querySessions.mockResolvedValue(mockQueryResult);

      const result = await controller.querySessions(mockQueryDto);

      expect(result.success).toBe(true);
      expect(result.data).toEqual(mockQueryResult);
      expect(mockLocationSessionService.querySessions).toHaveBeenCalledWith({
        status: mockQueryDto.status,
        minUsers: mockQueryDto.minUsers,
        maxUsers: mockQueryDto.maxUsers,
        publicOnly: mockQueryDto.publicOnly,
        offset: 0,
        limit: 10,
      });
    });

    it('应该处理查询失败', async () => {
      mockLocationSessionService.querySessions.mockRejectedValue(new Error('查询失败'));

      await expect(controller.querySessions(mockQueryDto))
        .rejects.toThrow(HttpException);
    });
  });

  describe('getSessionDetail', () => {
    const mockSessionDetail = {
      session: {
        sessionId: 'session123',
        users: [],
        createdAt: Date.now(),
        lastActivity: Date.now(),
        status: SessionStatus.ACTIVE,
        config: { maxUsers: 100, timeoutSeconds: 3600, allowObservers: true, requirePassword: false },
        metadata: {},
      },
      users: [],
      onlineCount: 0,
      activeMaps: [],
    };

    it('应该成功获取会话详情', async () => {
      mockLocationSessionService.getSessionDetail.mockResolvedValue(mockSessionDetail);

      const result = await controller.getSessionDetail('session123', mockUser);

      expect(result.success).toBe(true);
      expect(result.data).toEqual(mockSessionDetail);
      expect(mockLocationSessionService.getSessionDetail).toHaveBeenCalledWith('session123', mockUser.sub);
    });

    it('应该处理会话不存在', async () => {
      const notFoundException = new HttpException('会话不存在', HttpStatus.NOT_FOUND);
      mockLocationSessionService.getSessionDetail.mockRejectedValue(notFoundException);

      await expect(controller.getSessionDetail('nonexistent', mockUser))
        .rejects.toThrow(notFoundException);
    });
  });

  describe('updateSessionConfig', () => {
    const mockUpdateConfigDto: UpdateSessionConfigDto = {
      maxUsers: 150,
      allowObservers: false,
      broadcastRange: 1500,
    };

    const mockUpdatedSession: GameSession = {
      sessionId: 'session123',
      users: [],
      createdAt: Date.now(),
      lastActivity: Date.now(),
      status: SessionStatus.ACTIVE,
      config: {
        maxUsers: 150,
        timeoutSeconds: 3600,
        allowObservers: false,
        requirePassword: false,
        broadcastRange: 1500,
      },
      metadata: {},
    };

    it('应该成功更新会话配置', async () => {
      mockLocationSessionService.updateSessionConfig.mockResolvedValue(mockUpdatedSession);

      const result = await controller.updateSessionConfig('session123', mockUpdateConfigDto, mockUser);

      expect(result.success).toBe(true);
      expect(result.data).toEqual(mockUpdatedSession);
      expect(result.message).toBe('会话配置更新成功');
      expect(mockLocationSessionService.updateSessionConfig).toHaveBeenCalledWith(
        'session123',
        mockUpdateConfigDto,
        mockUser.sub,
      );
    });

    it('应该处理权限不足', async () => {
      const forbiddenException = new HttpException('权限不足', HttpStatus.FORBIDDEN);
      mockLocationSessionService.updateSessionConfig.mockRejectedValue(forbiddenException);

      await expect(controller.updateSessionConfig('session123', mockUpdateConfigDto, mockUser))
        .rejects.toThrow(forbiddenException);
    });
  });

  describe('endSession', () => {
    it('应该成功结束会话', async () => {
      mockLocationSessionService.endSession.mockResolvedValue(true);

      const result = await controller.endSession('session123', mockUser);

      expect(result.success).toBe(true);
      expect(result.message).toBe('会话结束成功');
      expect(mockLocationSessionService.endSession).toHaveBeenCalledWith('session123', mockUser.sub);
    });

    it('应该处理结束会话失败', async () => {
      mockLocationSessionService.endSession.mockRejectedValue(new Error('结束失败'));

      await expect(controller.endSession('session123', mockUser))
        .rejects.toThrow(HttpException);
    });
  });

  describe('queryPositions', () => {
    const mockQueryDto: PositionQueryDto = {
      mapId: 'plaza',
      limit: 50,
      offset: 0,
    };

    const mockQueryResult = {
      positions: [
        {
          userId: 'user1',
          x: 100,
          y: 200,
          mapId: 'plaza',
          timestamp: Date.now(),
          metadata: {},
        },
      ],
      total: 1,
      timestamp: Date.now(),
    };

    it('应该成功查询位置信息', async () => {
      mockLocationPositionService.queryPositions.mockResolvedValue(mockQueryResult);

      const result = await controller.queryPositions(mockQueryDto);

      expect(result.success).toBe(true);
      expect(result.data).toEqual(mockQueryResult);
      expect(mockLocationPositionService.queryPositions).toHaveBeenCalledWith({
        userIds: undefined,
        mapId: mockQueryDto.mapId,
        sessionId: mockQueryDto.sessionId,
        range: undefined,
        pagination: {
          offset: 0,
          limit: 50,
        },
      });
    });

    it('应该处理用户ID列表', async () => {
      const queryWithUserIds = { ...mockQueryDto, userIds: 'user1,user2,user3' };
      mockLocationPositionService.queryPositions.mockResolvedValue(mockQueryResult);

      await controller.queryPositions(queryWithUserIds);

      expect(mockLocationPositionService.queryPositions).toHaveBeenCalledWith(
        expect.objectContaining({
          userIds: ['user1', 'user2', 'user3'],
        }),
      );
    });

    it('应该处理范围查询', async () => {
      const queryWithRange = {
        ...mockQueryDto,
        centerX: 100,
        centerY: 200,
        radius: 50,
      };
      mockLocationPositionService.queryPositions.mockResolvedValue(mockQueryResult);

      await controller.queryPositions(queryWithRange);

      expect(mockLocationPositionService.queryPositions).toHaveBeenCalledWith(
        expect.objectContaining({
          range: {
            centerX: 100,
            centerY: 200,
            radius: 50,
          },
        }),
      );
    });
  });

  describe('getPositionStats', () => {
    const mockStatsResult = {
      totalUsers: 100,
      onlineUsers: 85,
      activeMaps: 5,
      mapDistribution: { plaza: 30, forest: 25, mountain: 30 },
      updateFrequency: 2.5,
      timestamp: Date.now(),
    };

    it('应该成功获取位置统计', async () => {
      mockLocationPositionService.getPositionStats.mockResolvedValue(mockStatsResult);

      const result = await controller.getPositionStats('plaza', 'session123');

      expect(result.success).toBe(true);
      expect(result.data).toEqual(mockStatsResult);
      expect(mockLocationPositionService.getPositionStats).toHaveBeenCalledWith({
        mapId: 'plaza',
        sessionId: 'session123',
      });
    });

    it('应该处理统计获取失败', async () => {
      mockLocationPositionService.getPositionStats.mockRejectedValue(new Error('统计失败'));

      await expect(controller.getPositionStats())
        .rejects.toThrow(HttpException);
    });
  });

  describe('getUserPositionHistory', () => {
    const mockHistoryResult = [
      {
        userId: 'user123',
        x: 100,
        y: 200,
        mapId: 'plaza',
        timestamp: Date.now() - 60000,
        sessionId: 'session123',
        metadata: {},
      },
    ];

    it('应该允许用户查看自己的位置历史', async () => {
      mockLocationPositionService.getPositionHistory.mockResolvedValue(mockHistoryResult);

      const result = await controller.getUserPositionHistory('user123', mockUser, 'plaza', 100);

      expect(result.success).toBe(true);
      expect(result.data).toEqual(mockHistoryResult);
      expect(mockLocationPositionService.getPositionHistory).toHaveBeenCalledWith({
        userId: 'user123',
        mapId: 'plaza',
        limit: 100,
      });
    });

    it('应该允许管理员查看任何用户的位置历史', async () => {
      mockLocationPositionService.getPositionHistory.mockResolvedValue(mockHistoryResult);

      const result = await controller.getUserPositionHistory('user456', mockAdminUser, 'plaza', 100);

      expect(result.success).toBe(true);
      expect(result.data).toEqual(mockHistoryResult);
    });

    it('应该拒绝普通用户查看其他用户的位置历史', async () => {
      await expect(controller.getUserPositionHistory('user456', mockUser, 'plaza', 100))
        .rejects.toThrow(HttpException);

      expect(mockLocationPositionService.getPositionHistory).not.toHaveBeenCalled();
    });

    it('应该处理历史获取失败', async () => {
      mockLocationPositionService.getPositionHistory.mockRejectedValue(new Error('获取失败'));

      await expect(controller.getUserPositionHistory('user123', mockUser))
        .rejects.toThrow(HttpException);
    });
  });

  describe('cleanupUserData', () => {
    it('应该允许用户清理自己的数据', async () => {
      mockLocationBroadcastService.cleanupUserData.mockResolvedValue(true);

      const result = await controller.cleanupUserData('user123', mockUser);

      expect(result.success).toBe(true);
      expect(result.message).toBe('用户数据清理成功');
      expect(mockLocationBroadcastService.cleanupUserData).toHaveBeenCalledWith('user123');
    });

    it('应该允许管理员清理任何用户的数据', async () => {
      mockLocationBroadcastService.cleanupUserData.mockResolvedValue(true);

      const result = await controller.cleanupUserData('user456', mockAdminUser);

      expect(result.success).toBe(true);
      expect(result.message).toBe('用户数据清理成功');
    });

    it('应该拒绝普通用户清理其他用户的数据', async () => {
      await expect(controller.cleanupUserData('user456', mockUser))
        .rejects.toThrow(HttpException);

      expect(mockLocationBroadcastService.cleanupUserData).not.toHaveBeenCalled();
    });

    it('应该处理清理失败', async () => {
      mockLocationBroadcastService.cleanupUserData.mockResolvedValue(false);

      await expect(controller.cleanupUserData('user123', mockUser))
        .rejects.toThrow(HttpException);
    });

    it('应该处理清理异常', async () => {
      mockLocationBroadcastService.cleanupUserData.mockRejectedValue(new Error('清理异常'));

      await expect(controller.cleanupUserData('user123', mockUser))
        .rejects.toThrow(HttpException);
    });
  });

  describe('错误处理', () => {
    it('应该正确处理HTTP异常', async () => {
      const httpException = new HttpException('测试异常', HttpStatus.BAD_REQUEST);
      mockLocationSessionService.createSession.mockRejectedValue(httpException);

      const createSessionDto: CreateSessionDto = {
        sessionId: 'test',
      };

      await expect(controller.createSession(createSessionDto, mockUser))
        .rejects.toThrow(httpException);
    });

    it('应该将普通异常转换为HTTP异常', async () => {
      const normalError = new Error('普通错误');
      mockLocationSessionService.createSession.mockRejectedValue(normalError);

      const createSessionDto: CreateSessionDto = {
        sessionId: 'test',
      };

      try {
        await controller.createSession(createSessionDto, mockUser);
      } catch (error) {
        expect(error).toBeInstanceOf(HttpException);
        expect((error as HttpException).getStatus()).toBe(HttpStatus.INTERNAL_SERVER_ERROR);
      }
    });
  });
});