/**
 * Database Client Tests
 */

import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
import { createPool, getPool, query, healthCheck, closePool } from './client';
import { Pool } from 'pg';

vi.mock('pg');

describe('Database Client', () => {
  beforeEach(() => {
    vi.clearAllMocks();
  });

  afterEach(() => {
    vi.clearAllMocks();
  });

  describe('createPool', () => {
    it('should create a pool with default config', () => {
      const config = {
        connectionString: 'postgresql://localhost:5432/test',
      };

      createPool(config);

      expect(Pool).toHaveBeenCalledWith(
        expect.objectContaining({
          connectionString: config.connectionString,
          max: 20,
          idleTimeoutMillis: 30000,
          connectionTimeoutMillis: 2000,
        })
      );
    });

    it('should create a pool with custom config', () => {
      const config = {
        host: 'localhost',
        port: 5432,
        database: 'test',
        user: 'testuser',
        password: 'testpass',
        max: 10,
        idleTimeoutMillis: 60000,
        connectionTimeoutMillis: 5000,
      };

      createPool(config);

      expect(Pool).toHaveBeenCalledWith(
        expect.objectContaining({
          host: config.host,
          port: config.port,
          database: config.database,
          user: config.user,
          password: config.password,
          max: config.max,
          idleTimeoutMillis: config.idleTimeoutMillis,
          connectionTimeoutMillis: config.connectionTimeoutMillis,
        })
      );
    });
  });

  describe('getPool', () => {
    it('should return existing pool if already created', () => {
      const config = {
        connectionString: 'postgresql://localhost:5432/test',
      };

      const pool1 = getPool(config);
      const pool2 = getPool();

      expect(pool1).toBe(pool2);
    });

    it('should throw error if no pool exists and no config provided', () => {
      // Reset the default pool
      closePool();

      expect(() => getPool()).toThrow('Database configuration required');
    });
  });

  describe('query', () => {
    it('should execute query with parameters', async () => {
      const config = {
        connectionString: 'postgresql://localhost:5432/test',
      };

      const mockPool = {
        query: vi.fn().mockResolvedValueOnce({
          rows: [{ id: '1', name: 'Test' }],
          rowCount: 1,
        }),
      };

      (Pool as any).mockImplementation(() => mockPool);

      getPool(config);

      const result = await query('SELECT * FROM users WHERE id = $1', ['1']);

      expect(result.rows).toHaveLength(1);
      expect(result.rows[0]?.name).toBe('Test');
      expect(mockPool.query).toHaveBeenCalledWith('SELECT * FROM users WHERE id = $1', ['1']);
    });

    it('should throw error if pool not initialized', async () => {
      closePool();

      await expect(query('SELECT 1')).rejects.toThrow('Database pool not initialized');
    });
  });

  describe('healthCheck', () => {
    it('should return true if database is healthy', async () => {
      const config = {
        connectionString: 'postgresql://localhost:5432/test',
      };

      const mockPool = {
        query: vi.fn().mockResolvedValueOnce({ rows: [{ '?column?': 1 }] }),
      };

      (Pool as any).mockImplementation(() => mockPool);

      getPool(config);

      const result = await healthCheck();

      expect(result).toBe(true);
      expect(mockPool.query).toHaveBeenCalledWith('SELECT 1');
    });

    it('should return false if database is unhealthy', async () => {
      const config = {
        connectionString: 'postgresql://localhost:5432/test',
      };

      const mockPool = {
        query: vi.fn().mockRejectedValueOnce(new Error('Connection failed')),
      };

      (Pool as any).mockImplementation(() => mockPool);

      getPool(config);

      const result = await healthCheck();

      expect(result).toBe(false);
    });
  });

  describe('closePool', () => {
    it('should close the pool', async () => {
      const config = {
        connectionString: 'postgresql://localhost:5432/test',
      };

      const mockPool = {
        query: vi.fn(),
        end: vi.fn().mockResolvedValueOnce(undefined),
      };

      (Pool as any).mockImplementation(() => mockPool);

      getPool(config);

      await closePool();

      expect(mockPool.end).toHaveBeenCalled();
    });
  });
});

