<?php

declare(strict_types=1);

namespace SHServ\Integrations\GAuth\Http;

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

final class Psr7Request implements RequestInterface
{
    private string $method;
    private string $uri;
    private array $headers = [];
    private StreamInterface $body;
    private string $protocolVersion = '1.1';
    private string $requestTarget = '';

    public function __construct(string $method, string $uri, array $headers = [], ?StreamInterface $body = null)
    {
        $this->method = $method;
        $this->uri = $uri;
        $this->headers = $this->normalizeHeaders($headers);
        $this->body = $body ?? new Psr7Stream('');
    }

    private function normalizeHeaders(array $headers): array
    {
        $normalized = [];
        foreach ($headers as $name => $values) {
            $name = $this->normalizeHeaderName($name);
            if (!is_array($values)) {
                $values = [$values];
            }
            $normalized[$name] = array_map('strval', $values);
        }
        return $normalized;
    }

    private function normalizeHeaderName(string $name): string
    {
        return strtolower($name);
    }

    public function getProtocolVersion(): string
    {
        return $this->protocolVersion;
    }

    public function withProtocolVersion(string $version): RequestInterface
    {
        $new = clone $this;
        $new->protocolVersion = $version;
        return $new;
    }

    public function getHeaders(): array
    {
        $result = [];
        foreach ($this->headers as $name => $values) {
            $result[$this->studlyCase($name)] = $values;
        }
        return $result;
    }

    public function hasHeader(string $name): bool
    {
        return isset($this->headers[$this->normalizeHeaderName($name)]);
    }

    public function getHeader(string $name): array
    {
        return $this->headers[$this->normalizeHeaderName($name)] ?? [];
    }

    public function getHeaderLine(string $name): string
    {
        return implode(', ', $this->getHeader($name));
    }

    public function withHeader(string $name, $value): RequestInterface
    {
        $new = clone $this;
        $new->headers[$this->normalizeHeaderName($name)] = is_array($value) ? array_map('strval', $value) : [(string) $value];
        return $new;
    }

    public function withAddedHeader(string $name, $value): RequestInterface
    {
        $new = clone $this;
        $name = $this->normalizeHeaderName($name);
        if (!isset($new->headers[$name])) {
            $new->headers[$name] = [];
        }
        $new->headers[$name] = array_merge($new->headers[$name], is_array($value) ? array_map('strval', $value) : [(string) $value]);
        return $new;
    }

    public function withoutHeader(string $name): RequestInterface
    {
        $new = clone $this;
        unset($new->headers[$this->normalizeHeaderName($name)]);
        return $new;
    }

    public function getBody(): StreamInterface
    {
        return $this->body;
    }

    public function withBody(StreamInterface $body): RequestInterface
    {
        $new = clone $this;
        $new->body = $body;
        return $new;
    }

    public function getRequestTarget(): string
    {
        if ($this->requestTarget !== '') {
            return $this->requestTarget;
        }
        return $this->uri;
    }

    public function withRequestTarget(string $requestTarget): RequestInterface
    {
        $new = clone $this;
        $new->requestTarget = $requestTarget;
        return $new;
    }

    public function getMethod(): string
    {
        return $this->method;
    }

    public function withMethod(string $method): RequestInterface
    {
        $new = clone $this;
        $new->method = $method;
        return $new;
    }

    public function getUri(): UriInterface
    {
        return new Psr7Uri($this->uri);
    }

    public function withUri(UriInterface $uri, bool $preserveHost = false): RequestInterface
    {
        $new = clone $this;
        $new->uri = (string) $uri;
        return $new;
    }

    private function studlyCase(string $name): string
    {
        return str_replace(' ', '-', ucwords(str_replace('-', ' ', $name)));
    }
}

final class Psr7Uri implements UriInterface
{
    private string $uri;

    public function __construct(string $uri = '')
    {
        $this->uri = $uri;
    }

    public function __toString(): string
    {
        return $this->uri;
    }

    public function getScheme(): string { return ''; }
    public function getAuthority(): string { return ''; }
    public function getUserInfo(): string { return ''; }
    public function getHost(): string { return ''; }
    public function getPort(): ?int { return null; }
    public function getPath(): string { return $this->uri; }
    public function getQuery(): string { return ''; }
    public function getFragment(): string { return ''; }
    public function withScheme(string $scheme): UriInterface { return $this; }
    public function withUserInfo(string $user, ?string $password = null): UriInterface { return $this; }
    public function withHost(string $host): UriInterface { return $this; }
    public function withPort(?int $port): UriInterface { return $this; }
    public function withPath(string $path): UriInterface { $new = clone $this; $new->uri = $path; return $new; }
    public function withQuery(string $query): UriInterface { return $this; }
    public function withFragment(string $fragment): UriInterface { return $this; }
}
