import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
import { DataSource, Repository } from 'typeorm';
import * as cron from 'node-cron';
import { DelaySchedulerService } from '../../src/services/delay-scheduler.service';
import type { FeieApiConfig } from '../../src/types/feie.types';
import { PrintTaskService } from '../../src/services/print-task.service';
import { OrderMt } from '@d8d/orders-module-mt';
import { FeiePrintTaskMt } from '../../src/entities';

// Mock node-cron
vi.mock('node-cron', () => {
  return {
    schedule: vi.fn()
  };
});

// Mock PrintTaskService
vi.mock('../../src/services/print-task.service', () => {
  return {
    PrintTaskService: vi.fn()
  };
});

describe('DelaySchedulerService', () => {
  let service: DelaySchedulerService;
  let mockDataSource: DataSource;
  let mockPrintTaskService: PrintTaskService;
  let mockOrderRepository: Repository<OrderMt>;
  const mockFeieConfig: FeieApiConfig = {
    baseUrl: 'http://api.feieyun.cn/Api/Open/',
    user: 'test_user',
    ukey: 'test_ukey'
  };

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

    // Mock PrintTaskService
    mockPrintTaskService = {
      getPendingDelayedTasks: vi.fn(),
      executePrintTask: vi.fn(),
      cancelPrintTask: vi.fn()
    } as any;

    // Mock Order Repository
    mockOrderRepository = {
      findOne: vi.fn()
    } as any;

    // Mock DataSource
    mockDataSource = {
      getRepository: vi.fn((entity) => {
        if (entity === OrderMt) {
          return mockOrderRepository;
        }
        return {} as any;
      }),
      query: vi.fn()
    } as any;

    // Mock PrintTaskService constructor
    vi.mocked(PrintTaskService).mockImplementation(() => mockPrintTaskService);

    service = new DelaySchedulerService(mockDataSource, mockFeieConfig);
  });

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

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

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

    it('应该初始化PrintTaskService', () => {
      expect(PrintTaskService).toHaveBeenCalledWith(mockDataSource, mockFeieConfig);
    });

    it('应该尝试获取订单仓库', () => {
      expect(mockDataSource.getRepository).toHaveBeenCalledWith(OrderMt);
    });
  });

  describe('start', () => {
    it('应该启动调度器', async () => {
      const mockCronJob = {
        start: vi.fn(),
        stop: vi.fn()
      };
      vi.mocked(cron.schedule).mockReturnValue(mockCronJob as any);

      await service.start();

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

    it('应该在调度器已在运行时抛出错误', async () => {
      const mockCronJob = {
        start: vi.fn(),
        stop: vi.fn()
      };
      vi.mocked(cron.schedule).mockReturnValue(mockCronJob as any);

      await service.start();

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

  describe('stop', () => {
    it('应该停止调度器', async () => {
      const mockCronJob = {
        start: vi.fn(),
        stop: vi.fn()
      };
      vi.mocked(cron.schedule).mockReturnValue(mockCronJob as any);

      await service.start();
      await service.stop();

      expect(mockCronJob.stop).toHaveBeenCalled();
      expect(service.getStatus().isRunning).toBe(false);
    });

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

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

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

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

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

    it('应该在调度器运行时返回正确状态', async () => {
      const mockCronJob = {
        start: vi.fn(),
        stop: vi.fn()
      };
      vi.mocked(cron.schedule).mockReturnValue(mockCronJob as any);

      await service.start();
      const status = service.getStatus();

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

  describe('triggerManualProcess', () => {
    it('应该手动触发任务处理', async () => {
      const tenantId = 1;
      const mockTasks: Partial<FeiePrintTaskMt>[] = [
        {
          id: 1,
          tenantId: 1,
          taskId: 'TASK1',
          orderId: 1001,
          printerSn: 'PRINTER1',
          content: '打印内容1',
          printType: 'RECEIPT',
          printStatus: 'DELAYED',
          retryCount: 0,
          maxRetries: 3,
          scheduledAt: new Date(),
          createdAt: new Date(),
          updatedAt: new Date()
        },
        {
          id: 2,
          tenantId: 1,
          taskId: 'TASK2',
          orderId: 1002,
          printerSn: 'PRINTER2',
          content: '打印内容2',
          printType: 'RECEIPT',
          printStatus: 'DELAYED',
          retryCount: 0,
          maxRetries: 3,
          scheduledAt: new Date(),
          createdAt: new Date(),
          updatedAt: new Date()
        }
      ];

      vi.mocked(mockPrintTaskService.getPendingDelayedTasks).mockResolvedValue(mockTasks as FeiePrintTaskMt[]);
      vi.mocked(mockPrintTaskService.executePrintTask).mockResolvedValue({} as any);

      const result = await service.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;

      vi.mocked(mockPrintTaskService.getPendingDelayedTasks).mockRejectedValue(new Error('数据库错误'));

      const result = await service.triggerManualProcess(tenantId);

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

    it('当未指定租户ID时应该返回错误', async () => {
      const result = await service.triggerManualProcess();

      expect(result).toEqual({
        success: false,
        processedTasks: 0,
        message: '未指定租户ID，无法手动处理任务'
      });
    });
  });

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

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

    it('应该在调度器运行时返回健康状态', async () => {
      const mockCronJob = {
        start: vi.fn(),
        stop: vi.fn()
      };
      vi.mocked(cron.schedule).mockReturnValue(mockCronJob as any);

      await service.start();
      const health = await service.healthCheck();

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

  describe('shouldCancelDueToRefund', () => {
    it('当订单已退款时应该返回true', async () => {
      const tenantId = 1;
      const orderId = 1001;
      const mockOrder = {
        id: orderId,
        tenantId,
        payState: 3, // 已退款
        state: 1
      };

      vi.mocked(mockOrderRepository.findOne).mockResolvedValue(mockOrder as any);

      // 通过私有方法测试
      const result = await (service as any).shouldCancelDueToRefund(tenantId, orderId);

      expect(result).toBe(true);
      expect(mockOrderRepository.findOne).toHaveBeenCalledWith({
        where: { id: orderId, tenantId }
      });
    });

    it('当订单已关闭时应该返回true', async () => {
      const tenantId = 1;
      const orderId = 1001;
      const mockOrder = {
        id: orderId,
        tenantId,
        payState: 2, // 已支付
        state: 5 // 订单关闭
      };

      vi.mocked(mockOrderRepository.findOne).mockResolvedValue(mockOrder as any);

      const result = await (service as any).shouldCancelDueToRefund(tenantId, orderId);

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

    it('当订单正常时应该返回false', async () => {
      const tenantId = 1;
      const orderId = 1001;
      const mockOrder = {
        id: orderId,
        tenantId,
        payState: 2, // 已支付
        state: 1 // 正常
      };

      vi.mocked(mockOrderRepository.findOne).mockResolvedValue(mockOrder as any);

      const result = await (service as any).shouldCancelDueToRefund(tenantId, orderId);

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

    it('当订单不存在时应该返回true', async () => {
      const tenantId = 1;
      const orderId = 1001;

      vi.mocked(mockOrderRepository.findOne).mockResolvedValue(null);

      const result = await (service as any).shouldCancelDueToRefund(tenantId, orderId);

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

    it('当订单仓库不可用时应该返回false', async () => {
      const tenantId = 1;
      const orderId = 1001;

      // 模拟订单仓库不可用
      (service as any).orderRepository = null;

      const result = await (service as any).shouldCancelDueToRefund(tenantId, orderId);

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

  describe('processSingleDelayedTask', () => {
    it('一次调度周期内每个打印任务应该只执行一次', async () => {
      const tenantId = 1;
      const mockTask: Partial<FeiePrintTaskMt> = {
        id: 1,
        tenantId: 1,
        taskId: 'TASK1',
        orderId: 1001,
        printerSn: 'PRINTER1',
        content: '打印内容',
        printType: 'RECEIPT',
        printStatus: 'DELAYED',
        retryCount: 0,
        maxRetries: 3,
        scheduledAt: new Date(),
        createdAt: new Date(),
        updatedAt: new Date()
      };

      // Mock 订单状态正常（未退款）
      vi.mocked(mockOrderRepository.findOne).mockResolvedValue({
        id: 1001,
        tenantId: 1,
        payState: 2, // 已支付
        state: 1 // 正常
      } as any);

      // Mock executePrintTask 成功
      vi.mocked(mockPrintTaskService.executePrintTask).mockResolvedValue({} as any);

      // 调用私有方法 processSingleDelayedTask
      await (service as any).processSingleDelayedTask(tenantId, mockTask);

      // 验证 executePrintTask 只被调用了一次
      expect(mockPrintTaskService.executePrintTask).toHaveBeenCalledTimes(1);
      expect(mockPrintTaskService.executePrintTask).toHaveBeenCalledWith(tenantId, 'TASK1');

      // 验证订单状态检查被调用
      expect(mockOrderRepository.findOne).toHaveBeenCalledWith({
        where: { id: 1001, tenantId: 1 }
      });
    });

    it('当任务状态为最终状态时应该跳过执行', async () => {
      const tenantId = 1;

      // 测试各种最终状态
      const finalStatuses = ['SUCCESS', 'FAILED', 'CANCELLED'];

      for (const status of finalStatuses) {
        const mockTask: Partial<FeiePrintTaskMt> = {
          id: 1,
          tenantId: 1,
          taskId: 'TASK1',
          orderId: 1001,
          printerSn: 'PRINTER1',
          content: '打印内容',
          printType: 'RECEIPT',
          printStatus: status,
          retryCount: 0,
          maxRetries: 3,
          scheduledAt: new Date(),
          createdAt: new Date(),
          updatedAt: new Date()
        };

        // 重置mock调用计数
        vi.mocked(mockPrintTaskService.executePrintTask).mockClear();

        // 调用私有方法 processSingleDelayedTask
        await (service as any).processSingleDelayedTask(tenantId, mockTask);

        // 验证 executePrintTask 没有被调用（因为任务已经是最终状态）
        expect(mockPrintTaskService.executePrintTask).not.toHaveBeenCalled();
      }
    });

    it('当订单已退款时应该取消打印任务', async () => {
      const tenantId = 1;
      const mockTask: Partial<FeiePrintTaskMt> = {
        id: 1,
        tenantId: 1,
        taskId: 'TASK1',
        orderId: 1001,
        printerSn: 'PRINTER1',
        content: '打印内容',
        printType: 'RECEIPT',
        printStatus: 'DELAYED',
        retryCount: 0,
        maxRetries: 3,
        scheduledAt: new Date(),
        createdAt: new Date(),
        updatedAt: new Date()
      };

      // Mock 订单状态为已退款
      vi.mocked(mockOrderRepository.findOne).mockResolvedValue({
        id: 1001,
        tenantId: 1,
        payState: 3, // 已退款
        state: 1
      } as any);

      // Mock cancelPrintTask
      vi.mocked(mockPrintTaskService.cancelPrintTask).mockResolvedValue({} as any);

      // 调用私有方法 processSingleDelayedTask
      await (service as any).processSingleDelayedTask(tenantId, mockTask);

      // 验证 cancelPrintTask 被调用，而不是 executePrintTask
      expect(mockPrintTaskService.cancelPrintTask).toHaveBeenCalledTimes(1);
      expect(mockPrintTaskService.cancelPrintTask).toHaveBeenCalledWith(tenantId, 'TASK1', 'REFUND');
      expect(mockPrintTaskService.executePrintTask).not.toHaveBeenCalled();
    });
  });
});