<?php
namespace boru\ai\Mcp;

use boru\ai\Http\GuzzleHttpClient;
use boru\ai\Http\HttpClientInterface;
use boru\ai\Http\HttpRequest;
use boru\ai\Http\HttpResponse;

class HttpProxyMcpClient implements McpClientInterface
{
    /** @var HttpClientInterface */
    protected $httpClient;

    /** @var int */
    protected $timeout = 10;

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

    public function __construct(HttpClientInterface $httpClient = null, array $options = array())
    {
        $this->httpClient = $httpClient ? $httpClient : new GuzzleHttpClient();

        if (isset($options['timeout']) && (int)$options['timeout'] > 0) {
            $this->timeout = (int)$options['timeout'];
        }
    }

    public function httpClient(HttpClientInterface $httpClient = null)
    {
        if ($httpClient !== null) {
            $this->httpClient = $httpClient;
            return $this;
        }
        return $this->httpClient;
    }

    public function initialize(McpServerConfig $serverConfig)
    {
        if (!$serverConfig->url()) {
            throw new \RuntimeException('MCP HTTP proxy server URL not configured for server "' . $serverConfig->name() . '"');
        }

        $this->serverConfig = $serverConfig;

        return array(
            'transport' => $serverConfig->transport(),
            'protocolVersion' => $serverConfig->protocolVersion(),
            'url' => $serverConfig->url(),
        );
    }

    public function listTools()
    {
        $response = $this->httpClient()->send($this->makeRequest('tools/list'));
        $decoded = $this->decodeJsonResponse($response, 'tools/list');

        if (isset($decoded['tools']) && is_array($decoded['tools'])) {
            return $decoded['tools'];
        }

        if (is_array($decoded)) {
            return $decoded;
        }

        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');
        }

        $request = $this->makeRequest('tools/call', array(
            'name' => $toolName,
            'arguments' => $arguments,
        ));

        $response = $this->httpClient()->send($request);
        return $this->decodeJsonResponse($response, 'tools/call');
    }

    protected function makeRequest($path, array $payload = null)
    {
        $serverConfig = $this->requireServerConfig();

        $request = new HttpRequest();
        $request->method('POST')
            ->url(rtrim($serverConfig->url(), '/') . '/' . ltrim($path, '/'))
            ->headers($this->buildHeaders($serverConfig))
            ->options(array(
                'timeout' => $this->timeout,
            ));

        if ($payload !== null) {
            $request->header('Content-Type', 'application/json');
            $request->json($payload);
        }

        return $request;
    }

    protected function buildHeaders(McpServerConfig $serverConfig)
    {
        $headers = $serverConfig->headers();
        $auth = $serverConfig->auth();

        if (!isset($headers['Accept'])) {
            $headers['Accept'] = 'application/json';
        }

        if (isset($auth['type']) && $auth['type'] === 'bearer' && isset($auth['token']) && $auth['token'] !== '') {
            if (!isset($headers['Authorization'])) {
                $headers['Authorization'] = 'Bearer ' . $auth['token'];
            }
        } elseif (isset($auth['type']) && $auth['type'] === 'header' && isset($auth['name']) && isset($auth['value'])) {
            if (!isset($headers[$auth['name']])) {
                $headers[$auth['name']] = $auth['value'];
            }
        }

        return $headers;
    }

    protected function decodeJsonResponse(HttpResponse $response, $operation)
    {
        $decoded = $response->json();

        if (!$response->isSuccess()) {
            $message = 'MCP HTTP proxy request failed for ' . $operation;
            if (is_array($decoded) && isset($decoded['error'])) {
                if (is_string($decoded['error'])) {
                    $message = $decoded['error'];
                } elseif (is_array($decoded['error']) && isset($decoded['error']['message'])) {
                    $message = $decoded['error']['message'];
                }
            } elseif (is_array($decoded) && isset($decoded['message']) && is_string($decoded['message'])) {
                $message = $decoded['message'];
            }
            throw new \RuntimeException($message, $response->statusCode());
        }

        if (!is_array($decoded)) {
            throw new \RuntimeException('Invalid JSON returned from MCP HTTP proxy for ' . $operation);
        }

        return $decoded;
    }

    protected function requireServerConfig()
    {
        if (!$this->serverConfig instanceof McpServerConfig) {
            throw new \RuntimeException('MCP client must be initialized before use');
        }

        return $this->serverConfig;
    }
}
