<?php

declare(strict_types=1);

/**
 * APO-198 — MCP front-controller entry.
 *
 * Streamable HTTP transport for MCP:
 *   - POST /mcp                  — single JSON-RPC request/response (canonical)
 *   - POST /api/mcp/v1/messages  — legacy alias (soft-deprecated; same handler)
 *   - OPTIONS preflight respects CORS allow-list
 *
 * The body is JSON-RPC 2.0; we hand it to MyronMcpServer for method dispatch.
 * The dispatcher inside the server reuses MyronActionDispatcher for tools/call,
 * so audit + rate-limit gates apply identically to MCP traffic.
 *
 * Protocol version is negotiated via the initialize handshake — not the URL —
 * so the path stays unversioned.
 */

require_once dirname(__DIR__, 2) . '/system/mcp/Server.php';
require_once dirname(__DIR__, 2) . '/system/api/CorsPolicy.php';

if (!function_exists('myronSendMcpNoStoreHeaders')) {
    function myronSendMcpNoStoreHeaders(): void
    {
        header('Cache-Control: no-store, no-cache, must-revalidate, max-age=0');
        header('Pragma: no-cache');
        header('Expires: Thu, 01 Jan 1970 00:00:00 GMT');
    }
}

/**
 * @param array<string, mixed> $response
 */
if (!function_exists('myronApplyMcpAuthErrorStatus')) {
    function myronApplyMcpAuthErrorStatus(array $response): void
    {
        $code = (string) ($response['error']['data']['code'] ?? '');
        if ($code !== 'MCP_AUTH_FAILED') {
            return;
        }

        http_response_code(401);
        $metadataUrl = function_exists('myronMcpProtectedResourceMetadataUrl')
            ? myronMcpProtectedResourceMetadataUrl()
            : rtrim((string) (getenv('MYRON_APP_URL') ?: ''), '/') . '/.well-known/oauth-protected-resource';
        if ($metadataUrl !== '') {
            header('WWW-Authenticate: Bearer resource_metadata="' . addcslashes($metadataUrl, '\\"') . '"');
        }
    }
}

$method = strtoupper((string) ($_SERVER['REQUEST_METHOD'] ?? 'GET'));
$origin = (string) ($_SERVER['HTTP_ORIGIN'] ?? '');

myronSendMcpNoStoreHeaders();

$cors = function_exists('myronMcpCorsPolicy') ? myronMcpCorsPolicy() : null;
$corsAllowed = $cors !== null ? $cors->isOriginAllowed($origin) : ($origin === '');

// CORS preflight
if ($method === 'OPTIONS') {
    if ($corsAllowed && $origin !== '') {
        header('Access-Control-Allow-Origin: ' . $origin);
        header('Access-Control-Allow-Methods: POST, OPTIONS');
        header('Access-Control-Allow-Headers: Authorization, Content-Type, Accept, Mcp-Session-Id');
        header('Access-Control-Max-Age: 86400');
    }
    http_response_code(204);
    return;
}

// CORS check for actual requests when Origin is present
if ($origin !== '' && !$corsAllowed) {
    http_response_code(403);
    header('Content-Type: application/json; charset=utf-8');
    echo json_encode(['jsonrpc' => '2.0', 'id' => null, 'error' => ['code' => -32000, 'message' => 'CORS origin not permitted.']]);
    return;
}
if ($corsAllowed && $origin !== '') {
    header('Access-Control-Allow-Origin: ' . $origin);
    header('Access-Control-Expose-Headers: Mcp-Session-Id');
}

if ($method !== 'POST') {
    http_response_code(405);
    header('Content-Type: application/json; charset=utf-8');
    echo json_encode(['jsonrpc' => '2.0', 'id' => null, 'error' => ['code' => -32600, 'message' => 'Method Not Allowed. Use POST /mcp.']]);
    return;
}

// Headers
$headers = [];
if (function_exists('getallheaders')) {
    $raw = getallheaders();
    if (is_array($raw)) {
        foreach ($raw as $name => $value) {
            $headers[(string) $name] = (string) $value;
        }
    }
}
foreach ($_SERVER as $key => $value) {
    if (!is_string($key) || !str_starts_with($key, 'HTTP_')) continue;
    $headerName = str_replace('_', '-', strtolower(substr($key, 5)));
    $cased = ucwords($headerName, '-');
    if (!isset($headers[$cased])) {
        $headers[$cased] = (string) $value;
    }
}
foreach (['HTTP_AUTHORIZATION', 'REDIRECT_HTTP_AUTHORIZATION'] as $key) {
    $value = $_SERVER[$key] ?? null;
    if (!is_string($value) || trim($value) === '') {
        continue;
    }
    if (!isset($headers[$key])) {
        $headers[$key] = $value;
    }
    if (!isset($headers['Authorization']) && str_ends_with($key, 'AUTHORIZATION')) {
        $headers['Authorization'] = $value;
    }
}

// Body
$rawBody = (string) file_get_contents('php://input');
$decoded = $rawBody !== '' ? json_decode($rawBody, true) : null;

header('Content-Type: application/json; charset=utf-8');
header('X-Content-Type-Options: nosniff');

if (!is_array($decoded)) {
    echo json_encode(MyronMcpServer::errorResponse(null, -32700, 'Parse error: body is not valid JSON.'));
    return;
}

// Single request OR batch
$server = myronMcpServer();
if (array_is_list($decoded)) {
    $responses = [];
    $responseHeaders = [];
    foreach ($decoded as $req) {
        if (!is_array($req)) continue;
        $resp = $server->handle($headers, $req);
        $responseHeaders = array_merge($responseHeaders, $server->consumeResponseHeaders());
        if ($resp !== []) {
            $responses[] = $resp;
        }
    }
    foreach ($responseHeaders as $name => $value) {
        header($name . ': ' . $value);
    }
    echo json_encode($responses, JSON_UNESCAPED_SLASHES);
} else {
    $resp = $server->handle($headers, $decoded);
    myronApplyMcpAuthErrorStatus($resp);
    foreach ($server->consumeResponseHeaders() as $name => $value) {
        header($name . ': ' . $value);
    }
    if ($resp === []) {
        // Notification: no response body
        http_response_code(204);
        return;
    }
    echo json_encode($resp, JSON_UNESCAPED_SLASHES);
}
