<?php

declare (strict_types=1);
namespace GDAVSCOPE\Amp\Http\Client\Connection;

use GDAVSCOPE\Amp\Cancellation;
use GDAVSCOPE\Amp\ForbidCloning;
use GDAVSCOPE\Amp\ForbidSerialization;
use GDAVSCOPE\Amp\Http\Client\Connection\Internal\Http2ConnectionProcessor;
use GDAVSCOPE\Amp\Http\Client\Request;
use GDAVSCOPE\Amp\Http\Client\Response;
use GDAVSCOPE\Amp\Socket\Socket;
use GDAVSCOPE\Amp\Socket\SocketAddress;
use GDAVSCOPE\Amp\Socket\TlsInfo;
use GDAVSCOPE\Amp\TimeoutCancellation;
use function GDAVSCOPE\Amp\Http\Client\events;
final class Http2Connection implements Connection
{
    use ForbidSerialization;
    use ForbidCloning;
    private const PROTOCOL_VERSIONS = ['2'];
    private readonly Http2ConnectionProcessor $processor;
    private int $streamCounter = 0;
    private int $requestCount = 0;
    public function __construct(private readonly Socket $socket, private readonly float $connectDuration, private readonly ?float $tlsHandshakeDuration)
    {
        $this->processor = new Http2ConnectionProcessor($socket);
    }
    public function isIdle(): bool
    {
        return $this->processor->isIdle();
    }
    public function getProtocolVersions(): array
    {
        return self::PROTOCOL_VERSIONS;
    }
    public function initialize(?Cancellation $cancellation = null): void
    {
        $this->processor->initialize($cancellation ?? new TimeoutCancellation(5));
    }
    public function getStream(Request $request): ?Stream
    {
        if (!$this->processor->isInitialized()) {
            throw new \Error('The ' . __CLASS__ . '::initialize() invocation must be complete before using the connection');
        }
        if ($this->processor->isClosed() || $this->processor->getRemainingStreams() <= 0) {
            return null;
        }
        $this->processor->reserveStream();
        events()->connectionAcquired($request, $this, ++$this->streamCounter);
        return HttpStream::fromConnection($this, $this->request(...), $this->processor->unreserveStream(...));
    }
    public function onClose(\Closure $onClose): void
    {
        $this->processor->onClose($onClose);
    }
    public function close(): void
    {
        $this->processor->close();
    }
    public function isClosed(): bool
    {
        return $this->processor->isClosed();
    }
    public function getLocalAddress(): SocketAddress
    {
        return $this->socket->getLocalAddress();
    }
    public function getRemoteAddress(): SocketAddress
    {
        return $this->socket->getRemoteAddress();
    }
    public function getTlsInfo(): ?TlsInfo
    {
        return $this->socket->getTlsInfo();
    }
    private function request(Request $request, Cancellation $cancellation, Stream $stream): Response
    {
        $this->requestCount++;
        return $this->processor->request($request, $cancellation, $stream);
    }
    public function getTlsHandshakeDuration(): ?float
    {
        return $this->tlsHandshakeDuration;
    }
    public function getConnectDuration(): float
    {
        return $this->connectDuration;
    }
}
