/**
 * 聊天会话清理服务测试
 * 
 * 测试范围：
 * - 定时清理任务启动和停止
 * - 过期会话清理逻辑
 * - 手动触发清理功能
 * - 资源释放和错误处理
 * 
 * @author moyin
 * @version 1.0.0
 * @since 2026-01-14
 * @lastModified 2026-01-14
 */

import { Test, TestingModule } from '@nestjs/testing';
import { Logger } from '@nestjs/common';
import { ChatCleanupService } from './chat_cleanup.service';
import { ChatSessionService } from './chat_session.service';

describe('ChatCleanupService', () => {
  let service: ChatCleanupService;
  let sessionService: jest.Mocked<ChatSessionService>;

  beforeEach(async () => {
    const mockSessionService = {
      cleanupExpiredSessions: jest.fn(),
    };

    const module: TestingModule = await Test.createTestingModule({
      providers: [
        ChatCleanupService,
        {
          provide: ChatSessionService,
          useValue: mockSessionService,
        },
      ],
    }).compile();

    service = module.get<ChatCleanupService>(ChatCleanupService);
    sessionService = module.get(ChatSessionService);

    // 禁用日志输出
    jest.spyOn(Logger.prototype, 'log').mockImplementation();
    jest.spyOn(Logger.prototype, 'error').mockImplementation();
    jest.spyOn(Logger.prototype, 'warn').mockImplementation();
    jest.spyOn(Logger.prototype, 'debug').mockImplementation();
  });

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

  describe('初始化', () => {
    it('应该成功创建服务实例', () => {
      expect(service).toBeDefined();
    });

    it('应该在模块初始化时启动清理任务', async () => {
      jest.useFakeTimers();
      const startCleanupTaskSpy = jest.spyOn(service as any, 'startCleanupTask');

      await service.onModuleInit();

      expect(startCleanupTaskSpy).toHaveBeenCalled();
      jest.useRealTimers();
    });

    it('应该在模块销毁时停止清理任务', async () => {
      jest.useFakeTimers();
      const stopCleanupTaskSpy = jest.spyOn(service as any, 'stopCleanupTask');

      await service.onModuleDestroy();

      expect(stopCleanupTaskSpy).toHaveBeenCalled();
      jest.useRealTimers();
    });
  });

  describe('定时清理任务', () => {
    it('应该定时执行清理操作', async () => {
      jest.useFakeTimers();
      sessionService.cleanupExpiredSessions.mockResolvedValue({
        cleanedCount: 5,
        zulipQueueIds: ['queue_1', 'queue_2'],
      });

      await service.onModuleInit();

      // 快进5分钟
      jest.advanceTimersByTime(5 * 60 * 1000);
      await Promise.resolve();

      expect(sessionService.cleanupExpiredSessions).toHaveBeenCalled();
      jest.useRealTimers();
    });

    it('应该在停止任务后不再执行清理', async () => {
      jest.useFakeTimers();
      sessionService.cleanupExpiredSessions.mockResolvedValue({
        cleanedCount: 0,
        zulipQueueIds: [],
      });

      await service.onModuleInit();
      await service.onModuleDestroy();

      sessionService.cleanupExpiredSessions.mockClear();

      // 快进5分钟
      jest.advanceTimersByTime(5 * 60 * 1000);
      await Promise.resolve();

      expect(sessionService.cleanupExpiredSessions).not.toHaveBeenCalled();
      jest.useRealTimers();
    });
  });

  describe('triggerCleanup', () => {
    it('应该成功执行手动清理', async () => {
      sessionService.cleanupExpiredSessions.mockResolvedValue({
        cleanedCount: 3,
        zulipQueueIds: ['queue_1', 'queue_2', 'queue_3'],
      });

      const result = await service.triggerCleanup();

      expect(result.cleanedCount).toBe(3);
      expect(sessionService.cleanupExpiredSessions).toHaveBeenCalledWith(30);
    });

    it('应该处理清理失败', async () => {
      sessionService.cleanupExpiredSessions.mockRejectedValue(new Error('Redis error'));

      await expect(service.triggerCleanup()).rejects.toThrow('Redis error');
    });

    it('应该返回清理数量为0当没有过期会话', async () => {
      sessionService.cleanupExpiredSessions.mockResolvedValue({
        cleanedCount: 0,
        zulipQueueIds: [],
      });

      const result = await service.triggerCleanup();

      expect(result.cleanedCount).toBe(0);
    });
  });

  describe('清理逻辑', () => {
    it('应该清理多个过期会话', async () => {
      jest.useFakeTimers();
      sessionService.cleanupExpiredSessions.mockResolvedValue({
        cleanedCount: 10,
        zulipQueueIds: Array.from({ length: 10 }, (_, i) => `queue_${i}`),
      });

      await service.onModuleInit();
      jest.advanceTimersByTime(5 * 60 * 1000);
      await Promise.resolve();

      expect(sessionService.cleanupExpiredSessions).toHaveBeenCalledWith(30);
      jest.useRealTimers();
    });

    it('应该处理清理过程中的异常', async () => {
      jest.useFakeTimers();
      sessionService.cleanupExpiredSessions.mockRejectedValue(new Error('Cleanup failed'));

      await service.onModuleInit();
      jest.advanceTimersByTime(5 * 60 * 1000);
      await Promise.resolve();

      // 应该记录错误但不抛出异常
      expect(sessionService.cleanupExpiredSessions).toHaveBeenCalled();
      jest.useRealTimers();
    });

    it('应该处理Zulip队列清理', async () => {
      jest.useFakeTimers();
      const zulipQueueIds = ['queue_1', 'queue_2', 'queue_3'];
      sessionService.cleanupExpiredSessions.mockResolvedValue({
        cleanedCount: 3,
        zulipQueueIds,
      });

      await service.onModuleInit();
      jest.advanceTimersByTime(5 * 60 * 1000);
      await Promise.resolve();

      expect(sessionService.cleanupExpiredSessions).toHaveBeenCalled();
      jest.useRealTimers();
    });
  });

  describe('边界情况', () => {
    it('应该处理空的清理结果', async () => {
      sessionService.cleanupExpiredSessions.mockResolvedValue({
        cleanedCount: 0,
        zulipQueueIds: [],
      });

      const result = await service.triggerCleanup();

      expect(result.cleanedCount).toBe(0);
    });

    it('应该处理大量过期会话', async () => {
      const largeCount = 1000;
      sessionService.cleanupExpiredSessions.mockResolvedValue({
        cleanedCount: largeCount,
        zulipQueueIds: Array.from({ length: largeCount }, (_, i) => `queue_${i}`),
      });

      const result = await service.triggerCleanup();

      expect(result.cleanedCount).toBe(largeCount);
    });

    it('应该处理重复启动清理任务', async () => {
      jest.useFakeTimers();

      await service.onModuleInit();
      await service.onModuleInit();

      // 应该只有一个定时器在运行
      jest.advanceTimersByTime(5 * 60 * 1000);
      await Promise.resolve();

      jest.useRealTimers();
    });

    it('应该处理重复停止清理任务', async () => {
      jest.useFakeTimers();

      await service.onModuleInit();
      await service.onModuleDestroy();
      await service.onModuleDestroy();

      // 不应该抛出异常
      expect(service['cleanupInterval']).toBeNull();
      jest.useRealTimers();
    });
  });
});
