<?php

declare(strict_types=1);

use App\Framework\HttpClient\Curl\Handle;
use App\Framework\HttpClient\Curl\HandleOption;
use App\Framework\HttpClient\Curl\Info;
use App\Framework\HttpClient\Curl\MultiHandlePool;

describe('Curl\MultiHandlePool', function () {
    it('can be initialized with default concurrency', function () {
        $pool = new MultiHandlePool();

        expect($pool)->toBeInstanceOf(MultiHandlePool::class);
        expect($pool->getMaxConcurrent())->toBe(10);
        expect($pool->queueSize())->toBe(0);
        expect($pool->completedCount())->toBe(0);
    });

    it('can be initialized with custom concurrency limit', function () {
        $pool = new MultiHandlePool(maxConcurrent: 3);

        expect($pool->getMaxConcurrent())->toBe(3);
    });

    it('throws on invalid concurrency limit', function () {
        new MultiHandlePool(maxConcurrent: 0);
    })->throws(\InvalidArgumentException::class, 'Max concurrent must be at least 1');

    it('can add handles to queue', function () {
        $pool = new MultiHandlePool();

        $pool->add(new Handle('https://httpbin.org/get'));
        $pool->add(new Handle('https://httpbin.org/uuid'));

        expect($pool->queueSize())->toBe(2);
        expect($pool->totalCount())->toBe(2);
    });

    it('supports method chaining', function () {
        $pool = new MultiHandlePool();

        $result = $pool
            ->add(new Handle('https://httpbin.org/get'))
            ->add(new Handle('https://httpbin.org/uuid'));

        expect($result)->toBe($pool);
        expect($pool->queueSize())->toBe(2);
    });

    it('executes all handles respecting concurrency limit', function () {
        $pool = new MultiHandlePool(maxConcurrent: 2);

        // Add 5 handles
        for ($i = 0; $i < 5; $i++) {
            $pool->add(new Handle('https://httpbin.org/get'));
        }

        expect($pool->queueSize())->toBe(5);

        $completed = $pool->executeAll();

        expect($completed)->toHaveCount(5);
        expect($pool->completedCount())->toBe(5);
        expect($pool->queueSize())->toBe(0);
        expect($pool->isComplete())->toBeTrue();
    });

    it('executes requests in batches', function () {
        $pool = new MultiHandlePool(maxConcurrent: 2);

        $urls = [
            'https://httpbin.org/delay/1',
            'https://httpbin.org/delay/1',
            'https://httpbin.org/delay/1',
            'https://httpbin.org/delay/1',
        ];

        foreach ($urls as $url) {
            $pool->add(new Handle($url));
        }

        $startTime = microtime(true);
        $completed = $pool->executeAll();
        $duration = microtime(true) - $startTime;

        expect($completed)->toHaveCount(4);

        // With concurrency 2, should take ~2 seconds (2 batches of 2)
        // If sequential, would take ~4 seconds
        expect($duration)->toBeLessThan(3.5);
        expect($duration)->toBeGreaterThan(1.5);
    });

    it('tracks progress during execution', function () {
        $pool = new MultiHandlePool(maxConcurrent: 10);

        for ($i = 0; $i < 10; $i++) {
            $pool->add(new Handle('https://httpbin.org/get'));
        }

        expect($pool->getProgress())->toBe(0.0);

        $pool->executeAll();

        expect($pool->getProgress())->toBe(100.0);
        expect($pool->isComplete())->toBeTrue();
    });

    it('can clear all handles', function () {
        $pool = new MultiHandlePool();

        $pool->add(new Handle('https://httpbin.org/get'));
        $pool->add(new Handle('https://httpbin.org/uuid'));

        expect($pool->queueSize())->toBe(2);

        $pool->clear();

        expect($pool->queueSize())->toBe(0);
        expect($pool->completedCount())->toBe(0);
        expect($pool->totalCount())->toBe(0);
    });

    it('handles empty pool gracefully', function () {
        $pool = new MultiHandlePool();

        $completed = $pool->executeAll();

        expect($completed)->toBeArray();
        expect($completed)->toHaveCount(0);
        expect($pool->isComplete())->toBeTrue();
    });

    it('can execute single handle', function () {
        $pool = new MultiHandlePool(maxConcurrent: 5);

        $pool->add(new Handle('https://httpbin.org/get'));

        $completed = $pool->executeAll();

        expect($completed)->toHaveCount(1);
    });

    it('handles many requests efficiently', function () {
        $pool = new MultiHandlePool(maxConcurrent: 10);

        // Add 30 handles
        for ($i = 0; $i < 30; $i++) {
            $pool->add(new Handle('https://httpbin.org/get'));
        }

        expect($pool->totalCount())->toBe(30);

        $startTime = microtime(true);
        $completed = $pool->executeAll();
        $duration = microtime(true) - $startTime;

        expect($completed)->toHaveCount(30);
        expect($pool->completedCount())->toBe(30);

        // With concurrency 10, should execute in ~3 batches
        // Much faster than sequential (would be 30+ seconds)
        expect($duration)->toBeLessThan(15.0);
    });

    it('preserves handle state after execution', function () {
        $pool = new MultiHandlePool();

        $handle = new Handle('https://httpbin.org/get');
        $handle->setOption(HandleOption::Timeout, 10);

        $pool->add($handle);
        $pool->executeAll();

        // Handle should still be accessible and have response info
        expect($handle->getInfo(Info::ResponseCode))->toBe(200);
        expect($handle->getInfo(Info::TotalTime))->toBeGreaterThan(0);
    });

    it('can reuse pool after execution', function () {
        $pool = new MultiHandlePool(maxConcurrent: 5);

        // First batch
        $pool->add(new Handle('https://httpbin.org/get'));
        $pool->add(new Handle('https://httpbin.org/uuid'));

        $firstBatch = $pool->executeAll();
        expect($firstBatch)->toHaveCount(2);

        // Clear and add new batch
        $pool->clear();

        $pool->add(new Handle('https://httpbin.org/get'));
        $pool->add(new Handle('https://httpbin.org/uuid'));
        $pool->add(new Handle('https://httpbin.org/get'));

        $secondBatch = $pool->executeAll();
        expect($secondBatch)->toHaveCount(3);
    });

    it('respects strict concurrency limit', function () {
        $pool = new MultiHandlePool(maxConcurrent: 1);

        // Add multiple handles
        for ($i = 0; $i < 3; $i++) {
            $pool->add(new Handle('https://httpbin.org/delay/1'));
        }

        $startTime = microtime(true);
        $pool->executeAll();
        $duration = microtime(true) - $startTime;

        // With concurrency 1, should execute sequentially (~3 seconds)
        expect($duration)->toBeGreaterThan(2.5);
    });

    it('provides accurate count methods', function () {
        $pool = new MultiHandlePool(maxConcurrent: 2);

        // Add 5 handles
        for ($i = 0; $i < 5; $i++) {
            $pool->add(new Handle('https://httpbin.org/get'));
        }

        expect($pool->queueSize())->toBe(5);
        expect($pool->activeCount())->toBe(0);
        expect($pool->completedCount())->toBe(0);
        expect($pool->totalCount())->toBe(5);

        // After execution
        $pool->executeAll();

        expect($pool->queueSize())->toBe(0);
        expect($pool->activeCount())->toBe(0);
        expect($pool->completedCount())->toBe(5);
        expect($pool->totalCount())->toBe(5);
    });
});

describe('Curl\MultiHandlePool edge cases', function () {
    it('handles mix of fast and slow requests', function () {
        $pool = new MultiHandlePool(maxConcurrent: 3);

        // Mix of instant and delayed requests
        $pool->add(new Handle('https://httpbin.org/get'));
        $pool->add(new Handle('https://httpbin.org/delay/1'));
        $pool->add(new Handle('https://httpbin.org/get'));
        $pool->add(new Handle('https://httpbin.org/delay/1'));

        $completed = $pool->executeAll();

        expect($completed)->toHaveCount(4);
    });

    it('handles large concurrency limit', function () {
        $pool = new MultiHandlePool(maxConcurrent: 100);

        for ($i = 0; $i < 20; $i++) {
            $pool->add(new Handle('https://httpbin.org/get'));
        }

        $completed = $pool->executeAll();

        expect($completed)->toHaveCount(20);
    });
});
