<?php

declare(strict_types=1);

namespace MCP\Server\Auth\Handlers;

use MCP\Shared\OAuthMetadata;
use MCP\Shared\OAuthProtectedResourceMetadata;
use Psr\Http\Message\ResponseFactoryInterface;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Message\StreamFactoryInterface;

/**
 * Handles OAuth metadata endpoints.
 */
final class MetadataHandler
{
    public function __construct(
        private readonly OAuthMetadata $oauthMetadata,
        private readonly OAuthProtectedResourceMetadata $protectedResourceMetadata,
        private readonly ?ResponseFactoryInterface $responseFactory = null,
        private readonly ?StreamFactoryInterface $streamFactory = null
    ) {
    }

    public function handleOAuthMetadata(ServerRequestInterface $request): ResponseInterface
    {
        $response = $this->createResponse();

        $json = json_encode($this->oauthMetadata->jsonSerialize(), JSON_THROW_ON_ERROR);
        $body = $this->createStream($json);

        return $response
            ->withStatus(200)
            ->withHeader('Content-Type', 'application/json')
            ->withHeader('Cache-Control', 'public, max-age=3600')
            ->withBody($body);
    }

    public function handleProtectedResourceMetadata(ServerRequestInterface $request): ResponseInterface
    {
        $response = $this->createResponse();

        $json = json_encode($this->protectedResourceMetadata->jsonSerialize(), JSON_THROW_ON_ERROR);
        $body = $this->createStream($json);

        return $response
            ->withStatus(200)
            ->withHeader('Content-Type', 'application/json')
            ->withHeader('Cache-Control', 'public, max-age=3600')
            ->withBody($body);
    }

    private function createResponse(): ResponseInterface
    {
        if ($this->responseFactory !== null) {
            return $this->responseFactory->createResponse();
        }

        // Fallback to a simple implementation
        return new class () implements ResponseInterface {
            private int $statusCode = 200;

            private string $reasonPhrase = 'OK';

            private array $headers = [];

            private $body = null;

            private string $version = '1.1';

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

            public function withProtocolVersion($version): ResponseInterface
            {
                $new = clone $this;
                $new->version = $version;

                return $new;
            }

            public function getHeaders(): array
            {
                return $this->headers;
            }

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

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

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

            public function withHeader($name, $value): ResponseInterface
            {
                $new = clone $this;
                $new->headers[strtolower($name)] = is_array($value) ? $value : [$value];

                return $new;
            }

            public function withAddedHeader($name, $value): ResponseInterface
            {
                $new = clone $this;
                $existing = $new->headers[strtolower($name)] ?? [];
                $new->headers[strtolower($name)] = array_merge($existing, is_array($value) ? $value : [$value]);

                return $new;
            }

            public function withoutHeader($name): ResponseInterface
            {
                $new = clone $this;
                unset($new->headers[strtolower($name)]);

                return $new;
            }

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

            public function withBody($body): ResponseInterface
            {
                $new = clone $this;
                $new->body = $body;

                return $new;
            }

            public function getStatusCode(): int
            {
                return $this->statusCode;
            }

            public function withStatus($code, $reasonPhrase = ''): ResponseInterface
            {
                $new = clone $this;
                $new->statusCode = $code;
                $new->reasonPhrase = $reasonPhrase;

                return $new;
            }

            public function getReasonPhrase(): string
            {
                return $this->reasonPhrase;
            }
        };
    }

    private function createStream(string $content)
    {
        if ($this->streamFactory !== null) {
            return $this->streamFactory->createStream($content);
        }

        // Fallback to a simple implementation
        return new class ($content) {
            public function __construct(private string $content)
            {
            }

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

            public function close(): void
            {
            }

            public function detach()
            {
                return null;
            }

            public function getSize(): ?int
            {
                return strlen($this->content);
            }

            public function tell(): int
            {
                return 0;
            }

            public function eof(): bool
            {
                return true;
            }

            public function isSeekable(): bool
            {
                return false;
            }

            public function seek($offset, $whence = SEEK_SET): void
            {
            }

            public function rewind(): void
            {
            }

            public function isWritable(): bool
            {
                return false;
            }

            public function write($string): int
            {
                return 0;
            }

            public function isReadable(): bool
            {
                return true;
            }

            public function read($length): string
            {
                return $this->content;
            }

            public function getContents(): string
            {
                return $this->content;
            }

            public function getMetadata($key = null)
            {
                return null;
            }
        };
    }
}
