<?php

declare(strict_types=1);

namespace Tests\Framework\Http\Parser;

use App\Framework\Cache\Compression\NullCompression;
use App\Framework\Cache\CompressionCacheDecorator;
use App\Framework\Cache\Driver\InMemoryCache;
use App\Framework\Cache\GeneralCache;
use App\Framework\Cache\Serializer\PhpSerializer;
use App\Framework\Http\Parser\Exception\ParserSecurityException;
use App\Framework\Http\Parser\HttpRequestParser;
use App\Framework\Http\Parser\ParserCache;
use App\Framework\Http\Parser\ParserConfig;
use PHPUnit\Framework\TestCase;

/**
 * Security tests for HttpRequestParser
 * Tests body size limits, URI length limits, and integration security
 */
final class HttpRequestParserSecurityTest extends TestCase
{
    private HttpRequestParser $parser;

    private ParserConfig $strictConfig;

    private ParserConfig $webConfig;

    protected function setUp(): void
    {
        // Strict security config for testing
        $this->strictConfig = new ParserConfig(
            maxTotalUploadSize: \App\Framework\Core\ValueObjects\Byte::fromBytes(1024), // 1KB limit
            maxFileSize: \App\Framework\Core\ValueObjects\Byte::fromBytes(500),
            maxFormDataSize: \App\Framework\Core\ValueObjects\Byte::fromBytes(500),
            maxQueryStringLength: 100,
            validateFileExtensions: true,
            scanForMaliciousContent: true,
            throwOnLimitExceeded: true,
            logSecurityViolations: false // Don't log during tests
        );

        // Web-friendly config
        $this->webConfig = new ParserConfig(
            maxTotalUploadSize: \App\Framework\Core\ValueObjects\Byte::fromMegabytes(10),
            maxFileSize: \App\Framework\Core\ValueObjects\Byte::fromMegabytes(5),
            maxFormDataSize: \App\Framework\Core\ValueObjects\Byte::fromMegabytes(5),
            maxQueryStringLength: 8192,
            validateFileExtensions: false,
            scanForMaliciousContent: false,
            throwOnLimitExceeded: true,
            logSecurityViolations: false
        );

        // Create parser cache with proper serialization
        $baseCache = new GeneralCache(new InMemoryCache(), new \App\Framework\Serializer\Php\PhpSerializer());
        $compressionCache = new CompressionCacheDecorator(
            $baseCache,
            new NullCompression(),
            new PhpSerializer()
        );
        $cache = new ParserCache($compressionCache);

        $this->parser = new HttpRequestParser($cache, $this->strictConfig);
    }

    public function testRequestBodySizeExceeded(): void
    {
        // Create a body larger than the 1KB limit
        $largeBody = str_repeat('a', 2048);

        $this->expectException(ParserSecurityException::class);
        $this->expectExceptionMessage('Request body size exceeded: 2048 bytes > 1024 bytes maximum');

        $this->parser->parseRequest('POST', '/test', [], $largeBody);
    }

    public function testRequestBodySizeWithinLimits(): void
    {
        // Create a body smaller than the 1KB limit
        $smallBody = str_repeat('a', 500);

        $request = $this->parser->parseRequest('POST', '/test', ['HTTP_CONTENT_TYPE' => 'text/plain'], $smallBody);

        $this->assertEquals('/test', $request->path);
        $this->assertEquals($smallBody, $request->body);
    }

    public function testUriTooLong(): void
    {
        // Create a URI longer than 4KB limit
        $longUri = '/test?' . str_repeat('param=value&', 500); // ~5KB

        $this->expectException(ParserSecurityException::class);
        $this->expectExceptionMessage('URI too long:');

        $this->parser->parseRequest('GET', $longUri, [], '');
    }

    public function testUriWithinLimits(): void
    {
        // Normal length URI
        $normalUri = '/test?param1=value1&param2=value2';

        $request = $this->parser->parseRequest('GET', $normalUri, [], '');

        $this->assertEquals('/test', $request->path);
        $this->assertEquals(['param1' => 'value1', 'param2' => 'value2'], $request->queryParams);
    }

    public function testMultipartFormDataWithSizeLimit(): void
    {
        $boundary = 'test-boundary-123';
        $contentType = "multipart/form-data; boundary={$boundary}";

        // Create multipart data that exceeds 1KB limit
        $multipartData = "--{$boundary}\r\n" .
            "Content-Disposition: form-data; name=\"field1\"\r\n\r\n" .
            str_repeat('large_value_', 200) . "\r\n" . // ~2.4KB of data
            "--{$boundary}--\r\n";

        $this->expectException(ParserSecurityException::class);
        $this->expectExceptionMessage('Request body size exceeded');

        $this->parser->parseRequest('POST', '/upload', [
            'HTTP_CONTENT_TYPE' => $contentType,
        ], $multipartData);
    }

