<?php
/**
 * MCP (Model Context Protocol) server
 *
 * Exposes LLMagnet data as MCP tools so AI assistants (Claude Desktop, etc.)
 * can query site visibility stats, bot traffic, and get recommendations.
 *
 * Endpoint: POST /wp-json/llmagnet/mcp/v1
 * Auth:     Optional Bearer token (llmagnet_mcp_api_token via WP-CLI), Application Password (Basic), or cookie + X-WP-Nonce.
 *
 * @package LLMagnet_AI_SEO_Optimizer
 */

namespace LLMagnet_AI_SEO_Optimizer;

class MCP {

    /** @var Analytics */
    private $analytics;

    /** @var Visibility_Score */
    private $visibility_score;

    public function __construct( $analytics, $visibility_score ) {
        $this->analytics       = $analytics;
        $this->visibility_score = $visibility_score;
    }

    public function init() {
        add_action( 'rest_api_init', [ $this, 'register_routes' ] );
    }

    public function register_routes() {
        register_rest_route( 'llmagnet/mcp', '/v1', [
            'methods'             => 'POST',
            'callback'            => [ $this, 'handle_request' ],
            'permission_callback' => [ $this, 'rest_permission_callback' ],
        ] );
    }

    /**
     * Bearer token (optional), Application Password, or cookie session with REST nonce.
     *
     * @param \WP_REST_Request $request Request.
     * @return bool
     */
    public function rest_permission_callback( \WP_REST_Request $request ) {
        $stored = get_option( 'llmagnet_mcp_api_token', '' );
        if ( is_string( $stored ) && $stored !== '' ) {
            $auth_header = $request->get_header( 'authorization' );
            if ( is_string( $auth_header ) && preg_match( '/^Bearer\s+(.+)$/i', $auth_header, $m ) ) {
                $token = trim( $m[1] );
                if ( $token !== '' && hash_equals( $stored, $token ) ) {
                    return true;
                }
            }
        }

        if ( ! is_user_logged_in() || ! current_user_can( 'manage_options' ) ) {
            return false;
        }

        if ( function_exists( 'wp_is_application_password_authenticated' ) && wp_is_application_password_authenticated() ) {
            return true;
        }

        $nonce = $request->get_header( 'X-WP-Nonce' );
        return is_string( $nonce ) && wp_verify_nonce( $nonce, 'wp_rest' );
    }

    // ── Request dispatcher ────────────────────────────────────────────────────

    public function handle_request( \WP_REST_Request $request ) {
        $body   = $request->get_json_params();
        $method = isset( $body['method'] ) ? $body['method'] : '';
        $id     = isset( $body['id'] ) ? $body['id'] : null;
        $params = isset( $body['params'] ) ? $body['params'] : [];

        switch ( $method ) {
            case 'initialize':
                return $this->handle_initialize( $id );

            case 'notifications/initialized':
                return $this->jsonrpc_success( $id, new \stdClass() );

            case 'ping':
                return $this->jsonrpc_success( $id, new \stdClass() );

            case 'tools/list':
                return $this->jsonrpc_success( $id, [ 'tools' => $this->get_tool_definitions() ] );

            case 'tools/call':
                return $this->handle_tool_call( $id, $params );

            default:
                return $this->jsonrpc_error( $id, -32601, 'Method not found: ' . $method );
        }
    }

    private function handle_initialize( $id ) {
        return $this->jsonrpc_success( $id, [
            'protocolVersion' => '2024-11-05',
            'capabilities'    => [ 'tools' => new \stdClass() ],
            'serverInfo'      => [
                'name'    => 'LLMagnet MCP',
                'version' => LLMAGNET_AISEO_VERSION,
            ],
        ] );
    }

