<?php

declare(strict_types=1);

namespace RedMCP;

final class McpStdioServer
{
    private McpDispatcher $dispatcher;

    public function __construct(McpDispatcher $dispatcher)
    {
        $this->dispatcher = $dispatcher;
    }

    public function run(): void
    {
        while (($message = $this->readMessage(STDIN)) !== null) {
            $response = $this->dispatcher->handleMessage($message, ['transport' => 'stdio']);
            if ($response !== null) {
                $this->writeMessage($response);
            }
        }
    }

    /**
     * @param resource $stream
     *
     * @return array<string,mixed>|null
     */
    private function readMessage($stream): ?array
    {
        $headers = [];
        while (($line = fgets($stream)) !== false) {
            $line = rtrim($line, "\r\n");
            if ($line === '') {
                break;
            }
            if (!str_contains($line, ':')) {
                $decoded = json_decode($line, true);
                return is_array($decoded) ? $decoded : null;
            }
            [$name, $value] = explode(':', $line, 2);
            $headers[strtolower(trim($name))] = trim($value);
        }

        if ($line === false && $headers === []) {
            return null;
        }

        $length = isset($headers['content-length']) ? (int) $headers['content-length'] : 0;
        if ($length <= 0) {
            return null;
        }

        $body = '';
        while (strlen($body) < $length && !feof($stream)) {
            $chunk = fread($stream, $length - strlen($body));
            if ($chunk === false || $chunk === '') {
                break;
            }
            $body .= $chunk;
        }

        $decoded = json_decode($body, true);
        return is_array($decoded) ? $decoded : null;
    }

    /**
     * @param array<string,mixed> $message
     */
    private function writeMessage(array $message): void
    {
        $body = json_encode($message, JSON_UNESCAPED_SLASHES);
        if ($body === false) {
            return;
        }

        fwrite(STDOUT, 'Content-Length: ' . strlen($body) . "\r\n\r\n" . $body);
        fflush(STDOUT);
    }
}