    public function testFormDataWithSizeLimit(): void
    {
        $contentType = 'application/x-www-form-urlencoded';

        // Create form data that exceeds 1KB limit
        $formData = 'field1=' . str_repeat('value_', 300); // ~1.8KB

        $this->expectException(ParserSecurityException::class);
        $this->expectExceptionMessage('Request body size exceeded');

        $this->parser->parseRequest('POST', '/form', [
            'HTTP_CONTENT_TYPE' => $contentType,
        ], $formData);
    }

    public function testWebConfigAllowsLargerRequests(): void
    {
        // Create parser cache for web test
        $baseCache = new GeneralCache(new InMemoryCache(), new \App\Framework\Serializer\Php\PhpSerializer());
        $compressionCache = new CompressionCacheDecorator(
            $baseCache,
            new NullCompression(),
            new PhpSerializer()
        );
        $cache = new ParserCache($compressionCache);

        $webParser = new HttpRequestParser($cache, $this->webConfig);

        // Create a 2KB body (within web config's 10MB limit)
        $body = str_repeat('a', 2048);

        $request = $webParser->parseRequest('POST', '/test', [
            'HTTP_CONTENT_TYPE' => 'text/plain',
        ], $body);

        $this->assertEquals('/test', $request->path);
        $this->assertEquals($body, $request->body);
    }

    public function testEmptyBodyIsAllowed(): void
    {
        $request = $this->parser->parseRequest('GET', '/test', [], '');

        $this->assertEquals('/test', $request->path);
        $this->assertEquals('', $request->body);
    }

    public function testSecurityIntegrationWithAllParsers(): void
    {
        // Test that security limits work across all sub-parsers
        $boundary = 'security-test-boundary';
        $contentType = "multipart/form-data; boundary={$boundary}";

        // Create valid multipart data within limits
        $validData = "--{$boundary}\r\n" .
            "Content-Disposition: form-data; name=\"message\"\r\n\r\n" .
            "Hello World\r\n" .
            "--{$boundary}\r\n" .
            "Content-Disposition: form-data; name=\"file\"; filename=\"test.txt\"\r\n" .
            "Content-Type: text/plain\r\n\r\n" .
            "File content\r\n" .
            "--{$boundary}--\r\n";

        $request = $this->parser->parseRequest('POST', '/upload?param=value', [
            'HTTP_CONTENT_TYPE' => $contentType,
            'HTTP_COOKIE' => 'session=abc123',
        ], $validData);

        $this->assertEquals('/upload', $request->path);
        $this->assertEquals(['param' => 'value'], $request->queryParams);
        $this->assertCount(1, $request->files->all());
        $this->assertEquals('abc123', $request->cookies->get('session')->value);
    }

    public function testParseFromGlobalsWithSecurityLimits(): void
    {
        // Test that parseFromGlobals also applies security limits
        $largeBody = str_repeat('x', 2048);

        $server = [
            'REQUEST_METHOD' => 'POST',
            'REQUEST_URI' => '/test',
            'HTTP_CONTENT_TYPE' => 'text/plain',
        ];

        $this->expectException(ParserSecurityException::class);
        $this->expectExceptionMessage('Request body size exceeded');

        $this->parser->parseFromGlobals($server, $largeBody);
    }

    public function testRawHttpRequestWithSecurityLimits(): void
    {
        // Test that parseRawHttpRequest also applies security limits
        $largeBody = str_repeat('y', 2048);

        $rawRequest = "POST /test HTTP/1.1\r\n" .
            "Content-Type: text/plain\r\n" .
            "Content-Length: " . strlen($largeBody) . "\r\n" .
            "\r\n" .
            $largeBody;

        $this->expectException(ParserSecurityException::class);
        $this->expectExceptionMessage('Request body size exceeded');

        $this->parser->parseRawHttpRequest($rawRequest);
    }

    public function testMethodOverrideWithSecurityLimits(): void
    {
        // Test that method override doesn't bypass security
        $largeData = '_method=PUT&data=' . str_repeat('value_', 300); // ~1.8KB

        $this->expectException(ParserSecurityException::class);
        $this->expectExceptionMessage('Request body size exceeded');

        $this->parser->parseRequest('POST', '/test', [
            'HTTP_CONTENT_TYPE' => 'application/x-www-form-urlencoded',
        ], $largeData);
    }
}
