<?php declare(strict_types=1);

namespace Amp\Http\Client\Connection\Internal;

use Amp\Http\Client\Request;

/** @internal */
final class RequestNormalizer
{
    public static function normalizeRequest(Request $request): Request
    {
        $headers = $request->getBody()->getHeaders();
        foreach ($headers as $name => $header) {
            if (!$request->hasHeader($name)) {
                $request->replaceHeaders([$name => $header]);
            }
        }

        self::normalizeRequestBodyHeaders($request);

        // Always normalize this as last item, because we need to strip sensitive headers
        self::normalizeTraceRequest($request);

        return $request;
    }

    private static function normalizeRequestBodyHeaders(Request $request): void
    {
        if (!$request->hasHeader('host')) {
            // Though servers are supposed to be able to handle standard port names on the end of the
            // Host header some fail to do this correctly. Thankfully PSR-7 recommends to strip the port
            // if it is the standard port for the given scheme.
            $request->setHeader('host', $request->getUri()->withUserInfo('')->getAuthority());
        }

        if ($request->hasHeader("transfer-encoding")) {
            $request->removeHeader("content-length");

            return;
        }

        if ($request->hasHeader("content-length")) {
            return;
        }

        $body = $request->getBody();
        $bodyLength = $body->getBodyLength();

        if ($bodyLength === 0) {
            if (\in_array($request->getMethod(), ['HEAD', 'GET', 'CONNECT'], true)) {
                $request->removeHeader('content-length');
            } else {
                $request->setHeader('content-length', '0');
            }

            $request->removeHeader('transfer-encoding');
        } elseif ($bodyLength > 0) {
            $request->setHeader("content-length", (string) $bodyLength);
            $request->removeHeader("transfer-encoding");
        } else {
            $request->setHeader("transfer-encoding", "chunked");
        }
    }

    private static function normalizeTraceRequest(Request $request): void
    {
        $method = $request->getMethod();

        if ($method !== 'TRACE') {
            return;
        }

        // https://tools.ietf.org/html/rfc7231#section-4.3.8
        $request->setBody('');

        // Remove all body and sensitive headers
        $request->replaceHeaders([
            "transfer-encoding" => [],
            "content-length" => [],
            "authorization" => [],
            "proxy-authorization" => [],
            "cookie" => [],
        ]);
    }
}
