<?php

use Psr\Http\Message\MessageInterface;
use Psr\Http\Message\RequestInterface;
use Psr\Http\Message\StreamInterface;
use Psr\Http\Message\UriInterface;

include_once 'MessageInterface.php';
include_once 'UriInterface.php';
include_once 'RequestInterface.php';

include_once 'util.php';
include_once 'stream.php';
include_once 'uri.php';

class Message implements RequestInterface {
    private string $protocolVersion;
    public array $headers;
    private StreamInterface $body;
    private string $target;
    private UriInterface $uri;
    private string $method;
    function __construct() {
        $this->protocolVersion = trim_prefix($_SERVER["SERVER_PROTOCOL"],"HTTP/");
//      if ($this->protocolVersion != "1.1" && $this->protocolVersion != "1.0") {
//          throw new Exception("Invalid Protocol Version: " . $this->protocolVersion);
//      }
        $headers = apache_request_headers();
        if ($headers == false) {
            $this->headers = [];
        } else {
            foreach ($headers as $header => $value) {
                $this->headers[to_title($header)] = [[$header, $value]];
            }
        }
        $contents =  file_get_contents('php://input');
        if($contents == false) {
            $this->body = new StringBuffer("");
        }else {
            $this->body = new StringBuffer($contents);
        }
        $this->uri = new StringURI($_SERVER["REQUEST_URI"]);
        $this->method = new StringURI($_SERVER["REQUEST_METHOD"]);
    }

    function getMethod(): string
    {
        return $this->method;
    }
    function withMethod(string $method): RequestInterface
    {
        $next = clone $this;
        $next->method = $method;
        return $next;
    }
    function getRequestTarget(): string
    {
        return $this->target;
    }
    function withRequestTarget(string $requestTarget): RequestInterface
    {
        $next = clone $this;
        $next->target = $requestTarget;
        return $next;
    }
    function getUri(): UriInterface
    {
        return $this->uri;
    }
    function withUri(UriInterface $uri, bool $preserveHost = false): RequestInterface
    {
        $next = clone $this;
        if ($preserveHost) {
            $next->uri = $uri;
        }else {
            $next->uri = $uri->withHost($this->getUri()->getHost());
        }
        return $next;
    }
    function getProtocolVersion(): string {
        return $this->protocolVersion;
    }
    function withProtocolVersion(string $version): MessageInterface
    {
        $next = clone $this;
        $next->protocolVersion = $version;
        return $next;
    }

    function getHeaders(): array {
        return $this->headers;
    }
    function hasHeader(string $name): bool {
        $header = to_title($name);
        return isset($this->headers[$header]);
    }

    function getHeader(string $name): array {
        $header = to_title($name);
        if (isset($this->headers[$header])){
            return array_map(fn($x)=>$x[1],$this->headers[$header]);
        }
        return [];
    }
    function getHeaderLine(string $name): string {
        $headers = $this->getHeader($name);
        if (count($headers) == 0) {
            return "";
        }
        return join(",",$headers);
    }

    function withHeader(string $name, $value): MessageInterface {
        $next = clone $this;
        if (is_array($value)) {
            $next->headers[to_title($name)] = array_map(fn($x)=>[$name,$x],$value);
        }else {
            $next->headers[to_title($name)] = [[$name, $value]];
        }
        return $next;
    }
    function withAddedHeader(string $name, $value): MessageInterface {
        $next = clone $this;
        if (!isset($next->headers[to_title($name)])) {
            $next->headers[to_title($name)] = [];
        }
        if (is_array($value)) {
            foreach ($value as $v) {
                $next->headers[to_title($name)][] = [$name, $v];
            }
        }else {
            $next->headers[to_title($name)][] = [$name, $value];
        }
        return $next;
    }
    function withoutHeader(string $name): MessageInterface {
        $next = clone $this;
        if (isset($next->headers[to_title($name)])) {
            unset($next->headers[to_title($name)]);
        }
        return $next;
    }

    function getBody(): StreamInterface
    {
        return $this->body;
    }
    function withBody(StreamInterface $body): MessageInterface
    {
        $next = clone $this;
        $next->body = $body;
        return $next;
    }

    function __clone() {
        $this->headers = array_merge(array(), $this->headers);
        $this->uri = clone $this->uri;
        $this->body = clone $this->body;
    }
}

?>