import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
import { DataSource } from 'typeorm';
import * as cron from 'node-cron';
import { DelaySchedulerService } from '../../src/services/delay-scheduler.service';
import type { FeieApiConfig } from '../../src/types/feie.types';

// Mock dependencies
vi.mock('../../src/services/print-task.service', () => {
  return {
    PrintTaskService: vi.fn().mockImplementation(() => ({
      getPendingDelayedTasks: vi.fn().mockResolvedValue([]),
      executePrintTask: vi.fn().mockResolvedValue({})
    }))
  };
});

vi.mock('node-cron', () => {
  return {
    schedule: vi.fn().mockReturnValue({
      start: vi.fn(),
      stop: vi.fn()
    })
  };
});

describe('DelaySchedulerService', () => {
  let delaySchedulerService: DelaySchedulerService;
  let mockDataSource: DataSource;
  const mockFeieConfig: FeieApiConfig = {
    baseUrl: 'http://api.feieyun.cn/Api/Open/',
    user: 'test_user',
    ukey: 'test_ukey'
  };

  beforeEach(() => {
    vi.clearAllMocks();
    vi.useFakeTimers();

    mockDataSource = {} as DataSource;
    delaySchedulerService = new DelaySchedulerService(mockDataSource, mockFeieConfig);
  });

  afterEach(() => {
    vi.useRealTimers();
  });

  describe('constructor', () => {
    it('应该创建调度器实例', () => {
      expect(delaySchedulerService).toBeInstanceOf(DelaySchedulerService);
    });

    it('应该设置默认延迟时间为120秒', () => {
      expect(delaySchedulerService.getDefaultDelaySeconds()).toBe(120);
    });
  });

  describe('start', () => {
    it('应该启动调度器', async () => {
      await delaySchedulerService.start();

      expect(cron.schedule).toHaveBeenCalledWith('*/30 * * * * *', expect.any(Function));
      expect(delaySchedulerService.getStatus().isRunning).toBe(true);
    });

    it('应该在调度器已在运行时抛出错误', async () => {
      await delaySchedulerService.start();

      await expect(delaySchedulerService.start())
        .rejects
        .toThrow('调度器已经在运行中');
    });
  });

  describe('stop', () => {
    it('应该停止调度器', async () => {
      await delaySchedulerService.start();
      await delaySchedulerService.stop();

      expect(delaySchedulerService.getStatus().isRunning).toBe(false);
    });

    it('应该在调度器未运行时抛出错误', async () => {
      await expect(delaySchedulerService.stop())
        .rejects
        .toThrow('调度器未在运行中');
    });
  });

  describe('setDefaultDelaySeconds', () => {
    it('应该设置默认延迟时间', () => {
      delaySchedulerService.setDefaultDelaySeconds(180);
      expect(delaySchedulerService.getDefaultDelaySeconds()).toBe(180);
    });

    it('应该在延迟时间为负数时抛出错误', () => {
      expect(() => delaySchedulerService.setDefaultDelaySeconds(-1))
        .toThrow('延迟时间不能为负数');
    });
  });

  describe('getStatus', () => {
    it('应该返回调度器状态', () => {
      const status = delaySchedulerService.getStatus();

      expect(status).toEqual({
        isRunning: false,
        defaultDelaySeconds: 120
      });
    });

    it('应该在调度器运行时返回正确状态', async () => {
      await delaySchedulerService.start();
      const status = delaySchedulerService.getStatus();

      expect(status.isRunning).toBe(true);
      expect(status.defaultDelaySeconds).toBe(120);
    });
  });

  describe('triggerManualProcess', () => {
    it('应该手动触发任务处理', async () => {
      const tenantId = 1;
      const mockTasks = [
        { taskId: 'TASK1', printerSn: 'PRINTER1' },
        { taskId: 'TASK2', printerSn: 'PRINTER2' }
      ];

      // Mock getPendingDelayedTasks to return tasks
      const mockPrintTaskService = {
        getPendingDelayedTasks: vi.fn().mockResolvedValue(mockTasks),
        executePrintTask: vi.fn().mockResolvedValue({})
      };
      (delaySchedulerService as any).printTaskService = mockPrintTaskService;

      const result = await delaySchedulerService.triggerManualProcess(tenantId);

      expect(result).toEqual({
        success: true,
        processedTasks: 2,
        message: '成功处理 2 个延迟打印任务'
      });
      expect(mockPrintTaskService.getPendingDelayedTasks).toHaveBeenCalledWith(tenantId);
      expect(mockPrintTaskService.executePrintTask).toHaveBeenCalledTimes(2);
    });

    it('应该在处理失败时返回错误信息', async () => {
      const tenantId = 1;

      // Mock getPendingDelayedTasks to throw error
      const mockPrintTaskService = {
        getPendingDelayedTasks: vi.fn().mockRejectedValue(new Error('数据库错误'))
      };
      (delaySchedulerService as any).printTaskService = mockPrintTaskService;

      const result = await delaySchedulerService.triggerManualProcess(tenantId);

      expect(result).toEqual({
        success: false,
        processedTasks: 0,
        message: '手动处理失败: 数据库错误'
      });
    });
  });

  describe('healthCheck', () => {
    it('应该返回健康状态', async () => {
      const health = await delaySchedulerService.healthCheck();

      expect(health).toEqual({
        healthy: false,
        isRunning: false,
        timestamp: expect.any(Date)
      });
    });

    it('应该在调度器运行时返回健康状态', async () => {
      await delaySchedulerService.start();
      const health = await delaySchedulerService.healthCheck();

      expect(health).toEqual({
        healthy: true,
        isRunning: true,
        timestamp: expect.any(Date)
      });
    });
  });

  describe('processDelayedTasks', () => {
    it('应该处理延迟任务', async () => {
      // 由于processDelayedTasks是私有方法，我们通过start方法来测试
      // 当调度器启动时，它会定期调用processDelayedTasks
      await delaySchedulerService.start();

      // 手动触发一次处理
      const scheduleCallback = vi.mocked(cron.schedule).mock.calls[0][1];
      await scheduleCallback();

      // 验证处理逻辑被调用
      expect(true).toBe(true);
    });

    it('应该在处理失败时记录错误', async () => {
      const consoleErrorSpy = vi.spyOn(console, 'error').mockImplementation(() => {});

      // Mock processDelayedTasks to throw error
      const originalProcessDelayedTasks = (delaySchedulerService as any).processDelayedTasks;
      (delaySchedulerService as any).processDelayedTasks = vi.fn().mockRejectedValue(new Error('处理失败'));

      await delaySchedulerService.start();
      const scheduleCallback = vi.mocked(cron.schedule).mock.calls[0][1];
      await scheduleCallback();

      expect(consoleErrorSpy).toHaveBeenCalledWith('处理延迟打印任务失败:', expect.any(Error));

      // Restore
      (delaySchedulerService as any).processDelayedTasks = originalProcessDelayedTasks;
      consoleErrorSpy.mockRestore();
    });
  });
});