import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
import { DataSource, Repository } from 'typeorm';
import { PrintTaskService } from '../../src/services/print-task.service';
import { FeiePrintTaskMt } from '../../src/entities/feie-print-task.mt.entity';
import { FeiePrinterMt } from '../../src/entities/feie-printer.mt.entity';
import type { FeieApiConfig, CreatePrintTaskDto } from '../../src/types/feie.types';
import { FileLogger } from '@d8d/shared-utils';

// Mock dependencies
vi.mock('../../src/services/feie-api.service', () => {
  return {
    FeieApiService: vi.fn().mockImplementation(() => ({
      printReceipt: vi.fn().mockResolvedValue({ ret: 0, msg: 'success', data: '123456' }),
      queryOrderStatus: vi.fn().mockResolvedValue({ ret: 0, data: '已打印' })
    }))
  };
});

vi.mock('../../src/services/printer.service', () => {
  return {
    PrinterService: vi.fn().mockImplementation(() => ({}))
  };
});

vi.mock('@d8d/shared-crud', () => {
  return {
    GenericCrudService: vi.fn().mockImplementation(() => ({
      create: vi.fn(),
      findOne: vi.fn(),
      findMany: vi.fn(),
      update: vi.fn(),
      delete: vi.fn(),
      repository: {
        findAndCount: vi.fn()
      }
    }))
  };
});

// Mock the file logger and other exports from shared-utils
vi.mock('@d8d/shared-utils', () => {
  const mockLogger = {
    info: vi.fn(),
    warn: vi.fn(),
    error: vi.fn(),
    debug: vi.fn(),
    log: vi.fn()
  };

  return {
    createServiceLogger: vi.fn().mockReturnValue(mockLogger),
    FileLogger: vi.fn().mockImplementation(() => mockLogger),
    // Mock other exports that might be needed
    jwt: {
      sign: vi.fn(),
      verify: vi.fn()
    },
    errorHandler: vi.fn(),
    parseWithAwait: vi.fn(),
    logger: {
      info: vi.fn(),
      warn: vi.fn(),
      error: vi.fn(),
      debug: vi.fn()
    },
    redis: {
      get: vi.fn(),
      set: vi.fn(),
      del: vi.fn()
    }
  };
});

