import { SortOrder } from '../../common/enums/sort-order.enum';
import { AuditService } from './audit.service';

describe('AuditService', () => {
  const createService = () => {
    const auditRepository = {
      create: jest.fn(),
      findMany: jest.fn(),
      count: jest.fn(),
    };

    return {
      service: new AuditService(auditRepository as any),
      auditRepository,
    };
  };

  it('writes a complete super-admin audit record', async () => {
    const { service, auditRepository } = createService();
    auditRepository.create.mockResolvedValue(undefined);

    await expect(
      service.logSuperAdminAction('root@example.com', 'suspend', 'user', 'user-1', {
        reason: 'abuse',
      }),
    ).resolves.toBeUndefined();
    expect(auditRepository.create).toHaveBeenCalledWith({
      actorType: 'superadmin',
      actorIdentifier: 'root@example.com',
      action: 'suspend',
      targetType: 'user',
      targetId: 'user-1',
      metadata: { reason: 'abuse' },
    });
  });

  it('uses default pagination, filters, and descending order', async () => {
    const { service, auditRepository } = createService();
    const items = [{ id: 'log-1' }, { id: 'log-2' }];
    auditRepository.findMany.mockResolvedValue(items);
    auditRepository.count.mockResolvedValue(25);

    const result = await service.listSuperAdminLogs({});

    expect(auditRepository.findMany).toHaveBeenCalledWith({}, 0, 20, { createdAt: -1 });
    expect(auditRepository.count).toHaveBeenCalledWith({});
    expect(result).toMatchObject({
      items,
      count: 2,
      page: 1,
      limit: 20,
      total: 25,
      totalPages: 2,
      pagination: {
        hasNextPage: true,
        hasPreviousPage: false,
        nextPage: 2,
        previousPage: null,
      },
    });
  });

  it('builds escaped search filters and applies explicit ascending pagination', async () => {
    const { service, auditRepository } = createService();
    auditRepository.findMany.mockResolvedValue([{ id: 'log-3' }]);
    auditRepository.count.mockResolvedValue(21);

    const result = await service.listSuperAdminLogs({
      q: '  user.*  ',
      actorType: 'system',
      targetType: '  post  ',
      page: 3,
      limit: 10,
      sortOrder: SortOrder.ASC,
    });

    const filter = {
      $or: [
        { action: { $regex: 'user\\.\\*', $options: 'i' } },
        { targetType: { $regex: 'user\\.\\*', $options: 'i' } },
        { targetId: { $regex: 'user\\.\\*', $options: 'i' } },
        { actorIdentifier: { $regex: 'user\\.\\*', $options: 'i' } },
      ],
      actorType: 'system',
      targetType: 'post',
    };
    expect(auditRepository.findMany).toHaveBeenCalledWith(filter, 20, 10, { createdAt: 1 });
    expect(auditRepository.count).toHaveBeenCalledWith(filter);
    expect(result.pagination).toMatchObject({
      hasNextPage: false,
      hasPreviousPage: true,
      nextPage: null,
      previousPage: 2,
    });
  });

  it('ignores whitespace-only optional filters', async () => {
    const { service, auditRepository } = createService();
    auditRepository.findMany.mockResolvedValue([]);
    auditRepository.count.mockResolvedValue(0);

    await service.listSuperAdminLogs({ q: '  ', targetType: '  ' });

    expect(auditRepository.findMany).toHaveBeenCalledWith({}, 0, 20, { createdAt: -1 });
  });

  it('propagates write and read failures', async () => {
    const { service, auditRepository } = createService();
    auditRepository.create.mockRejectedValue(new Error('write failed'));

    await expect(
      service.logSuperAdminAction('root', 'action', 'target'),
    ).rejects.toThrow('write failed');

    auditRepository.findMany.mockRejectedValue(new Error('read failed'));
    auditRepository.count.mockResolvedValue(0);
    await expect(service.listSuperAdminLogs({})).rejects.toThrow('read failed');
  });
});
