/// <reference types="jest" />
import { ConfigService } from '@nestjs/config';
import { Test, TestingModule } from '@nestjs/testing';
import { WINSTON_MODULE_PROVIDER } from 'nest-winston';

// Mock nestjs-prisma 模块
jest.mock('nestjs-prisma', () => ({
  PrismaService: jest.fn(),
}));

import { PrismaService } from 'nestjs-prisma';
import { WorkflowEventOutboxService } from './workflowEventOutbox.service';

// Mock PrismaService
const mockPrismaService = {
  workflowEventOutbox: {
    create: jest.fn(),
    createMany: jest.fn(),
    findMany: jest.fn(),
    findUnique: jest.fn(),
    update: jest.fn(),
    updateMany: jest.fn(),
    delete: jest.fn(),
    deleteMany: jest.fn(),
    count: jest.fn(),
    groupBy: jest.fn(),
    aggregate: jest.fn(),
  },
};

// Mock Logger
const mockLogger = {
  child: jest.fn().mockReturnThis(),
  info: jest.fn(),
  error: jest.fn(),
  warn: jest.fn(),
  debug: jest.fn(),
};

// Mock ConfigService
const mockConfigService = {
  get: jest.fn(),
};

describe('WorkflowEventOutboxService', () => {
  let service: WorkflowEventOutboxService;
  let prisma: typeof mockPrismaService;

  beforeEach(async () => {
    // 每个测试前重置所有 mock
    jest.clearAllMocks();

    const module: TestingModule = await Test.createTestingModule({
      providers: [
        WorkflowEventOutboxService,
        { provide: PrismaService, useValue: mockPrismaService },
        { provide: ConfigService, useValue: mockConfigService },
        { provide: WINSTON_MODULE_PROVIDER, useValue: mockLogger },
      ],
    }).compile();

    service = module.get<WorkflowEventOutboxService>(
      WorkflowEventOutboxService,
    );
    prisma = mockPrismaService;
  });

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

  describe('createOne', () => {
    it('should create a single workflowEventOutbox record', async () => {
      const createArgs = {
        data: {
          eventName: 'test_eventName',
          payload: {},
          processed: false,
          processedAt: new Date(),
        },
      } as any;
      const expectedResult = { id: 1, ...createArgs.data };

      prisma.workflowEventOutbox.create.mockResolvedValue(expectedResult);

      const result = await service.createOne(createArgs);

      expect(prisma.workflowEventOutbox.create).toHaveBeenCalledWith(
        createArgs,
      );
      expect(prisma.workflowEventOutbox.create).toHaveBeenCalledTimes(1);
      expect(result).toEqual(expectedResult);
    });

    it('should throw error when create fails', async () => {
      const createArgs = {
        data: {
          eventName: 'test_eventName',
          payload: {},
          processed: false,
          processedAt: new Date(),
        },
      } as any;
      const error = new Error('Database connection failed');

      prisma.workflowEventOutbox.create.mockRejectedValue(error);

      await expect(service.createOne(createArgs)).rejects.toThrow(
        'Database connection failed',
      );
    });
  });

  describe('createMany', () => {
    it('should create multiple workflowEventOutbox records with skipDuplicates', async () => {
      const data = [
        {
          eventName: 'test_eventName',
          payload: {},
          processed: false,
          processedAt: new Date(),
        },
        {
          eventName: 'test_eventName',
          payload: {},
          processed: false,
          processedAt: new Date(),
        },
      ] as any;
      const expectedResult = { count: 2 };

      prisma.workflowEventOutbox.createMany.mockResolvedValue(expectedResult);

      const result = await service.createMany(data);

      expect(prisma.workflowEventOutbox.createMany).toHaveBeenCalledWith({
        data,
        skipDuplicates: true,
      });
      expect(result).toEqual(expectedResult);
    });
  });

  describe('findMany', () => {
    it('should return entities with itemCount', async () => {
      const findArgs = {
        where: {},
        take: 10,
        skip: 0,
      } as any;
      const mockEntities = [{ id: 1 }, { id: 2 }];

      prisma.workflowEventOutbox.count.mockResolvedValue(2);
      prisma.workflowEventOutbox.findMany.mockResolvedValue(mockEntities);

      const result = await service.findMany(findArgs);

      expect(prisma.workflowEventOutbox.count).toHaveBeenCalledWith({
        where: findArgs.where,
      });
      expect(prisma.workflowEventOutbox.findMany).toHaveBeenCalledWith(
        findArgs,
      );
      expect(result).toEqual({
        itemCount: 2,
        entities: mockEntities,
      });
    });

    it('should return empty result when no records found', async () => {
      const findArgs = { where: {} } as any;

      prisma.workflowEventOutbox.count.mockResolvedValue(0);
      prisma.workflowEventOutbox.findMany.mockResolvedValue([]);

      const result = await service.findMany(findArgs);

      expect(result).toEqual({ itemCount: 0, entities: [] });
    });
  });

  describe('findUnique', () => {
    it('should find a single workflowEventOutbox record by id', async () => {
      const id = 1;
      const select = { id: true };
      const expectedResult = { id: 1 };

      prisma.workflowEventOutbox.findUnique.mockResolvedValue(expectedResult);

      const result = await service.findUnique(id, select);

      expect(prisma.workflowEventOutbox.findUnique).toHaveBeenCalledWith({
        where: { id },
        select,
      });
      expect(result).toEqual(expectedResult);
    });

    it('should return null when record not found', async () => {
      prisma.workflowEventOutbox.findUnique.mockResolvedValue(null);

      const result = await service.findUnique(1, { id: true });

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

  describe('updateUnique', () => {
    it('should update a single workflowEventOutbox record', async () => {
      const id = 1;
      const data = {
        eventName: 'test_eventName',
        payload: {},
        processed: false,
        processedAt: new Date(),
      } as any;
      const expectedResult = { id: 1, ...data };

      prisma.workflowEventOutbox.update.mockResolvedValue(expectedResult);

      const result = await service.updateUnique(id, data);

      expect(prisma.workflowEventOutbox.update).toHaveBeenCalledWith({
        where: { id },
        data,
      });
      expect(result).toEqual(expectedResult);
    });
  });

  describe('updateMany', () => {
    it('should update multiple workflowEventOutbox records', async () => {
      const updateArgs = {
        where: {},
        data: {
          eventName: 'test_eventName',
          payload: {},
          processed: false,
          processedAt: new Date(),
        },
      } as any;
      const expectedResult = { count: 5 };

      prisma.workflowEventOutbox.updateMany.mockResolvedValue(expectedResult);

      const result = await service.updateMany(updateArgs);

      expect(prisma.workflowEventOutbox.updateMany).toHaveBeenCalledWith(
        updateArgs,
      );
      expect(result).toEqual(expectedResult);
    });
  });

  describe('deleteUnique', () => {
    it('should delete a single workflowEventOutbox record and return count', async () => {
      const id = 1;

      prisma.workflowEventOutbox.delete.mockResolvedValue({ id: 1 });

      const result = await service.deleteUnique(id);

      expect(prisma.workflowEventOutbox.delete).toHaveBeenCalledWith({
        where: { id },
      });
      expect(result).toEqual({ count: 1 });
    });
  });

  describe('deleteMany', () => {
    it('should delete multiple workflowEventOutbox records', async () => {
      const deleteArgs = { where: {} } as any;
      const expectedResult = { count: 3 };

      prisma.workflowEventOutbox.deleteMany.mockResolvedValue(expectedResult);

      const result = await service.deleteMany(deleteArgs);

      expect(prisma.workflowEventOutbox.deleteMany).toHaveBeenCalledWith(
        deleteArgs,
      );
      expect(result).toEqual(expectedResult);
    });
  });

  describe('count', () => {
    it('should count workflowEventOutbox records with filter', async () => {
      const countArgs = { where: {} } as any;

      prisma.workflowEventOutbox.count.mockResolvedValue(10);

      const result = await service.count(countArgs);

      expect(prisma.workflowEventOutbox.count).toHaveBeenCalledWith(countArgs);
      expect(result).toBe(10);
    });

    it('should count all records when no filter provided', async () => {
      prisma.workflowEventOutbox.count.mockResolvedValue(100);

      const result = await service.count();

      expect(prisma.workflowEventOutbox.count).toHaveBeenCalledWith(undefined);
      expect(result).toBe(100);
    });
  });

  describe('groupBy', () => {
    it('should group workflowEventOutbox records by specified field', async () => {
      const groupByArgs = {
        by: ['id'],
        _count: { id: true },
      } as any;
      const expectedResult = [{ id: 1, _count: { id: 50 } }];

      prisma.workflowEventOutbox.groupBy.mockResolvedValue(expectedResult);

      const result = await service.groupBy(groupByArgs);

      expect(prisma.workflowEventOutbox.groupBy).toHaveBeenCalledWith(
        groupByArgs,
      );
      expect(result).toEqual(expectedResult);
    });
  });

  describe('aggregate', () => {
    it('should aggregate workflowEventOutbox records', async () => {
      const aggregateArgs = {
        _count: true,
        _max: { id: true },
        _min: { id: true },
      } as any;
      const expectedResult = {
        _count: 100,
        _max: { id: 1 },
        _min: { id: 1 },
      };

      prisma.workflowEventOutbox.aggregate.mockResolvedValue(expectedResult);

      const result = await service.aggregate(aggregateArgs);

      expect(prisma.workflowEventOutbox.aggregate).toHaveBeenCalledWith(
        aggregateArgs,
      );
      expect(result).toEqual(expectedResult);
    });
  });
});