    private function handle_tool_call( $id, $params ) {
        $name      = isset( $params['name'] ) ? $params['name'] : '';
        $arguments = isset( $params['arguments'] ) ? $params['arguments'] : [];

        switch ( $name ) {
            case 'get_site_info':
                $result = $this->tool_get_site_info();
                break;
            case 'get_visibility_score':
                $result = $this->tool_get_visibility_score( $arguments );
                break;
            case 'get_bot_traffic':
                $result = $this->tool_get_bot_traffic( $arguments );
                break;
            case 'get_top_pages':
                $result = $this->tool_get_top_pages();
                break;
            case 'get_bot_stats_table':
                $result = $this->tool_get_bot_stats_table();
                break;
            case 'get_recommendations':
                $result = $this->tool_get_recommendations( $arguments );
                break;
            default:
                return $this->jsonrpc_error( $id, -32602, 'Unknown tool: ' . $name );
        }

        return $this->jsonrpc_success( $id, [
            'content' => [ [
                'type' => 'text',
                'text' => wp_json_encode( $result, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE ),
            ] ],
        ] );
    }

    // ── Tools ─────────────────────────────────────────────────────────────────

    private function tool_get_site_info() {
        $llms_txt_path = ABSPATH . 'llms.txt';
        return [
            'site_name'          => get_bloginfo( 'name' ),
            'site_url'           => get_site_url(),
            'plugin_version'     => LLMAGNET_AISEO_VERSION,
            'wordpress_version'  => get_bloginfo( 'version' ),
            'llms_txt_exists'    => file_exists( $llms_txt_path ),
            'llms_txt_size'      => file_exists( $llms_txt_path ) ? filesize( $llms_txt_path ) : 0,
            'woocommerce_active' => class_exists( 'WooCommerce' ),
        ];
    }

    private function tool_get_visibility_score( $arguments ) {
        $range_days = isset( $arguments['range_days'] ) ? (int) $arguments['range_days'] : 30;
        $score_data = $this->visibility_score->get_latest_score( $range_days );
        if ( ! $score_data ) {
            $score_data = $this->visibility_score->compute_visibility_score( $range_days );
        }
        return $score_data;
    }

    private function tool_get_bot_traffic( $arguments ) {
        $days = isset( $arguments['days'] ) ? (int) $arguments['days'] : 30;
        return [
            'total_by_bot'  => $this->analytics->get_total_bot_visits(),
            'recent_visits' => $this->analytics->get_recent_bot_visits( $days ),
            'days_range'    => $days,
        ];
    }

    private function tool_get_top_pages() {
        return $this->analytics->get_page_stats();
    }

    private function tool_get_bot_stats_table() {
        return $this->analytics->get_bot_stats_for_table();
    }

    private function tool_get_recommendations( $arguments ) {
        $range_days = isset( $arguments['range_days'] ) ? (int) $arguments['range_days'] : 30;

        $score_data    = $this->visibility_score->get_latest_score( $range_days );
        if ( ! $score_data ) {
            $score_data = $this->visibility_score->compute_visibility_score( $range_days );
        }
        $total_visits  = $this->analytics->get_total_bot_visits();
        $recent_visits = $this->analytics->get_recent_bot_visits( $range_days );

        $recommendations = [];
        $score           = isset( $score_data['score'] ) ? (int) $score_data['score'] : 0;

        // Score-based
        if ( $score < 30 ) {
            $recommendations[] = [
                'priority' => 'critical',
                'area'     => 'visibility',
                'message'  => "Your AI visibility score is very low ({$score}/100). AI crawlers are barely discovering your content.",
                'action'   => 'Regenerate your llms.txt from the LLMagnet dashboard and verify robots.txt is not blocking AI crawlers.',
            ];
        } elseif ( $score < 60 ) {
            $recommendations[] = [
                'priority' => 'medium',
                'area'     => 'visibility',
                'message'  => "Your AI visibility score is {$score}/100. There is significant room for improvement.",
                'action'   => 'Review the score breakdown to identify the weakest sub-scores and address them first.',
            ];
        } else {
            $recommendations[] = [
                'priority' => 'low',
                'area'     => 'visibility',
                'message'  => "Good AI visibility score: {$score}/100. Keep your llms.txt up to date as you publish new content.",
                'action'   => 'Schedule regular llms.txt regeneration (weekly recommended).',
            ];
        }

        // llms.txt missing
        if ( ! file_exists( ABSPATH . 'llms.txt' ) ) {
            $recommendations[] = [
                'priority' => 'critical',
                'area'     => 'llms_txt',
                'message'  => 'No llms.txt file found at the site root.',
                'action'   => 'Generate your llms.txt immediately from the LLMagnet dashboard.',
            ];
        }

        // Total visits
        $total_count = 0;
        if ( is_array( $total_visits ) ) {
            foreach ( $total_visits as $data ) {
                if ( is_array( $data ) && isset( $data['count'] ) ) {
                    $total_count += (int) $data['count'];
                } elseif ( is_numeric( $data ) ) {
                    $total_count += (int) $data;
                }
            }
        }

        if ( $total_count === 0 ) {
            $recommendations[] = [
                'priority' => 'critical',
                'area'     => 'bot_traffic',
                'message'  => 'No AI bot visits have been detected at all.',
                'action'   => 'Verify robots.txt allows AI crawlers, the site is publicly accessible, and the plugin is tracking correctly.',
            ];
        }

        // Key bots absent
        $detected   = is_array( $total_visits ) ? array_keys( $total_visits ) : [];
        $watch_bots = [ 'GPTBot', 'ClaudeBot', 'PerplexityBot' ];
        foreach ( $watch_bots as $bot ) {
            if ( ! in_array( $bot, $detected, true ) ) {
                $recommendations[] = [
                    'priority' => 'high',
                    'area'     => 'bot_coverage',
                    'message'  => "{$bot} has not been detected on your site.",
                    'action'   => "Check that robots.txt allows {$bot} and that your llms.txt is publicly accessible.",
                ];
            }
        }

        return [
            'score'           => $score,
            'range_days'      => $range_days,
            'recommendations' => $recommendations,
        ];
    }

