<?php
/**
 * Request_Context header parsing + UUID fallback tests.
 *
 * @package Ppok\AIToolkit\Tests
 * @license GPL-2.0-or-later
 */

namespace Ppok\AIToolkit\Tests;

use Ppok\AIToolkit\Request_Context;

class Test_Request_Context extends \WP_UnitTestCase {

	public function test_uses_x_request_id_for_correlation_id(): void {
		$request = new \WP_REST_Request( 'GET', '/wp/v2/posts' );
		$request->set_header( 'X-Request-ID', 'req-abc-123' );
		$request->set_header( 'X-MCP-Session', 'session-xyz' );

		$ctx = new Request_Context();
		$ctx->capture( $request );

		$this->assertSame( 'req-abc-123', $ctx->get_correlation_id() );
	}

	public function test_falls_back_to_x_mcp_session_when_x_request_id_missing(): void {
		$request = new \WP_REST_Request( 'GET', '/wp/v2/posts' );
		$request->set_header( 'X-MCP-Session', 'session-only-789' );

		$ctx = new Request_Context();
		$ctx->capture( $request );

		$this->assertSame( 'session-only-789', $ctx->get_correlation_id() );
	}

	public function test_generates_uuid_when_no_correlation_headers(): void {
		$request = new \WP_REST_Request( 'GET', '/wp/v2/posts' );

		$ctx = new Request_Context();
		$ctx->capture( $request );

		$correlation = $ctx->get_correlation_id();
		$this->assertNotEmpty( $correlation );
		$this->assertMatchesRegularExpression(
			'/^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i',
			$correlation,
			'Fallback correlation_id should be a UUID v4.'
		);
	}

	public function test_captures_user_agent_into_mcp_client(): void {
		$request = new \WP_REST_Request( 'GET', '/wp/v2/posts' );
		$request->set_header( 'User-Agent', 'Claude Desktop/0.7.4' );

		$ctx = new Request_Context();
		$ctx->capture( $request );

		$this->assertSame( 'Claude Desktop/0.7.4', $ctx->get_mcp_client() );
	}

	public function test_null_mcp_client_when_no_user_agent(): void {
		$request = new \WP_REST_Request( 'GET', '/wp/v2/posts' );

		$ctx = new Request_Context();
		$ctx->capture( $request );

		$this->assertNull( $ctx->get_mcp_client() );
	}

	public function test_captures_remote_addr_into_ip(): void {
		$previous_remote          = $_SERVER['REMOTE_ADDR'] ?? null;
		$_SERVER['REMOTE_ADDR']   = '203.0.113.7';

		try {
			$ctx = new Request_Context();
			$ctx->capture( new \WP_REST_Request( 'GET', '/wp/v2/posts' ) );
			$this->assertSame( '203.0.113.7', $ctx->get_ip() );
		} finally {
			if ( null === $previous_remote ) {
				unset( $_SERVER['REMOTE_ADDR'] );
			} else {
				$_SERVER['REMOTE_ADDR'] = $previous_remote;
			}
		}
	}

	public function test_truncates_long_correlation_id_to_64_chars(): void {
		$request = new \WP_REST_Request( 'GET', '/wp/v2/posts' );
		$long    = str_repeat( 'a', 200 );
		$request->set_header( 'X-Request-ID', $long );

		$ctx = new Request_Context();
		$ctx->capture( $request );

		$this->assertSame( 64, strlen( $ctx->get_correlation_id() ) );
	}

	public function test_truncates_long_user_agent_to_191_chars(): void {
		$request = new \WP_REST_Request( 'GET', '/wp/v2/posts' );
		$request->set_header( 'User-Agent', str_repeat( 'u', 300 ) );

		$ctx = new Request_Context();
		$ctx->capture( $request );

		$this->assertSame( 191, strlen( $ctx->get_mcp_client() ) );
	}

	public function test_filter_callback_passes_through_result_unchanged(): void {
		$ctx     = new Request_Context();
		$request = new \WP_REST_Request( 'GET', '/wp/v2/posts' );
		$request->set_header( 'X-Request-ID', 'pass-through' );

		$sentinel = new \stdClass();
		$server   = new \WP_REST_Server();

		$out = $ctx->on_rest_pre_dispatch( $sentinel, $server, $request );

		$this->assertSame( $sentinel, $out );
		$this->assertSame( 'pass-through', $ctx->get_correlation_id() );
	}
}
