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

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

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

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

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

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

  describe('createOne', () => {
    it('should create a single page record', async () => {
      const createArgs = {
        data: {
          code: 'test_code',
          name: 'test_name',
          path: 'test_path',
          iframeUrl: 'test_iframeUrl',
        },
      } as any;
      const expectedResult = { id: 1, ...createArgs.data };

      prisma.page.create.mockResolvedValue(expectedResult);

      const result = await service.createOne(createArgs);

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

    it('should throw error when create fails', async () => {
      const createArgs = {
        data: {
          code: 'test_code',
          name: 'test_name',
          path: 'test_path',
          iframeUrl: 'test_iframeUrl',
        },
      } as any;
      const error = new Error('Database connection failed');

      prisma.page.create.mockRejectedValue(error);

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

  describe('createMany', () => {
    it('should create multiple page records with skipDuplicates', async () => {
      const data = [
        {
          code: 'test_code',
          name: 'test_name',
          path: 'test_path',
          iframeUrl: 'test_iframeUrl',
        },
        {
          code: 'test_code',
          name: 'test_name',
          path: 'test_path',
          iframeUrl: 'test_iframeUrl',
        },
      ] as any;
      const expectedResult = { count: 2 };

      prisma.page.createMany.mockResolvedValue(expectedResult);

      const result = await service.createMany(data);

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

      const result = await service.findMany(findArgs);

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

      const result = await service.findMany(findArgs);

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

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

      prisma.page.findUnique.mockResolvedValue(expectedResult);

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

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

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

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

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

  describe('updateUnique', () => {
    it('should update a single page record', async () => {
      const id = 1;
      const data = {
        code: 'test_code',
        name: 'test_name',
        path: 'test_path',
        iframeUrl: 'test_iframeUrl',
      } as any;
      const expectedResult = { id: 1, ...data };

      prisma.page.update.mockResolvedValue(expectedResult);

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

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

  describe('updateMany', () => {
    it('should update multiple page records', async () => {
      const updateArgs = {
        where: {},
        data: {
          code: 'test_code',
          name: 'test_name',
          path: 'test_path',
          iframeUrl: 'test_iframeUrl',
        },
      } as any;
      const expectedResult = { count: 5 };

      prisma.page.updateMany.mockResolvedValue(expectedResult);

      const result = await service.updateMany(updateArgs);

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

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

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

      const result = await service.deleteUnique(id);

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

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

      prisma.page.deleteMany.mockResolvedValue(expectedResult);

      const result = await service.deleteMany(deleteArgs);

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

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

      prisma.page.count.mockResolvedValue(10);

      const result = await service.count(countArgs);

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

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

      const result = await service.count();

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

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

      prisma.page.groupBy.mockResolvedValue(expectedResult);

      const result = await service.groupBy(groupByArgs);

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

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

      const result = await service.aggregate(aggregateArgs);

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