<?php
/**
 * GGM Commerce MCP Server
 *
 * Main server class for handling Model Context Protocol requests
 * Registers resources, tools, and prompts for AI assistant integration
 *
 * @package GGM_Commerce
 * @since 1.0.0
 */

// Prevent direct access
if (!defined('ABSPATH')) {
    exit;
}

class GGM_Commerce_MCP_Server {

    /**
     * Server instance
     * @var GGM_Commerce_MCP_Server
     */
    private static $instance = null;

    /**
     * Registered resources
     * @var array
     */
    private $resources = [];

    /**
     * Registered tools
     * @var array
     */
    private $tools = [];

    /**
     * Registered prompts
     * @var array
     */
    private $prompts = [];

    /**
     * Server metadata
     * @var array
     */
    private $server_info = [
        'name' => 'ggm-commerce',
        'version' => '1.0.0',
        'protocol_version' => '2024-11-05',
        'capabilities' => [
            'resources' => [],
            'tools' => [],
            'prompts' => []
        ]
    ];

    /**
     * Get singleton instance
     */
    public static function get_instance() {
        if (self::$instance === null) {
            self::$instance = new self();
        }
        return self::$instance;
    }

    /**
     * Constructor
     */
    private function __construct() {
        $this->initialize();
    }

    /**
     * Initialize the MCP server
     */
    private function initialize() {
        // Register built-in capabilities
        $this->server_info['capabilities']['resources'] = (object)['subscribe' => true];
        $this->server_info['capabilities']['tools'] = (object)[];
        $this->server_info['capabilities']['prompts'] = (object)[];
    }

    /**
     * Handle JSON-RPC 2.0 request
     *
     * @param array $request The decoded JSON-RPC request
     * @return array JSON-RPC response
     */
    public function handle_request($request) {
        // Validate JSON-RPC format
        if (!isset($request['jsonrpc']) || $request['jsonrpc'] !== '2.0') {
            return $this->error_response(null, -32600, 'Invalid Request: Missing or invalid jsonrpc version');
        }

        if (!isset($request['method'])) {
            return $this->error_response(
                isset($request['id']) ? $request['id'] : null,
                -32600,
                'Invalid Request: Missing method'
            );
        }

        $method = $request['method'];
        $params = isset($request['params']) ? $request['params'] : [];
        $id = isset($request['id']) ? $request['id'] : null;

        // Route to appropriate handler
        try {
            switch ($method) {
                case 'initialize':
                    $result = $this->handle_initialize($params);
                    break;

                case 'resources/list':
                    $result = $this->handle_resources_list($params);
                    break;

                case 'resources/read':
                    $result = $this->handle_resources_read($params);
                    break;

                case 'tools/list':
                    $result = $this->handle_tools_list($params);
                    break;

                case 'tools/call':
                    $result = $this->handle_tools_call($params);
                    break;

                case 'prompts/list':
                    $result = $this->handle_prompts_list($params);
                    break;

                case 'prompts/get':
                    $result = $this->handle_prompts_get($params);
                    break;

                default:
                    return $this->error_response($id, -32601, "Method not found: {$method}");
            }

            return $this->success_response($id, $result);

        } catch (Exception $e) {
            return $this->error_response($id, -32603, 'Internal error: ' . $e->getMessage());
        }
    }

    /**
     * Handle initialize request
     */
    private function handle_initialize($params) {
        return [
            'protocolVersion' => $this->server_info['protocol_version'],
            'capabilities' => $this->server_info['capabilities'],
            'serverInfo' => [
                'name' => $this->server_info['name'],
                'version' => $this->server_info['version']
            ]
        ];
    }

    /**
     * Handle resources/list request
     */
    private function handle_resources_list($params) {
        $resource_list = [];

        foreach ($this->resources as $uri => $resource) {
            $resource_list[] = [
                'uri' => $uri,
                'name' => $resource['name'],
                'description' => $resource['description'],
                'mimeType' => $resource['mimeType']
            ];
        }

        return ['resources' => $resource_list];
    }

    /**
     * Handle resources/read request
     */
    private function handle_resources_read($params) {
        if (!isset($params['uri'])) {
            throw new Exception('Missing required parameter: uri');
        }

        $uri = $params['uri'];

        // Check for URI template matching
        $resource = $this->find_resource($uri);

        if (!$resource) {
            // phpcs:ignore WordPress.Security.EscapeOutput.ExceptionNotEscaped -- sanitize_text_field provides adequate sanitization for exception messages
            throw new Exception(sprintf('Resource not found: %s', sanitize_text_field($uri)));
        }

        // Call the resource handler
        $handler = $resource['handler'];
        $uri_params = $this->extract_uri_params($uri, $resource['uri']);

        $data = call_user_func($handler, $uri_params);

        return [
            'contents' => [
                [
                    'uri' => $uri,
                    'mimeType' => $resource['mimeType'],
                    'text' => wp_json_encode($data, JSON_PRETTY_PRINT)
                ]
            ]
        ];
    }

    /**
     * Handle tools/list request
     */
    private function handle_tools_list($params) {
        $tool_list = [];

        foreach ($this->tools as $name => $tool) {
            $tool_list[] = [
                'name' => $name,
                'description' => $tool['description'],
                'inputSchema' => $tool['inputSchema']
            ];
        }

        return ['tools' => $tool_list];
    }

