/// <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 { WorkflowNodeTypeService } from './workflowNodeType.service';

// Mock PrismaService
const mockPrismaService = {
  workflowNodeType: {
    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('WorkflowNodeTypeService', () => {
  let service: WorkflowNodeTypeService;
  let prisma: typeof mockPrismaService;

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

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

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

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

  describe('createOne', () => {
    it('should create a single workflowNodeType record', async () => {
      const createArgs = {
        data: {
          type: 'test_type',
          category: 'ENUM_VALUE',
          label: 'test_label',
          icon: 'test_icon',
          configSchema: {},
          inputSchema: {},
          outputSchema: {},
          description: 'test_description',
          isBuiltin: true,
        },
      } as any;
      const expectedResult = { id: 1, ...createArgs.data };

      prisma.workflowNodeType.create.mockResolvedValue(expectedResult);

      const result = await service.createOne(createArgs);

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

    it('should throw error when create fails', async () => {
      const createArgs = {
        data: {
          type: 'test_type',
          category: 'ENUM_VALUE',
          label: 'test_label',
          icon: 'test_icon',
          configSchema: {},
          inputSchema: {},
          outputSchema: {},
          description: 'test_description',
          isBuiltin: true,
        },
      } as any;
      const error = new Error('Database connection failed');

      prisma.workflowNodeType.create.mockRejectedValue(error);

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

  describe('createMany', () => {
    it('should create multiple workflowNodeType records with skipDuplicates', async () => {
      const data = [
        {
          type: 'test_type',
          category: 'ENUM_VALUE',
          label: 'test_label',
          icon: 'test_icon',
          configSchema: {},
          inputSchema: {},
          outputSchema: {},
          description: 'test_description',
          isBuiltin: true,
        },
        {
          type: 'test_type',
          category: 'ENUM_VALUE',
          label: 'test_label',
          icon: 'test_icon',
          configSchema: {},
          inputSchema: {},
          outputSchema: {},
          description: 'test_description',
          isBuiltin: true,
        },
      ] as any;
      const expectedResult = { count: 2 };

      prisma.workflowNodeType.createMany.mockResolvedValue(expectedResult);

      const result = await service.createMany(data);

      expect(prisma.workflowNodeType.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.workflowNodeType.count.mockResolvedValue(2);
      prisma.workflowNodeType.findMany.mockResolvedValue(mockEntities);

      const result = await service.findMany(findArgs);

      expect(prisma.workflowNodeType.count).toHaveBeenCalledWith({
        where: findArgs.where,
      });
      expect(prisma.workflowNodeType.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.workflowNodeType.count.mockResolvedValue(0);
      prisma.workflowNodeType.findMany.mockResolvedValue([]);

      const result = await service.findMany(findArgs);

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

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

      prisma.workflowNodeType.findUnique.mockResolvedValue(expectedResult);

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

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

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

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

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

  describe('updateUnique', () => {
    it('should update a single workflowNodeType record', async () => {
      const id = 1;
      const data = {
        type: 'test_type',
        category: 'ENUM_VALUE',
        label: 'test_label',
        icon: 'test_icon',
        configSchema: {},
        inputSchema: {},
        outputSchema: {},
        description: 'test_description',
        isBuiltin: true,
      } as any;
      const expectedResult = { id: 1, ...data };

      prisma.workflowNodeType.update.mockResolvedValue(expectedResult);

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

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

  describe('updateMany', () => {
    it('should update multiple workflowNodeType records', async () => {
      const updateArgs = {
        where: {},
        data: {
          type: 'test_type',
          category: 'ENUM_VALUE',
          label: 'test_label',
          icon: 'test_icon',
          configSchema: {},
          inputSchema: {},
          outputSchema: {},
          description: 'test_description',
          isBuiltin: true,
        },
      } as any;
      const expectedResult = { count: 5 };

      prisma.workflowNodeType.updateMany.mockResolvedValue(expectedResult);

      const result = await service.updateMany(updateArgs);

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

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

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

      const result = await service.deleteUnique(id);

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

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

      prisma.workflowNodeType.deleteMany.mockResolvedValue(expectedResult);

      const result = await service.deleteMany(deleteArgs);

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

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

      prisma.workflowNodeType.count.mockResolvedValue(10);

      const result = await service.count(countArgs);

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

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

      const result = await service.count();

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

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

      prisma.workflowNodeType.groupBy.mockResolvedValue(expectedResult);

      const result = await service.groupBy(groupByArgs);

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

  describe('aggregate', () => {
    it('should aggregate workflowNodeType 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.workflowNodeType.aggregate.mockResolvedValue(expectedResult);

      const result = await service.aggregate(aggregateArgs);

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