<?php

/**
 * Пул з'єднань для оптимізації
 *
 * Перевикористання з'єднань для зменшення навантаження
 *
 * @package Flowaxy\Infrastructure\Persistence\Database
 * @version 1.0.0 Alpha prerelease
 */

declare(strict_types=1);

namespace Flowaxy\Infrastructure\Persistence\Database;

use Flowaxy\Support\Facades\Log;

final class ConnectionPool
{
    /**
     * @var array<int, \PDO>
     */
    private array $pool = [];

    private int $maxSize;
    private int $currentSize = 0;
    private string $dsn;
    private string $username;
    private string $password;
    private array $options;

    public function __construct(
        string $dsn,
        string $username,
        string $password,
        array $options = [],
        int $maxSize = 10
    ) {
        $this->dsn = $dsn;
        $this->username = $username;
        $this->password = $password;
        $this->options = $options;
        $this->maxSize = $maxSize;

        Log::debug('ConnectionPool::__construct: Connection pool created', [
            'max_size' => $maxSize,
            'dsn' => $dsn,
        ]);
    }

    public function get(): \PDO
    {
        if (!empty($this->pool)) {
            $connection = array_pop($this->pool);
            Log::debug('ConnectionPool::get: Connection retrieved from pool', [
                'available_count' => count($this->pool),
                'current_size' => $this->currentSize,
            ]);
            return $connection;
        }

        if ($this->currentSize < $this->maxSize) {
            $this->currentSize++;
            Log::debug('ConnectionPool::get: Creating new connection within pool limit', [
                'current_size' => $this->currentSize,
                'max_size' => $this->maxSize,
            ]);
            try {
                $connection = $this->createConnection();
                Log::info('ConnectionPool::get: New connection created successfully', [
                    'current_size' => $this->currentSize,
                ]);
                return $connection;
            } catch (\Exception $e) {
                $this->currentSize--;
                Log::error('ConnectionPool::get: Failed to create connection', [
                    'error' => $e->getMessage(),
                    'exception' => $e,
                ]);
                throw $e;
            }
        }

        // Переповнення пулу - створюємо тимчасове з'єднання
        Log::warning('ConnectionPool::get: Pool overflow, creating temporary connection', [
            'current_size' => $this->currentSize,
            'max_size' => $this->maxSize,
        ]);
        try {
            $connection = $this->createConnection();
            return $connection;
        } catch (\Exception $e) {
            Log::error('ConnectionPool::get: Failed to create temporary connection', [
                'error' => $e->getMessage(),
                'exception' => $e,
            ]);
            throw $e;
        }
    }

    public function release(\PDO $connection): void
    {
        try {
            $connection->query('SELECT 1');
        } catch (\PDOException $e) {
            $this->currentSize--;
            Log::warning('ConnectionPool::release: Connection is invalid, removing from pool', [
                'error' => $e->getMessage(),
                'current_size' => $this->currentSize,
            ]);
            return;
        }

        if (count($this->pool) < $this->maxSize) {
            $this->pool[] = $connection;
            Log::debug('ConnectionPool::release: Connection returned to pool', [
                'available_count' => count($this->pool),
                'current_size' => $this->currentSize,
            ]);
        } else {
            $connection = null;
            $this->currentSize--;
            Log::debug('ConnectionPool::release: Pool is full, connection closed', [
                'current_size' => $this->currentSize,
            ]);
        }
    }

    private function createConnection(): \PDO
    {
        try {
            Log::debug('ConnectionPool::createConnection: Creating new PDO connection', [
                'dsn' => $this->dsn,
            ]);
            $connection = new \PDO($this->dsn, $this->username, $this->password, $this->options);
            Log::info('ConnectionPool::createConnection: Connection created successfully');
            return $connection;
        } catch (\PDOException $e) {
            Log::error('ConnectionPool::createConnection: Failed to create connection', [
                'error' => $e->getMessage(),
                'code' => $e->getCode(),
                'exception' => $e,
            ]);
            throw $e;
        }
    }

    public function getCurrentSize(): int
    {
        return $this->currentSize;
    }

    public function getAvailableCount(): int
    {
        return count($this->pool);
    }

    public function clear(): void
    {
        Log::debug('ConnectionPool::clear: Clearing connection pool', [
            'pool_size' => count($this->pool),
            'current_size' => $this->currentSize,
        ]);
        $this->pool = [];
        $this->currentSize = 0;
        Log::info('ConnectionPool::clear: Connection pool cleared');
    }

    public function close(): void
    {
        Log::debug('ConnectionPool::close: Closing all connections', [
            'pool_size' => count($this->pool),
        ]);
        foreach ($this->pool as $connection) {
            $connection = null;
        }

        $this->pool = [];
        $this->currentSize = 0;
    }
}