    /**
     * Handle tools/call request
     */
    private function handle_tools_call($params) {
        if (!isset($params['name'])) {
            throw new Exception('Missing required parameter: name');
        }

        $name = $params['name'];
        $arguments = isset($params['arguments']) ? $params['arguments'] : [];

        if (!isset($this->tools[$name])) {
            // phpcs:ignore WordPress.Security.EscapeOutput.ExceptionNotEscaped -- sanitize_text_field provides adequate sanitization for exception messages
            throw new Exception(sprintf('Tool not found: %s', sanitize_text_field($name)));
        }

        $tool = $this->tools[$name];
        $handler = $tool['handler'];

        // Call the tool handler
        $result = call_user_func($handler, $arguments);

        // Format result as MCP content
        return [
            'content' => [
                [
                    'type' => 'text',
                    'text' => is_string($result) ? $result : wp_json_encode($result, JSON_PRETTY_PRINT)
                ]
            ]
        ];
    }

    /**
     * Handle prompts/list request
     */
    private function handle_prompts_list($params) {
        $prompt_list = [];

        foreach ($this->prompts as $name => $prompt) {
            $prompt_list[] = [
                'name' => $name,
                'description' => $prompt['description'],
                'arguments' => isset($prompt['arguments']) ? $prompt['arguments'] : []
            ];
        }

        return ['prompts' => $prompt_list];
    }

    /**
     * Handle prompts/get request
     */
    private function handle_prompts_get($params) {
        if (!isset($params['name'])) {
            throw new Exception('Missing required parameter: name');
        }

        $name = $params['name'];
        $arguments = isset($params['arguments']) ? $params['arguments'] : [];

        if (!isset($this->prompts[$name])) {
            // phpcs:ignore WordPress.Security.EscapeOutput.ExceptionNotEscaped -- sanitize_text_field provides adequate sanitization for exception messages
            throw new Exception(sprintf('Prompt not found: %s', sanitize_text_field($name)));
        }

        $prompt = $this->prompts[$name];
        $handler = $prompt['handler'];

        // Call the prompt handler
        $result = call_user_func($handler, $arguments);

        return $result;
    }

    /**
     * Register a resource
     *
     * @param string $uri Resource URI or URI template
     * @param string $name Resource name
     * @param string $description Resource description
     * @param callable $handler Handler function
     * @param string $mimeType MIME type (default: application/json)
     */
    public function register_resource($uri, $name, $description, $handler, $mimeType = 'application/json') {
        $this->resources[$uri] = [
            'uri' => $uri,
            'name' => $name,
            'description' => $description,
            'handler' => $handler,
            'mimeType' => $mimeType
        ];
    }

    /**
     * Register a tool
     *
     * @param string $name Tool name
     * @param string $description Tool description
     * @param array $inputSchema JSON Schema for input validation
     * @param callable $handler Handler function
     */
    public function register_tool($name, $description, $inputSchema, $handler) {
        $this->tools[$name] = [
            'name' => $name,
            'description' => $description,
            'inputSchema' => $inputSchema,
            'handler' => $handler
        ];
    }

    /**
     * Register a prompt
     *
     * @param string $name Prompt name
     * @param string $description Prompt description
     * @param callable $handler Handler function
     * @param array $arguments Optional prompt arguments
     */
    public function register_prompt($name, $description, $handler, $arguments = []) {
        $this->prompts[$name] = [
            'name' => $name,
            'description' => $description,
            'handler' => $handler,
            'arguments' => $arguments
        ];
    }

    /**
     * Find resource by URI (supports templates)
     */
    private function find_resource($uri) {
        // Exact match first
        if (isset($this->resources[$uri])) {
            return $this->resources[$uri];
        }

        // Try template matching
        foreach ($this->resources as $template => $resource) {
            if ($this->uri_matches_template($uri, $template)) {
                return $resource;
            }
        }

        return null;
    }

    /**
     * Check if URI matches template
     */
    private function uri_matches_template($uri, $template) {
        $pattern = preg_replace('/\{[^}]+\}/', '([^/]+)', preg_quote($template, '/'));
        return preg_match("/^{$pattern}$/", $uri);
    }

    /**
     * Extract parameters from URI based on template
     */
    private function extract_uri_params($uri, $template) {
        preg_match_all('/\{([^}]+)\}/', $template, $param_names);
        $pattern = preg_replace('/\{[^}]+\}/', '([^/]+)', preg_quote($template, '/'));

        if (preg_match("/^{$pattern}$/", $uri, $matches)) {
            $params = [];
            for ($i = 0; $i < count($param_names[1]); $i++) {
                $params[$param_names[1][$i]] = $matches[$i + 1];
            }
            return $params;
        }

        return [];
    }

    /**
     * Create success response
     */
    private function success_response($id, $result) {
        return [
            'jsonrpc' => '2.0',
            'id' => $id,
            'result' => $result
        ];
    }

    /**
     * Create error response
     */
    private function error_response($id, $code, $message, $data = null) {
        $error = [
            'code' => $code,
            'message' => $message
        ];

        if ($data !== null) {
            $error['data'] = $data;
        }

        return [
            'jsonrpc' => '2.0',
            'id' => $id,
            'error' => $error
        ];
    }
}
