import { describe, expect, it } from 'bun:test';
import { PoolSizeCalculator } from '../src/pool-size-calculator';
import type { ConnectionPoolConfig } from '../src/types';

describe('PoolSizeCalculator', () => {
  describe('calculate', () => {
    it('should return service-level defaults for known services', () => {
      const result = PoolSizeCalculator.calculate('data-ingestion');
      expect(result).toEqual({ min: 5, max: 50, idle: 10 });
    });

    it('should return handler-level defaults when handler name is provided', () => {
      const result = PoolSizeCalculator.calculate('any-service', 'batch-import');
      expect(result).toEqual({ min: 10, max: 100, idle: 20 });
    });

    it('should prefer handler-level over service-level defaults', () => {
      const result = PoolSizeCalculator.calculate('data-ingestion', 'real-time');
      expect(result).toEqual({ min: 2, max: 10, idle: 3 });
    });

    it('should return generic defaults for unknown services', () => {
      const result = PoolSizeCalculator.calculate('unknown-service');
      expect(result).toEqual({ min: 2, max: 10, idle: 3 });
    });

    it('should use custom configuration when provided', () => {
      const customConfig: Partial<ConnectionPoolConfig> = {
        minConnections: 15,
        maxConnections: 75,
      };
      const result = PoolSizeCalculator.calculate('data-ingestion', undefined, customConfig);
      expect(result).toEqual({
        min: 15,
        max: 75,
        idle: Math.floor((15 + 75) / 4), // 22
      });
    });

    it('should ignore partial custom configuration', () => {
      const customConfig: Partial<ConnectionPoolConfig> = {
        minConnections: 15,
        // maxConnections not provided
      };
      const result = PoolSizeCalculator.calculate('data-ingestion', undefined, customConfig);
      // Should fall back to defaults
      expect(result).toEqual({ min: 5, max: 50, idle: 10 });
    });

    it('should handle all predefined service types', () => {
      const services = [
        { name: 'data-pipeline', expected: { min: 3, max: 30, idle: 5 } },
        { name: 'processing-service', expected: { min: 2, max: 20, idle: 3 } },
        { name: 'web-api', expected: { min: 2, max: 10, idle: 2 } },
        { name: 'portfolio-service', expected: { min: 2, max: 15, idle: 3 } },
        { name: 'strategy-service', expected: { min: 3, max: 25, idle: 5 } },
        { name: 'execution-service', expected: { min: 2, max: 10, idle: 2 } },
      ];

      services.forEach(({ name, expected }) => {
        const result = PoolSizeCalculator.calculate(name);
        expect(result).toEqual(expected);
      });
    });

    it('should handle all predefined handler types', () => {
      const handlers = [
        { name: 'analytics', expected: { min: 5, max: 30, idle: 10 } },
        { name: 'reporting', expected: { min: 3, max: 20, idle: 5 } },
      ];

      handlers.forEach(({ name, expected }) => {
        const result = PoolSizeCalculator.calculate('any-service', name);
        expect(result).toEqual(expected);
      });
    });

    it('should return a new object each time', () => {
      const result1 = PoolSizeCalculator.calculate('data-ingestion');
      const result2 = PoolSizeCalculator.calculate('data-ingestion');

      expect(result1).not.toBe(result2);
      expect(result1).toEqual(result2);
    });
  });

  describe('getOptimalPoolSize', () => {
    it("should calculate pool size based on Little's Law", () => {
      // 10 requests/second, 100ms average query time, 50ms target latency
      const result = PoolSizeCalculator.getOptimalPoolSize(10, 100, 50);

      // Little's Law: L = λ * W = 10 * 0.1 = 1
      // With 20% buffer: 1 * 1.2 = 1.2, ceil = 2
      // Latency based: 10 * (100/50) = 20
      // Max of (2, 20, 2) = 20
      expect(result).toBe(20);
    });

    it('should return minimum 2 connections', () => {
      // Very low concurrency
      const result = PoolSizeCalculator.getOptimalPoolSize(0.1, 10, 1000);
      expect(result).toBe(2);
    });

    it('should handle high concurrency scenarios', () => {
      // 100 requests/second, 500ms average query time, 100ms target latency
      const result = PoolSizeCalculator.getOptimalPoolSize(100, 500, 100);

      // Little's Law: L = 100 * 0.5 = 50
      // With 20% buffer: 50 * 1.2 = 60
      // Latency based: 100 * (500/100) = 500
      // Max of (60, 500, 2) = 500
      expect(result).toBe(500);
    });

    it('should handle scenarios where latency target is already met', () => {
      // 10 requests/second, 50ms average query time, 200ms target latency
      const result = PoolSizeCalculator.getOptimalPoolSize(10, 50, 200);

      // Little's Law: L = 10 * 0.05 = 0.5
      // With 20% buffer: 0.5 * 1.2 = 0.6, ceil = 1
      // Latency based: 10 * (50/200) = 2.5, ceil = 3
      // Max of (1, 3, 2) = 3
      expect(result).toBe(3);
    });

    it('should handle edge cases with zero values', () => {
      expect(PoolSizeCalculator.getOptimalPoolSize(0, 100, 100)).toBe(2);
      expect(PoolSizeCalculator.getOptimalPoolSize(10, 0, 100)).toBe(2);
    });

    it('should handle fractional calculations correctly', () => {
      // 15 requests/second, 75ms average query time, 150ms target latency
      const result = PoolSizeCalculator.getOptimalPoolSize(15, 75, 150);

      // Little's Law: L = 15 * 0.075 = 1.125
      // With 20% buffer: 1.125 * 1.2 = 1.35, ceil = 2
      // Latency based: 15 * (75/150) = 7.5, ceil = 8
      // Max of (2, 8, 2) = 8
      expect(result).toBe(8);
    });

    it('should prioritize latency-based sizing when it requires more connections', () => {
      // Scenario where latency requirements demand more connections than throughput
      const result = PoolSizeCalculator.getOptimalPoolSize(5, 200, 50);

      // Little's Law: L = 5 * 0.2 = 1
      // With 20% buffer: 1 * 1.2 = 1.2, ceil = 2
      // Latency based: 5 * (200/50) = 20
      // Max of (2, 20, 2) = 20
      expect(result).toBe(20);
    });

    it('should handle very high query times', () => {
      // 50 requests/second, 2000ms average query time, 500ms target latency
      const result = PoolSizeCalculator.getOptimalPoolSize(50, 2000, 500);

      // Little's Law: L = 50 * 2 = 100
      // With 20% buffer: 100 * 1.2 = 120
      // Latency based: 50 * (2000/500) = 200
      // Max of (120, 200, 2) = 200
      expect(result).toBe(200);
    });
  });
});