    // ── Tool definitions (JSON Schema) ────────────────────────────────────────

    private function get_tool_definitions() {
        return [
            [
                'name'        => 'get_site_info',
                'description' => 'Returns basic information about the WordPress site: name, URL, plugin version, llms.txt status, and whether WooCommerce is active.',
                'inputSchema' => [ 'type' => 'object', 'properties' => new \stdClass() ],
            ],
            [
                'name'        => 'get_visibility_score',
                'description' => 'Returns the current AI visibility score (0-100) and its breakdown by component (frequency, visit type, page coverage, URL types, etc.).',
                'inputSchema' => [
                    'type'       => 'object',
                    'properties' => [
                        'range_days' => [
                            'type'        => 'integer',
                            'description' => 'Number of days to analyze. Default: 30.',
                        ],
                    ],
                ],
            ],
            [
                'name'        => 'get_bot_traffic',
                'description' => 'Returns AI bot visit statistics: total visits per bot and a recent visit history.',
                'inputSchema' => [
                    'type'       => 'object',
                    'properties' => [
                        'days' => [
                            'type'        => 'integer',
                            'description' => 'Number of days of recent history to return. Default: 30.',
                        ],
                    ],
                ],
            ],
            [
                'name'        => 'get_top_pages',
                'description' => 'Returns per-page analytics showing which pages AI bots have visited, along with click counts and CTR.',
                'inputSchema' => [ 'type' => 'object', 'properties' => new \stdClass() ],
            ],
            [
                'name'        => 'get_bot_stats_table',
                'description' => 'Returns a summary table of all detected AI bots with impressions, clicks, CTR, and trend direction.',
                'inputSchema' => [ 'type' => 'object', 'properties' => new \stdClass() ],
            ],
            [
                'name'        => 'get_recommendations',
                'description' => 'Analyzes the site visibility data and returns a prioritized list of actionable recommendations for improving AI discoverability.',
                'inputSchema' => [
                    'type'       => 'object',
                    'properties' => [
                        'range_days' => [
                            'type'        => 'integer',
                            'description' => 'Number of days to analyze. Default: 30.',
                        ],
                    ],
                ],
            ],
        ];
    }

    // ── JSON-RPC helpers ──────────────────────────────────────────────────────

    private function jsonrpc_success( $id, $result ) {
        return new \WP_REST_Response( [
            'jsonrpc' => '2.0',
            'id'      => $id,
            'result'  => $result,
        ], 200 );
    }

    private function jsonrpc_error( $id, $code, $message ) {
        // JSON-RPC spec: errors still return HTTP 200
        return new \WP_REST_Response( [
            'jsonrpc' => '2.0',
            'id'      => $id,
            'error'   => [
                'code'    => $code,
                'message' => $message,
            ],
        ], 200 );
    }
}
