import { Test, TestingModule } from '@nestjs/testing';
import { AuditLogRepository } from '../../src/infrastructure/persistence/repositories/audit-log.repository';
import { PrismaService } from '../../src/infrastructure/persistence/prisma/prisma.service';
import { MockPrismaService } from '../utils/mock-prisma.service';

describe('AuditLogRepository Integration', () => {
  let repository: AuditLogRepository;
  let mockPrisma: MockPrismaService;

  beforeEach(async () => {
    mockPrisma = new MockPrismaService();

    const module: TestingModule = await Test.createTestingModule({
      providers: [
        AuditLogRepository,
        { provide: PrismaService, useValue: mockPrisma },
      ],
    }).compile();

    repository = module.get<AuditLogRepository>(AuditLogRepository);
  });

  afterEach(() => {
    mockPrisma.reset();
  });

  describe('log', () => {
    it('should create an audit log entry for STORE action', async () => {
      await repository.log({
        shareId: BigInt(1),
        userId: BigInt(12345),
        action: 'STORE',
        sourceService: 'identity-service',
        sourceIp: '192.168.1.1',
        success: true,
      });

      expect(mockPrisma.shareAccessLog.create).toHaveBeenCalledWith({
        data: expect.objectContaining({
          shareId: BigInt(1),
          userId: BigInt(12345),
          action: 'STORE',
          sourceService: 'identity-service',
          sourceIp: '192.168.1.1',
          success: true,
        }),
      });
    });

    it('should create an audit log entry for RETRIEVE action', async () => {
      await repository.log({
        shareId: BigInt(2),
        userId: BigInt(67890),
        action: 'RETRIEVE',
        sourceService: 'recovery-service',
        sourceIp: '10.0.0.1',
        success: true,
      });

      expect(mockPrisma.shareAccessLog.create).toHaveBeenCalledWith({
        data: expect.objectContaining({
          action: 'RETRIEVE',
          sourceService: 'recovery-service',
        }),
      });
    });

    it('should create an audit log entry for REVOKE action', async () => {
      await repository.log({
        shareId: BigInt(3),
        userId: BigInt(11111),
        action: 'REVOKE',
        sourceService: 'identity-service',
        sourceIp: '172.16.0.1',
        success: true,
      });

      expect(mockPrisma.shareAccessLog.create).toHaveBeenCalledWith({
        data: expect.objectContaining({
          action: 'REVOKE',
        }),
      });
    });

    it('should log failed operations with error message', async () => {
      await repository.log({
        shareId: BigInt(0),
        userId: BigInt(22222),
        action: 'RETRIEVE',
        sourceService: 'identity-service',
        sourceIp: '192.168.1.100',
        success: false,
        errorMessage: 'Share not found',
      });

      expect(mockPrisma.shareAccessLog.create).toHaveBeenCalledWith({
        data: expect.objectContaining({
          success: false,
          errorMessage: 'Share not found',
        }),
      });
    });

    it('should not throw when database error occurs', async () => {
      mockPrisma.shareAccessLog.create.mockRejectedValueOnce(new Error('DB Error'));

      await expect(
        repository.log({
          shareId: BigInt(1),
          userId: BigInt(12345),
          action: 'STORE',
          sourceService: 'identity-service',
          sourceIp: '127.0.0.1',
          success: true,
        }),
      ).resolves.not.toThrow();
    });
  });

  describe('findByShareId', () => {
    it('should find logs by share ID', async () => {
      const shareId = BigInt(100);

      // Create some logs
      await repository.log({
        shareId,
        userId: BigInt(12345),
        action: 'STORE',
        sourceService: 'identity-service',
        sourceIp: '127.0.0.1',
        success: true,
      });
      await repository.log({
        shareId,
        userId: BigInt(12345),
        action: 'RETRIEVE',
        sourceService: 'identity-service',
        sourceIp: '127.0.0.1',
        success: true,
      });

      await repository.findByShareId(shareId);

      expect(mockPrisma.shareAccessLog.findMany).toHaveBeenCalledWith({
        where: { shareId },
        orderBy: { createdAt: 'desc' },
        take: 100,
      });
    });

    it('should respect limit parameter', async () => {
      await repository.findByShareId(BigInt(1), 50);

      expect(mockPrisma.shareAccessLog.findMany).toHaveBeenCalledWith(
        expect.objectContaining({
          take: 50,
        }),
      );
    });
  });

  describe('findByUserId', () => {
    it('should find logs by user ID', async () => {
      const userId = BigInt(55555);

      await repository.findByUserId(userId);

      expect(mockPrisma.shareAccessLog.findMany).toHaveBeenCalledWith({
        where: { userId },
        orderBy: { createdAt: 'desc' },
        take: 100,
      });
    });
  });

  describe('countRetrievesByUserToday', () => {
    it('should count today\'s retrieves for a user', async () => {
      const userId = BigInt(33333);

      await repository.countRetrievesByUserToday(userId);

      expect(mockPrisma.shareAccessLog.count).toHaveBeenCalledWith({
        where: {
          userId,
          action: 'RETRIEVE',
          createdAt: expect.objectContaining({
            gte: expect.any(Date),
          }),
        },
      });
    });

    it('should return correct count', async () => {
      const userId = BigInt(44444);

      // Simulate 2 retrieves today
      mockPrisma.shareAccessLog.count.mockResolvedValueOnce(2);

      const count = await repository.countRetrievesByUserToday(userId);

      expect(count).toBe(2);
    });
  });
});
