<?php

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

use GDAVSCOPE\Amp\ByteStream\Compression\DecompressingReadableStream;
use GDAVSCOPE\Amp\Cancellation;
use GDAVSCOPE\Amp\ForbidCloning;
use GDAVSCOPE\Amp\ForbidSerialization;
use GDAVSCOPE\Amp\Http\Client\Connection\Stream;
use GDAVSCOPE\Amp\Http\Client\Internal\SizeLimitingReadableStream;
use GDAVSCOPE\Amp\Http\Client\NetworkInterceptor;
use GDAVSCOPE\Amp\Http\Client\Request;
use GDAVSCOPE\Amp\Http\Client\Response;
final class DecompressResponse implements NetworkInterceptor
{
    use ForbidCloning;
    use ForbidSerialization;
    private readonly bool $hasZlib;
    public function __construct()
    {
        $this->hasZlib = \extension_loaded('zlib');
    }
    public function requestViaNetwork(Request $request, Cancellation $cancellation, Stream $stream): Response
    {
        // If a header is manually set, we won't interfere
        if ($request->hasHeader('accept-encoding')) {
            return $stream->request($request, $cancellation);
        }
        $this->addAcceptEncodingHeader($request);
        $request->interceptPush(function (Request $request, Response $response): Response {
            return $this->decompressResponse($response);
        });
        return $this->decompressResponse($stream->request($request, $cancellation));
    }
    private function addAcceptEncodingHeader(Request $request): void
    {
        if ($this->hasZlib) {
            $request->setHeader('Accept-Encoding', 'gzip, deflate, identity');
        }
    }
    private function decompressResponse(Response $response): Response
    {
        if ($encoding = $this->determineCompressionEncoding($response)) {
            $stream = new DecompressingReadableStream($response->getBody(), $encoding);
            $sizeLimit = $response->getRequest()->getBodySizeLimit();
            if ($sizeLimit > 0) {
                $stream = new SizeLimitingReadableStream($stream, $sizeLimit);
            }
            $response->setBody($stream);
            $response->removeHeader('content-encoding');
        }
        return $response;
    }
    private function determineCompressionEncoding(Response $response): int
    {
        if (!$this->hasZlib) {
            return 0;
        }
        if (!$response->hasHeader("content-encoding")) {
            return 0;
        }
        $contentEncoding = $response->getHeader("content-encoding");
        \assert($contentEncoding !== null);
        $contentEncodingHeader = \trim($contentEncoding);
        if (\strcasecmp($contentEncodingHeader, 'gzip') === 0) {
            return \ZLIB_ENCODING_GZIP;
        }
        if (\strcasecmp($contentEncodingHeader, 'deflate') === 0) {
            return \ZLIB_ENCODING_DEFLATE;
        }
        return 0;
    }
}
