/**
 * @ai-summary Integration tests for AuditLogService
 * @ai-context Tests log creation, search, filtering, and cleanup
 */

import { Pool } from 'pg';
import { appConfig } from '../../../core/config/config-loader';
import { AuditLogService } from '../domain/audit-log.service';
import { AuditLogRepository } from '../data/audit-log.repository';

describe('AuditLogService', () => {
  let pool: Pool;
  let repository: AuditLogRepository;
  let service: AuditLogService;
  const createdIds: string[] = [];

  beforeAll(async () => {
    pool = new Pool({
      connectionString: appConfig.getDatabaseUrl(),
    });
    repository = new AuditLogRepository(pool);
    service = new AuditLogService(repository);
  });

  afterAll(async () => {
    // Cleanup test data
    if (createdIds.length > 0) {
      await pool.query(`DELETE FROM audit_logs WHERE id = ANY($1::uuid[])`, [createdIds]);
    }
    await pool.end();
  });

  describe('log()', () => {
    it('should create log entry with all fields', async () => {
      const entry = await service.log(
        'auth',
        'info',
        'user-123',
        'User logged in',
        'session',
        'session-456',
        { ip: '192.168.1.1', browser: 'Chrome' }
      );

      createdIds.push(entry.id);

      expect(entry.id).toBeDefined();
      expect(entry.category).toBe('auth');
      expect(entry.severity).toBe('info');
      expect(entry.userId).toBe('user-123');
      expect(entry.action).toBe('User logged in');
      expect(entry.resourceType).toBe('session');
      expect(entry.resourceId).toBe('session-456');
      expect(entry.details).toEqual({ ip: '192.168.1.1', browser: 'Chrome' });
      expect(entry.createdAt).toBeInstanceOf(Date);
    });

    it('should create log entry with null userId for system actions', async () => {
      const entry = await service.log(
        'system',
        'info',
        null,
        'Scheduled backup started'
      );

      createdIds.push(entry.id);

      expect(entry.id).toBeDefined();
      expect(entry.category).toBe('system');
      expect(entry.userId).toBeNull();
    });

    it('should throw error for invalid category', async () => {
      await expect(
        service.log(
          'invalid' as any,
          'info',
          'user-123',
          'Test action'
        )
      ).rejects.toThrow('Invalid audit log category');
    });

    it('should throw error for invalid severity', async () => {
      await expect(
        service.log(
          'auth',
          'invalid' as any,
          'user-123',
          'Test action'
        )
      ).rejects.toThrow('Invalid audit log severity');
    });
  });

  describe('convenience methods', () => {
    it('info() should create info-level log', async () => {
      const entry = await service.info('vehicle', 'user-123', 'Vehicle created');
      createdIds.push(entry.id);

      expect(entry.severity).toBe('info');
    });

    it('warning() should create warning-level log', async () => {
      const entry = await service.warning('user', 'user-123', 'Password reset requested');
      createdIds.push(entry.id);

      expect(entry.severity).toBe('warning');
    });

    it('error() should create error-level log', async () => {
      const entry = await service.error('admin', 'admin-123', 'Failed to revoke user');
      createdIds.push(entry.id);

      expect(entry.severity).toBe('error');
    });
  });

  describe('search()', () => {
    beforeAll(async () => {
      // Create test data for search
      const testLogs = [
        { category: 'auth', severity: 'info', action: 'Login successful' },
        { category: 'auth', severity: 'warning', action: 'Login failed' },
        { category: 'vehicle', severity: 'info', action: 'Vehicle created' },
        { category: 'vehicle', severity: 'info', action: 'Vehicle updated' },
        { category: 'admin', severity: 'error', action: 'Admin action failed' },
      ];

      for (const log of testLogs) {
        const entry = await service.log(
          log.category as any,
          log.severity as any,
          'test-user',
          log.action
        );
        createdIds.push(entry.id);
      }
    });

    it('should return paginated results', async () => {
      const result = await service.search({}, { limit: 10, offset: 0 });

      expect(result.logs).toBeInstanceOf(Array);
      expect(result.total).toBeGreaterThan(0);
      expect(result.limit).toBe(10);
      expect(result.offset).toBe(0);
    });

    it('should filter by category', async () => {
      const result = await service.search(
        { category: 'auth' },
        { limit: 100, offset: 0 }
      );

      expect(result.logs.length).toBeGreaterThan(0);
      expect(result.logs.every((log) => log.category === 'auth')).toBe(true);
    });

    it('should filter by severity', async () => {
      const result = await service.search(
        { severity: 'error' },
        { limit: 100, offset: 0 }
      );

      expect(result.logs.every((log) => log.severity === 'error')).toBe(true);
    });

    it('should search by action text', async () => {
      const result = await service.search(
        { search: 'Login' },
        { limit: 100, offset: 0 }
      );

      expect(result.logs.length).toBeGreaterThan(0);
      expect(result.logs.every((log) => log.action.includes('Login'))).toBe(true);
    });
  });

  describe('cleanup()', () => {
    it('should delete entries older than specified days', async () => {
      // Create an old entry by directly inserting
      await pool.query(`
        INSERT INTO audit_logs (category, severity, action, created_at)
        VALUES ('system', 'info', 'Old test entry', NOW() - INTERVAL '100 days')
      `);

      const deletedCount = await service.cleanup(90);

      expect(deletedCount).toBeGreaterThanOrEqual(1);
    });

    it('should not delete recent entries', async () => {
      const entry = await service.log('system', 'info', null, 'Recent entry');
      createdIds.push(entry.id);

      await service.cleanup(90);

      // Verify entry still exists
      const result = await pool.query(
        'SELECT id FROM audit_logs WHERE id = $1',
        [entry.id]
      );
      expect(result.rows.length).toBe(1);
    });
  });
});
