import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
import Fastify from 'fastify';
import type { FastifyInstance } from 'fastify';
import { registerAuditLogRoutes } from '../src/routes/audit-logs.js';
import { AuditLogService } from '../src/services/audit-log.service.js';
import type { IAuditLogRepository } from '../src/repositories/interfaces.js';
import { errorHandler } from '../src/middleware/error-handler.js';

function makeLog(id: string) {
  return {
    id,
    userId: 'user-1',
    action: 'CREATE',
    resource: 'servers',
    resourceId: null,
    details: {},
    createdAt: new Date('2025-01-15T00:00:00Z'),
  };
}

function mockRepo(): IAuditLogRepository {
  return {
    findAll: vi.fn(async () => [makeLog('log-1'), makeLog('log-2')]),
    findById: vi.fn(async () => null),
    create: vi.fn(async () => makeLog('new-log')),
    count: vi.fn(async () => 2),
    deleteOlderThan: vi.fn(async () => 3),
  };
}

describe('Audit Log Routes', () => {
  let app: FastifyInstance;
  let repo: ReturnType<typeof mockRepo>;
  let service: AuditLogService;

  beforeEach(async () => {
    app = Fastify();
    app.setErrorHandler(errorHandler);
    repo = mockRepo();
    service = new AuditLogService(repo, 90);
    registerAuditLogRoutes(app, service);
    await app.ready();
  });

  afterEach(async () => {
    await app.close();
  });

  describe('GET /api/v1/audit-logs', () => {
    it('returns paginated audit logs', async () => {
      const res = await app.inject({ method: 'GET', url: '/api/v1/audit-logs' });
      expect(res.statusCode).toBe(200);
      const body = res.json();
      expect(body.logs).toHaveLength(2);
      expect(body.total).toBe(2);
    });

    it('passes query filters to service', async () => {
      await app.inject({
        method: 'GET',
        url: '/api/v1/audit-logs?userId=user-1&action=CREATE&resource=servers&limit=10&offset=5',
      });

      expect(repo.findAll).toHaveBeenCalledWith(
        expect.objectContaining({
          userId: 'user-1',
          action: 'CREATE',
          resource: 'servers',
          limit: 10,
          offset: 5,
        }),
      );
    });

    it('passes date range filters', async () => {
      await app.inject({
        method: 'GET',
        url: '/api/v1/audit-logs?since=2025-01-01T00:00:00Z&until=2025-12-31T23:59:59Z',
      });

      expect(repo.findAll).toHaveBeenCalledWith(
        expect.objectContaining({
          since: expect.any(Date),
          until: expect.any(Date),
        }),
      );
    });
  });

  describe('GET /api/v1/audit-logs/:id', () => {
    it('returns a specific log entry', async () => {
      vi.mocked(repo.findById).mockResolvedValue(makeLog('log-1'));
      const res = await app.inject({ method: 'GET', url: '/api/v1/audit-logs/log-1' });
      expect(res.statusCode).toBe(200);
      expect(res.json().id).toBe('log-1');
    });

    it('returns 404 for missing log', async () => {
      const res = await app.inject({ method: 'GET', url: '/api/v1/audit-logs/nonexistent' });
      expect(res.statusCode).toBe(404);
    });
  });

  describe('POST /api/v1/audit-logs/purge', () => {
    it('purges expired logs and returns count', async () => {
      const res = await app.inject({ method: 'POST', url: '/api/v1/audit-logs/purge' });
      expect(res.statusCode).toBe(200);
      expect(res.json().deleted).toBe(3);
    });
  });
});
