<?php
namespace boru\ai\Mcp;

class TcpMcpClient implements McpClientInterface
{
    /** @var TcpMcpConnectorInterface */
    protected $connector;

    /** @var TcpMcpConnectionInterface|null */
    protected $connection;

    /** @var McpServerConfig|null */
    protected $serverConfig;

    /** @var bool */
    protected $initialized = false;

    /** @var int */
    protected $nextId = 1;

    public function __construct(TcpMcpConnectorInterface $connector = null)
    {
        $this->connector = $connector ? $connector : new NativeTcpMcpConnector();
    }

    public function initialize(McpServerConfig $serverConfig)
    {
        $this->close();
        $this->serverConfig = $serverConfig;
        $this->connection = $this->connector->connect($serverConfig);
        $this->initialized = false;
        $this->nextId = 1;

        $params = array(
            'protocolVersion' => $serverConfig->protocolVersion(),
        );

        $auth = $serverConfig->auth();
        if (isset($auth['token']) && is_string($auth['token']) && $auth['token'] !== '') {
            $params['authToken'] = $auth['token'];
        }

        $response = $this->sendRequest('initialize', $params);
        $this->initialized = true;

        return $response;
    }

    public function listTools()
    {
        $response = $this->sendRequest('tools/list', new \stdClass());
        if (isset($response['tools']) && is_array($response['tools'])) {
            return $response['tools'];
        }

        throw new \RuntimeException('Invalid MCP tools/list response payload');
    }

    public function callTool($toolName, array $arguments)
    {
        if (!is_string($toolName) || trim($toolName) === '') {
            throw new \InvalidArgumentException('MCP tool name is required');
        }

        return $this->sendRequest('tools/call', array(
            'name' => $toolName,
            'arguments' => $arguments,
        ));
    }

    public function __destruct()
    {
        $this->close();
    }

    protected function sendRequest($method, $params)
    {
        $connection = $this->requireConnection($method);
        $id = $this->nextId++;

        $payload = array(
            'jsonrpc' => '2.0',
            'id' => $id,
            'method' => $method,
            'params' => $params,
        );

        $connection->writeLine(json_encode($payload, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE));

        while (true) {
            $line = $connection->readLine();
            if ($line === false) {
                throw new \RuntimeException('No response received from MCP TCP server for method "' . $method . '"');
            }

            $line = trim($line);
            if ($line === '') {
                continue;
            }

            $decoded = json_decode($line, true);
            if (!is_array($decoded)) {
                continue;
            }
            if (!array_key_exists('id', $decoded) || $decoded['id'] !== $id) {
                continue;
            }

            if (isset($decoded['error']) && is_array($decoded['error'])) {
                $message = isset($decoded['error']['message']) ? $decoded['error']['message'] : 'MCP TCP request failed';
                $code = isset($decoded['error']['code']) ? (int)$decoded['error']['code'] : 0;
                throw new \RuntimeException($message, $code);
            }

            if (!isset($decoded['result']) || !is_array($decoded['result'])) {
                throw new \RuntimeException('Invalid MCP TCP response payload for method "' . $method . '"');
            }

            return $decoded['result'];
        }
    }

    protected function requireConnection($method)
    {
        if (!$this->connection instanceof TcpMcpConnectionInterface) {
            throw new \RuntimeException('MCP TCP client must be initialized before calling ' . $method);
        }

        if (!$this->initialized && $method !== 'initialize') {
            throw new \RuntimeException('MCP TCP client must complete initialize before calling ' . $method);
        }

        return $this->connection;
    }

    protected function close()
    {
        if ($this->connection instanceof TcpMcpConnectionInterface) {
            $this->connection->close();
        }
        $this->connection = null;
        $this->initialized = false;
    }
}
