/**
 * Zulip账号关联业务服务测试
 * 
 * 功能描述：
 * - 测试ZulipAccountsBusinessService的业务逻辑
 * - 验证缓存机制和性能监控
 * - 测试异常处理和错误转换
 * 
 * 最近修改：
 * - 2026-01-12: 代码规范优化 - 提取测试数据魔法数字为常量，提升代码可读性 (修改者: moyin)
 * - 2026-01-12: 架构优化 - 从core/zulip_core移动到business/zulip，符合架构分层规范 (修改者: moyin)
 * 
 * @author angjustinl
 * @version 2.1.1
 * @since 2026-01-12
 * @lastModified 2026-01-12
 */

import { Test, TestingModule } from '@nestjs/testing';
import { ConflictException, NotFoundException } from '@nestjs/common';
import { CACHE_MANAGER } from '@nestjs/cache-manager';
import { Cache } from 'cache-manager';
import { ZulipAccountsBusinessService } from './zulip_accounts_business.service';
import { AppLoggerService } from '../../../core/utils/logger/logger.service';
import { CreateZulipAccountDto, ZulipAccountResponseDto } from '../../../core/db/zulip_accounts/zulip_accounts.dto';

describe('ZulipAccountsBusinessService', () => {
  let service: ZulipAccountsBusinessService;
  let mockRepository: any;
  let mockLogger: jest.Mocked<AppLoggerService>;
  let mockCacheManager: jest.Mocked<Cache>;

  // 测试数据常量
  const TEST_ACCOUNT_ID = BigInt(1);
  const TEST_GAME_USER_ID = BigInt(12345);
  const TEST_ZULIP_USER_ID = 67890;

  const mockAccount = {
    id: TEST_ACCOUNT_ID,
    gameUserId: TEST_GAME_USER_ID,
    zulipUserId: TEST_ZULIP_USER_ID,
    zulipEmail: 'test@example.com',
    zulipFullName: 'Test User',
    zulipApiKeyEncrypted: 'encrypted_key',
    status: 'active',
    lastVerifiedAt: new Date('2026-01-12T00:00:00Z'),
    lastSyncedAt: new Date('2026-01-12T00:00:00Z'),
    errorMessage: null,
    retryCount: 0,
    createdAt: new Date('2026-01-12T00:00:00Z'),
    updatedAt: new Date('2026-01-12T00:00:00Z'),
    gameUser: null,
  };

  beforeEach(async () => {
    mockRepository = {
      create: jest.fn(),
      findByGameUserId: jest.fn(),
      getStatusStatistics: jest.fn(),
    };

    mockLogger = {
      info: jest.fn(),
      warn: jest.fn(),
      error: jest.fn(),
      debug: jest.fn(),
    } as any;

    mockCacheManager = {
      get: jest.fn(),
      set: jest.fn(),
      del: jest.fn(),
    } as any;

    const module: TestingModule = await Test.createTestingModule({
      providers: [
        ZulipAccountsBusinessService,
        {
          provide: 'ZulipAccountsRepository',
          useValue: mockRepository,
        },
        {
          provide: AppLoggerService,
          useValue: mockLogger,
        },
        {
          provide: CACHE_MANAGER,
          useValue: mockCacheManager,
        },
      ],
    }).compile();

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

  it('should be defined', () => {
    expect(service).toBeDefined();
  });

  describe('create', () => {
    const createDto: CreateZulipAccountDto = {
      gameUserId: TEST_GAME_USER_ID.toString(),
      zulipUserId: TEST_ZULIP_USER_ID,
      zulipEmail: 'test@example.com',
      zulipFullName: 'Test User',
      zulipApiKeyEncrypted: 'encrypted_key',
      status: 'active',
    };

    it('应该成功创建Zulip账号关联', async () => {
      mockRepository.create.mockResolvedValue(mockAccount);

      const result = await service.create(createDto);

      expect(result).toBeDefined();
      expect(result.gameUserId).toBe(TEST_GAME_USER_ID.toString());
      expect(result.zulipEmail).toBe('test@example.com');
      expect(mockRepository.create).toHaveBeenCalledWith({
        gameUserId: TEST_GAME_USER_ID,
        zulipUserId: TEST_ZULIP_USER_ID,
        zulipEmail: 'test@example.com',
        zulipFullName: 'Test User',
        zulipApiKeyEncrypted: 'encrypted_key',
        status: 'active',
      });
    });

    it('应该处理重复关联异常', async () => {
      const error = new Error(`Game user ${TEST_GAME_USER_ID} already has a Zulip account`);
      mockRepository.create.mockRejectedValue(error);

      await expect(service.create(createDto)).rejects.toThrow(ConflictException);
    });

    it('应该处理Zulip用户已关联异常', async () => {
      const error = new Error(`Zulip user ${TEST_ZULIP_USER_ID} is already linked`);
      mockRepository.create.mockRejectedValue(error);

      await expect(service.create(createDto)).rejects.toThrow(ConflictException);
    });

    it('应该处理无效的游戏用户ID格式', async () => {
      const invalidDto = { ...createDto, gameUserId: 'invalid' };

      await expect(service.create(invalidDto)).rejects.toThrow(ConflictException);
    });
  });

  describe('findByGameUserId', () => {
    it('应该从缓存返回结果', async () => {
      const cachedResult: ZulipAccountResponseDto = {
        id: TEST_ACCOUNT_ID.toString(),
        gameUserId: TEST_GAME_USER_ID.toString(),
        zulipUserId: TEST_ZULIP_USER_ID,
        zulipEmail: 'test@example.com',
        zulipFullName: 'Test User',
        status: 'active',
        lastVerifiedAt: '2026-01-12T00:00:00.000Z',
        lastSyncedAt: '2026-01-12T00:00:00.000Z',
        errorMessage: null,
        retryCount: 0,
        createdAt: '2026-01-12T00:00:00.000Z',
        updatedAt: '2026-01-12T00:00:00.000Z',
        gameUser: null,
      };

      mockCacheManager.get.mockResolvedValue(cachedResult);

      const result = await service.findByGameUserId(TEST_GAME_USER_ID.toString());

      expect(result).toEqual(cachedResult);
      expect(mockRepository.findByGameUserId).not.toHaveBeenCalled();
    });

    it('应该从Repository查询并缓存结果', async () => {
      mockCacheManager.get.mockResolvedValue(null);
      mockRepository.findByGameUserId.mockResolvedValue(mockAccount);

      const result = await service.findByGameUserId(TEST_GAME_USER_ID.toString());

      expect(result).toBeDefined();
      expect(result?.gameUserId).toBe(TEST_GAME_USER_ID.toString());
      expect(mockRepository.findByGameUserId).toHaveBeenCalledWith(TEST_GAME_USER_ID, false);
      expect(mockCacheManager.set).toHaveBeenCalled();
    });

    it('应该在未找到时返回null', async () => {
      mockCacheManager.get.mockResolvedValue(null);
      mockRepository.findByGameUserId.mockResolvedValue(null);

      const result = await service.findByGameUserId(TEST_GAME_USER_ID.toString());

      expect(result).toBeNull();
    });

    it('应该处理Repository异常', async () => {
      mockCacheManager.get.mockResolvedValue(null);
      mockRepository.findByGameUserId.mockRejectedValue(new Error('Database error'));

      await expect(service.findByGameUserId(TEST_GAME_USER_ID.toString())).rejects.toThrow(ConflictException);
    });
  });

  describe('getStatusStatistics', () => {
    const mockStats = {
      active: 10,
      inactive: 5,
      suspended: 2,
      error: 1,
    };

    it('应该从缓存返回统计数据', async () => {
      const cachedStats = {
        active: 10,
        inactive: 5,
        suspended: 2,
        error: 1,
        total: 18,
      };

      mockCacheManager.get.mockResolvedValue(cachedStats);

      const result = await service.getStatusStatistics();

      expect(result).toEqual(cachedStats);
      expect(mockRepository.getStatusStatistics).not.toHaveBeenCalled();
    });

    it('应该从Repository查询并缓存统计数据', async () => {
      mockCacheManager.get.mockResolvedValue(null);
      mockRepository.getStatusStatistics.mockResolvedValue(mockStats);

      const result = await service.getStatusStatistics();

      expect(result).toEqual({
        active: 10,
        inactive: 5,
        suspended: 2,
        error: 1,
        total: 18,
      });
      expect(mockRepository.getStatusStatistics).toHaveBeenCalled();
      expect(mockCacheManager.set).toHaveBeenCalled();
    });

    it('应该处理缺失的统计字段', async () => {
      mockCacheManager.get.mockResolvedValue(null);
      mockRepository.getStatusStatistics.mockResolvedValue({
        active: 5,
        // 缺少其他字段
      });

      const result = await service.getStatusStatistics();

      expect(result).toEqual({
        active: 5,
        inactive: 0,
        suspended: 0,
        error: 0,
        total: 5,
      });
    });
  });

  describe('toResponseDto', () => {
    it('应该正确转换实体为响应DTO', () => {
      const result = (service as any).toResponseDto(mockAccount);

      expect(result).toEqual({
        id: TEST_ACCOUNT_ID.toString(),
        gameUserId: TEST_GAME_USER_ID.toString(),
        zulipUserId: TEST_ZULIP_USER_ID,
        zulipEmail: 'test@example.com',
        zulipFullName: 'Test User',
        status: 'active',
        lastVerifiedAt: '2026-01-12T00:00:00.000Z',
        lastSyncedAt: '2026-01-12T00:00:00.000Z',
        errorMessage: null,
        retryCount: 0,
        createdAt: '2026-01-12T00:00:00.000Z',
        updatedAt: '2026-01-12T00:00:00.000Z',
        gameUser: null,
      });
    });

    it('应该处理null的可选字段', () => {
      const accountWithNulls = {
        ...mockAccount,
        lastVerifiedAt: null,
        lastSyncedAt: null,
        errorMessage: null,
        gameUser: null,
      };

      const result = (service as any).toResponseDto(accountWithNulls);

      expect(result.lastVerifiedAt).toBeUndefined();
      expect(result.lastSyncedAt).toBeUndefined();
      expect(result.errorMessage).toBeNull();
      expect(result.gameUser).toBeNull();
    });
  });

  describe('parseGameUserId', () => {
    it('应该正确解析有效的游戏用户ID', () => {
      const result = (service as any).parseGameUserId(TEST_GAME_USER_ID.toString());
      expect(result).toBe(TEST_GAME_USER_ID);
    });

    it('应该在无效ID时抛出异常', () => {
      expect(() => (service as any).parseGameUserId('invalid')).toThrow(ConflictException);
    });

    it('应该处理大数字ID', () => {
      const largeId = '9007199254740991';
      const result = (service as any).parseGameUserId(largeId);
      expect(result).toBe(BigInt(largeId));
    });
  });

  describe('缓存管理', () => {
    it('应该构建正确的缓存键', () => {
      const key1 = (service as any).buildCacheKey('game_user', '12345', false);
      const key2 = (service as any).buildCacheKey('game_user', '12345', true);
      const key3 = (service as any).buildCacheKey('stats');

      expect(key1).toBe('zulip_accounts:game_user:12345');
      expect(key2).toBe('zulip_accounts:game_user:12345:with_user');
      expect(key3).toBe('zulip_accounts:stats');
    });

    it('应该清除相关缓存', async () => {
      await (service as any).clearRelatedCache(TEST_GAME_USER_ID.toString(), TEST_ZULIP_USER_ID, 'test@example.com');

      expect(mockCacheManager.del).toHaveBeenCalledTimes(7); // stats + game_user*2 + zulip_user*2 + zulip_email*2
    });

    it('应该处理缓存清除失败', async () => {
      mockCacheManager.del.mockRejectedValue(new Error('Cache error'));

      // 不应该抛出异常
      await expect((service as any).clearRelatedCache(TEST_GAME_USER_ID.toString())).resolves.not.toThrow();
      expect(mockLogger.warn).toHaveBeenCalled();
    });
  });

  describe('错误处理', () => {
    it('应该格式化Error对象', () => {
      const error = new Error('Test error');
      const result = (service as any).formatError(error);
      expect(result).toBe('Test error');
    });

    it('应该格式化非Error对象', () => {
      const result = (service as any).formatError('String error');
      expect(result).toBe('String error');
    });

    it('应该处理ConflictException', () => {
      const error = new ConflictException('Conflict');
      expect(() => (service as any).handleServiceError(error, 'test')).toThrow(ConflictException);
    });

    it('应该处理NotFoundException', () => {
      const error = new NotFoundException('Not found');
      expect(() => (service as any).handleServiceError(error, 'test')).toThrow(NotFoundException);
    });

    it('应该将其他异常转换为ConflictException', () => {
      const error = new Error('Generic error');
      expect(() => (service as any).handleServiceError(error, 'test')).toThrow(ConflictException);
    });
  });

  describe('性能监控', () => {
    it('应该创建性能监控器', () => {
      const monitor = (service as any).createPerformanceMonitor('test', { key: 'value' });

      expect(monitor).toHaveProperty('success');
      expect(monitor).toHaveProperty('error');
      expect(typeof monitor.success).toBe('function');
      expect(typeof monitor.error).toBe('function');
    });

    it('应该记录成功操作', () => {
      const monitor = (service as any).createPerformanceMonitor('test');
      monitor.success({ result: 'ok' });

      expect(mockLogger.info).toHaveBeenCalledWith(
        expect.stringContaining('成功'),
        expect.objectContaining({
          operation: 'test',
          duration: expect.any(Number)
        })
      );
    });

    it('应该记录失败操作', () => {
      const monitor = (service as any).createPerformanceMonitor('test');
      const error = new Error('Test error');

      expect(() => monitor.error(error)).toThrow();
      expect(mockLogger.error).toHaveBeenCalled();
    });
  });
});