describe('PrintTaskService 日志功能测试', () => {
  let printTaskService: PrintTaskService;
  let mockDataSource: DataSource;
  let mockPrinterRepository: Repository<FeiePrinterMt>;
  let mockLogger: any;
  const mockFeieConfig: FeieApiConfig = {
    baseUrl: 'http://api.feieyun.cn/Api/Open/',
    user: 'test_user',
    ukey: 'test_ukey'
  };

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

    mockPrinterRepository = {
      findOne: vi.fn()
    } as unknown as Repository<FeiePrinterMt>;

    mockDataSource = {
      getRepository: vi.fn().mockReturnValue(mockPrinterRepository),
      query: vi.fn().mockResolvedValue([{ db_time: new Date() }])
    } as unknown as DataSource;

    // Get the mocked logger instance
    const { createServiceLogger } = require('@d8d/shared-utils');
    mockLogger = createServiceLogger();

    printTaskService = new PrintTaskService(mockDataSource, mockFeieConfig);
  });

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

  describe('日志记录功能', () => {
    it('应该在创建打印任务时记录日志', async () => {
      const tenantId = 1;
      const taskDto: CreatePrintTaskDto = {
        printerSn: 'TEST123456',
        content: '测试打印内容',
        printType: 'RECEIPT',
        delaySeconds: 0
      };

      const mockPrinter: Partial<FeiePrinterMt> = {
        id: 1,
        tenantId,
        printerSn: taskDto.printerSn,
        printerStatus: 'ACTIVE'
      };

      const mockTask: Partial<FeiePrintTaskMt> = {
        id: 1,
        tenantId,
        taskId: 'FEIE_123456789_1234',
        printerSn: taskDto.printerSn,
        content: taskDto.content,
        printType: taskDto.printType,
        printStatus: 'PENDING'
      };

      // Mock dependencies
      vi.mocked(mockPrinterRepository.findOne).mockResolvedValue(mockPrinter as FeiePrinterMt);
      (printTaskService as any).create = vi.fn().mockResolvedValue(mockTask);
      (printTaskService as any).executePrintTask = vi.fn().mockResolvedValue(mockTask);

      await printTaskService.createPrintTask(tenantId, taskDto);

      // 验证日志器被正确创建
      const { createServiceLogger } = require('@d8d/shared-utils');
      expect(createServiceLogger).toHaveBeenCalledWith('feie-print-task-service');
    });

    it('应该在获取数据库时间失败时记录警告日志', async () => {
      const tenantId = 1;
      const taskDto: CreatePrintTaskDto = {
        printerSn: 'TEST123456',
        content: '测试打印内容',
        printType: 'RECEIPT',
        delaySeconds: 10
      };

      const mockPrinter: Partial<FeiePrinterMt> = {
        id: 1,
        tenantId,
        printerSn: taskDto.printerSn,
        printerStatus: 'ACTIVE'
      };

      const mockTask: Partial<FeiePrintTaskMt> = {
        id: 1,
        tenantId,
        taskId: 'FEIE_123456789_1234',
        printerSn: taskDto.printerSn,
        content: taskDto.content,
        printType: taskDto.printType,
        printStatus: 'DELAYED'
      };

      // Mock database query to fail
      vi.mocked(mockDataSource.query).mockRejectedValue(new Error('数据库连接失败'));
      vi.mocked(mockPrinterRepository.findOne).mockResolvedValue(mockPrinter as FeiePrinterMt);
      (printTaskService as any).create = vi.fn().mockResolvedValue(mockTask);

      await printTaskService.createPrintTask(tenantId, taskDto);

      // 验证警告日志被记录
      expect(mockLogger.warn).toHaveBeenCalledWith(
        expect.stringContaining('获取数据库时间失败，使用本地时间'),
        expect.any(Error)
      );
    });

    it('应该在执行打印任务时记录调试日志', async () => {
      const tenantId = 1;
      const taskId = 'FEIE_123456789_1234';

      const mockTask: Partial<FeiePrintTaskMt> = {
        id: 1,
        tenantId,
        taskId,
        printerSn: 'TEST123456',
        content: '测试内容',
        printType: 'RECEIPT',
        printStatus: 'PENDING',
        retryCount: 0,
        maxRetries: 3
      };

      const updatedTask: Partial<FeiePrintTaskMt> = {
        ...mockTask,
        printStatus: 'SUCCESS',
        printedAt: new Date()
      };

      // Mock dependencies
      (printTaskService as any).findOne = vi.fn().mockResolvedValue(mockTask);
      (printTaskService as any).update = vi.fn().mockResolvedValue(updatedTask);

      await printTaskService.executePrintTask(tenantId, taskId);

      // 验证调试日志被记录
      expect(mockLogger.debug).toHaveBeenCalledWith(
        '调用飞鹅API打印response',
        expect.objectContaining({ response: expect.any(Object) })
      );

      expect(mockLogger.debug).toHaveBeenCalledWith(
        '更新任务状态为成功updatedTask：',
        expect.objectContaining({ updatedTask: expect.any(Object) })
      );
    });

    it('应该在打印失败时记录错误日志并安排重试', async () => {
      const tenantId = 1;
      const taskId = 'FEIE_123456789_1234';

      const mockTask: Partial<FeiePrintTaskMt> = {
        id: 1,
        tenantId,
        taskId,
        printerSn: 'TEST123456',
        content: '测试内容',
        printType: 'RECEIPT',
        printStatus: 'PENDING',
        retryCount: 0,
        maxRetries: 3
      };

      // Mock dependencies
      (printTaskService as any).findOne = vi.fn().mockResolvedValue(mockTask);
      (printTaskService as any).update = vi.fn().mockImplementation(async (id, data) => ({
        ...mockTask,
        ...data
      }));

      // Mock printReceipt to throw error
      const mockFeieApiService = {
        printReceipt: vi.fn().mockRejectedValue(new Error('打印失败'))
      };
      (printTaskService as any).feieApiService = mockFeieApiService;

      await expect(printTaskService.executePrintTask(tenantId, taskId))
        .rejects
        .toThrow('打印失败');

      // 验证错误日志被记录
      expect(mockLogger.info).toHaveBeenCalledWith(
        'executePrintTask执行失败：',
        expect.objectContaining({ error: expect.any(Error) })
      );

      expect(mockLogger.info).toHaveBeenCalledWith(
        'maxRetries最大重试次数：',
        expect.objectContaining({ maxRetries: expect.any(Number) })
      );

      expect(mockLogger.info).toHaveBeenCalledWith(
        expect.stringContaining('打印任务失败，5000ms后重试')
      );
    });

    it('应该在订单号重复时记录信息日志并标记为成功', async () => {
      const tenantId = 1;
      const taskId = 'FEIE_123456789_1234';

      const mockTask: Partial<FeiePrintTaskMt> = {
        id: 1,
        tenantId,
        taskId,
        printerSn: 'TEST123456',
        content: '测试内容',
        printType: 'RECEIPT',
        printStatus: 'PENDING',
        retryCount: 0,
        maxRetries: 3
      };

      const updatedTask: Partial<FeiePrintTaskMt> = {
        ...mockTask,
        printStatus: 'SUCCESS',
        printedAt: new Date(),
        errorMessage: '订单号重复，飞鹅API已打印',
        retryCount: 1
      };

      // Mock dependencies
      (printTaskService as any).findOne = vi.fn().mockResolvedValue(mockTask);
      (printTaskService as any).update = vi.fn().mockResolvedValue(updatedTask);

      // Mock printReceipt to throw order duplicate error
      const mockFeieApiService = {
        printReceipt: vi.fn().mockRejectedValue(new Error('错误代码: -6, 订单号重复'))
      };
      (printTaskService as any).feieApiService = mockFeieApiService;

      const result = await printTaskService.executePrintTask(tenantId, taskId);

      // 验证信息日志被记录
      expect(mockLogger.info).toHaveBeenCalledWith(
        expect.stringContaining('打印任务 订单号重复，飞鹅API已打印，标记为成功')
      );

      expect(result.printStatus).toBe('SUCCESS');
    });

    it('应该在任务已经在打印中时记录警告日志', async () => {
      const tenantId = 1;
      const taskId = 'FEIE_123456789_1234';

      const mockTask: Partial<FeiePrintTaskMt> = {
        id: 1,
        tenantId,
        taskId,
        printerSn: 'TEST123456',
        content: '测试内容',
        printType: 'RECEIPT',
        printStatus: 'PRINTING', // 已经在打印中
        retryCount: 0,
        maxRetries: 3
      };

      // Mock dependencies
      (printTaskService as any).findOne = vi.fn().mockResolvedValue(mockTask);

      const result = await printTaskService.executePrintTask(tenantId, taskId);

      // 验证警告日志被记录
      expect(mockLogger.warn).toHaveBeenCalledWith(
        expect.stringContaining('打印任务 已经在打印中，跳过重复执行')
      );

      expect(result).toEqual(mockTask);
    });

    it('应该在回滚事务失败时记录警告日志', async () => {
      const tenantId = 1;
      const taskId = 'FEIE_123456789_1234';

      // Mock dependencies to throw error during transaction
      (printTaskService as any).findOne = vi.fn().mockRejectedValue(new Error('事务执行失败'));

      // Mock queryRunner to throw error during rollback
      const mockQueryRunner = {
        connect: vi.fn(),
        startTransaction: vi.fn(),
        rollbackTransaction: vi.fn().mockRejectedValue(new Error('回滚失败')),
        release: vi.fn()
      };
      (printTaskService as any).dataSource.createQueryRunner = vi.fn().mockReturnValue(mockQueryRunner);

      await expect(printTaskService.executePrintTask(tenantId, taskId))
        .rejects
        .toThrow('事务执行失败');

      // 验证警告日志被记录
      expect(mockLogger.warn).toHaveBeenCalledWith(
        expect.stringContaining('回滚事务失败'),
        expect.any(Error)
      );
    });

    it('应该在获取租户配置失败时记录警告日志', async () => {
      const tenantId = 1;
      const key = 'test_key';
      const defaultValue = 'default_value';

      // Mock config repository to throw error
      const mockConfigRepository = {
        findOne: vi.fn().mockRejectedValue(new Error('数据库查询失败'))
      };
      (printTaskService as any).configRepository = mockConfigRepository;

      const result = await (printTaskService as any).getTenantConfigValue(tenantId, key, defaultValue);

      // 验证警告日志被记录
      expect(mockLogger.warn).toHaveBeenCalledWith(
        expect.stringContaining(`[租户${tenantId}] 获取配置失败，key: ${key}`),
        expect.any(Error)
      );

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

  describe('日志级别测试', () => {
    it('应该支持所有日志级别', async () => {
      // 测试各种日志级别
      await mockLogger.info('测试信息日志', { data: 'test' });
      await mockLogger.warn('测试警告日志', { data: 'test' });
      await mockLogger.error('测试错误日志', { data: 'test' });
      await mockLogger.debug('测试调试日志', { data: 'test' });

      expect(mockLogger.info).toHaveBeenCalledWith('测试信息日志', { data: 'test' });
      expect(mockLogger.warn).toHaveBeenCalledWith('测试警告日志', { data: 'test' });
      expect(mockLogger.error).toHaveBeenCalledWith('测试错误日志', { data: 'test' });
      expect(mockLogger.debug).toHaveBeenCalledWith('测试调试日志', { data: 'test' });
    });

    it('应该正确处理日志元数据', async () => {
      const testData = {
        taskId: 'FEIE_123456789_1234',
        tenantId: 1,
        status: 'PENDING',
        nested: {
          level1: {
            level2: 'value'
          }
        }
      };

      await mockLogger.info('测试结构化日志', testData);

      expect(mockLogger.info).toHaveBeenCalledWith(
        '测试结构化日志',
        testData
      );
    });
  });
});