<?php

declare(strict_types=1);

namespace Tests\Framework\Http\Parser;

use App\Framework\Core\ValueObjects\Byte;
use App\Framework\Http\Parser\Exception\ParserSecurityException;
use App\Framework\Http\Parser\FileUploadParser;
use App\Framework\Http\Parser\ParserConfig;
use App\Framework\Http\UploadError;
use PHPUnit\Framework\TestCase;

final class FileUploadParserTest extends TestCase
{
    private FileUploadParser $parser;

    protected function setUp(): void
    {
        $this->parser = new FileUploadParser();
    }

    protected function tearDown(): void
    {
        // Clean up any temporary files created during tests
        $tempDir = sys_get_temp_dir();
        $files = glob($tempDir . '/upload_*');
        foreach ($files as $file) {
            if (is_file($file)) {
                unlink($file);
            }
        }
    }

    public function testParseMultipartSingleFile(): void
    {
        $boundary = '----FormBoundary123';
        $body = "------FormBoundary123\r\n" .
                "Content-Disposition: form-data; name=\"upload\"; filename=\"test.txt\"\r\n" .
                "Content-Type: text/plain\r\n" .
                "\r\n" .
                "Hello World!\r\n" .
                "------FormBoundary123--\r\n";

        $result = $this->parser->parseMultipart($body, $boundary);

        $this->assertCount(1, $result->all());

        $file = $result->get('upload');
        $this->assertNotNull($file);
        $this->assertSame('test.txt', $file->name);
        $this->assertSame('text/plain', $file->type);
        $this->assertSame(12, $file->size); // "Hello World!" length
        $this->assertSame(UploadError::OK, $file->error);
        $this->assertTrue(file_exists($file->tmpName));
        $this->assertSame('Hello World!', file_get_contents($file->tmpName));
    }

    public function testParseMultipartMultipleFiles(): void
    {
        $boundary = '----FormBoundary123';
        $body = "------FormBoundary123\r\n" .
                "Content-Disposition: form-data; name=\"file1\"; filename=\"test1.txt\"\r\n" .
                "Content-Type: text/plain\r\n" .
                "\r\n" .
                "Content 1\r\n" .
                "------FormBoundary123\r\n" .
                "Content-Disposition: form-data; name=\"file2\"; filename=\"test2.txt\"\r\n" .
                "Content-Type: text/plain\r\n" .
                "\r\n" .
                "Content 2\r\n" .
                "------FormBoundary123--\r\n";

        $result = $this->parser->parseMultipart($body, $boundary);

        $this->assertCount(2, $result->all());

        $file1 = $result->get('file1');
        $file2 = $result->get('file2');

        $this->assertNotNull($file1);
        $this->assertNotNull($file2);

        $this->assertSame('test1.txt', $file1->name);
        $this->assertSame('test2.txt', $file2->name);
        $this->assertSame('Content 1', file_get_contents($file1->tmpName));
        $this->assertSame('Content 2', file_get_contents($file2->tmpName));
    }

    public function testParseMultipartFileArray(): void
    {
        $boundary = '----FormBoundary123';
        $body = "------FormBoundary123\r\n" .
                "Content-Disposition: form-data; name=\"files[]\"; filename=\"file1.txt\"\r\n" .
                "Content-Type: text/plain\r\n" .
                "\r\n" .
                "File 1\r\n" .
                "------FormBoundary123\r\n" .
                "Content-Disposition: form-data; name=\"files[]\"; filename=\"file2.txt\"\r\n" .
                "Content-Type: text/plain\r\n" .
                "\r\n" .
                "File 2\r\n" .
                "------FormBoundary123--\r\n";

        $result = $this->parser->parseMultipart($body, $boundary);

        $files = $result->get('files');
        $this->assertIsArray($files);
        $this->assertCount(2, $files);

        $this->assertSame('file1.txt', $files[0]->name);
        $this->assertSame('file2.txt', $files[1]->name);
        $this->assertSame('File 1', file_get_contents($files[0]->tmpName));
        $this->assertSame('File 2', file_get_contents($files[1]->tmpName));
    }

    public function testParseMultipartNestedFileArray(): void
    {
        $boundary = '----FormBoundary123';
        $body = "------FormBoundary123\r\n" .
                "Content-Disposition: form-data; name=\"docs[legal][]\"; filename=\"contract.pdf\"\r\n" .
                "Content-Type: application/pdf\r\n" .
                "\r\n" .
                "%PDF-1.4 PDF content here\r\n" . // Add PDF signature to match MIME type
                "------FormBoundary123\r\n" .
                "Content-Disposition: form-data; name=\"docs[images][0]\"; filename=\"logo.png\"\r\n" .
                "Content-Type: image/png\r\n" .
                "\r\n" .
                "\x89\x50\x4E\x47\x0D\x0A\x1A\x0A" . "PNG content here\r\n" . // Add PNG signature
                "------FormBoundary123--\r\n";

        $result = $this->parser->parseMultipart($body, $boundary);

        $docs = $result->get('docs');
        $this->assertIsArray($docs);
        $this->assertArrayHasKey('legal', $docs);
        $this->assertArrayHasKey('images', $docs);

        $legalFiles = $docs['legal'];
        $this->assertIsArray($legalFiles);
        $this->assertCount(1, $legalFiles);
        $this->assertSame('contract.pdf', $legalFiles[0]->name);
        $this->assertSame('application/pdf', $legalFiles[0]->type);

        $imageFiles = $docs['images'];
        $this->assertIsArray($imageFiles);
        $this->assertSame('logo.png', $imageFiles['0']->name);
        $this->assertSame('image/png', $imageFiles['0']->type);
    }

    public function testParseMultipartWithoutFilename(): void
    {
        $boundary = '----FormBoundary123';
        $body = "------FormBoundary123\r\n" .
                "Content-Disposition: form-data; name=\"data\"\r\n" .
                "Content-Type: text/plain\r\n" .
                "\r\n" .
                "Just regular form data\r\n" .
                "------FormBoundary123--\r\n";

        $result = $this->parser->parseMultipart($body, $boundary);

        // Should not create any files for parts without filename
        $this->assertCount(0, $result->all());
    }

    public function testParseMultipartDefaultContentType(): void
    {
        // Use a parser with relaxed security for this test
        $config = new ParserConfig(validateFileExtensions: false, scanForMaliciousContent: false);
        $parser = new FileUploadParser($config);

        $boundary = '----FormBoundary123';
        $body = "------FormBoundary123\r\n" .
                "Content-Disposition: form-data; name=\"upload\"; filename=\"binary.dat\"\r\n" .
                "\r\n" .
                "Binary data here\r\n" .
                "------FormBoundary123--\r\n";

        $result = $parser->parseMultipart($body, $boundary);

        $file = $result->get('upload');
        $this->assertNotNull($file);
        $this->assertSame('application/octet-stream', $file->type);
    }

    public function testParseMultipartRfc2231ExtendedFilename(): void
    {
        $boundary = '----FormBoundary123';
        $body = "------FormBoundary123\r\n" .
                "Content-Disposition: form-data; name=\"upload\"; filename*=UTF-8''caf%C3%A9.txt\r\n" .
                "Content-Type: text/plain\r\n" .
                "\r\n" .
                "Content\r\n" .
                "------FormBoundary123--\r\n";

        $result = $this->parser->parseMultipart($body, $boundary);

        $file = $result->get('upload');
        $this->assertNotNull($file);
        $this->assertSame('café.txt', $file->name); // Should be properly decoded
    }

    public function testParseMultipartRfc2231InvalidFormat(): void
    {
        $boundary = '----FormBoundary123';
        $body = "------FormBoundary123\r\n" .
                "Content-Disposition: form-data; name=\"upload\"; filename*=invalid_format\r\n" .
                "Content-Type: text/plain\r\n" .
                "\r\n" .
                "Content\r\n" .
                "------FormBoundary123--\r\n";

        $result = $this->parser->parseMultipart($body, $boundary);

        $file = $result->get('upload');
        $this->assertNotNull($file);
        $this->assertSame('invalid_format', $file->name); // Should return as-is for invalid format
    }

    public function testParseMultipartEmptyFile(): void
    {
        $boundary = '----FormBoundary123';
        $body = "------FormBoundary123\r\n" .
                "Content-Disposition: form-data; name=\"upload\"; filename=\"empty.txt\"\r\n" .
                "Content-Type: text/plain\r\n" .
                "\r\n" .
                "\r\n" .
                "------FormBoundary123--\r\n";

        $result = $this->parser->parseMultipart($body, $boundary);

        $file = $result->get('upload');
        $this->assertNotNull($file);
        $this->assertSame('empty.txt', $file->name);
        $this->assertSame(0, $file->size);
        $this->assertSame('', file_get_contents($file->tmpName));
    }

    public function testParseMultipartMalformedParts(): void
    {
        $boundary = '----FormBoundary123';
        $body = "------FormBoundary123\r\n" .
                "Malformed part without proper headers\r\n" .
                "------FormBoundary123\r\n" .
                "Content-Disposition: form-data; name=\"valid\"; filename=\"test.txt\"\r\n" .
                "\r\n" .
                "Valid content\r\n" .
                "------FormBoundary123--\r\n";

        $result = $this->parser->parseMultipart($body, $boundary);

        // Should ignore malformed parts and process valid ones
        $this->assertCount(1, $result->all());
        $file = $result->get('valid');
        $this->assertNotNull($file);
        $this->assertSame('test.txt', $file->name);
    }

    public function testParseMultipartMissingName(): void
    {
        $boundary = '----FormBoundary123';
        $body = "------FormBoundary123\r\n" .
                "Content-Disposition: form-data; filename=\"test.txt\"\r\n" .
                "Content-Type: text/plain\r\n" .
                "\r\n" .
                "Content\r\n" .
                "------FormBoundary123--\r\n";

        $result = $this->parser->parseMultipart($body, $boundary);

        // Should ignore parts without name attribute
        $this->assertCount(0, $result->all());
    }

    public function testParseMultipartQuotedValues(): void
    {
        $boundary = '----FormBoundary123';
        $body = "------FormBoundary123\r\n" .
                "Content-Disposition: form-data; name=\"upload\"; filename=\"my file.txt\"\r\n" .
                "Content-Type: text/plain\r\n" .
                "\r\n" .
                "Content with spaces in filename\r\n" .
                "------FormBoundary123--\r\n";

        $result = $this->parser->parseMultipart($body, $boundary);

        $file = $result->get('upload');
        $this->assertNotNull($file);
        $this->assertSame('my file.txt', $file->name);
    }

    public function testParseMultipartLargeFile(): void
    {
        $boundary = '----FormBoundary123';
        $largeContent = str_repeat('A', 10000); // 10KB of 'A's
        $body = "------FormBoundary123\r\n" .
                "Content-Disposition: form-data; name=\"large\"; filename=\"large.txt\"\r\n" .
                "Content-Type: text/plain\r\n" .
                "\r\n" .
                $largeContent . "\r\n" .
                "------FormBoundary123--\r\n";

        $result = $this->parser->parseMultipart($body, $boundary);

        $file = $result->get('large');
        $this->assertNotNull($file);
        $this->assertSame(10000, $file->size);
        $this->assertSame($largeContent, file_get_contents($file->tmpName));
    }

    public function testTemporaryFileCleanup(): void
    {
        $boundary = '----FormBoundary123';
        $body = "------FormBoundary123\r\n" .
                "Content-Disposition: form-data; name=\"upload\"; filename=\"test.txt\"\r\n" .
                "Content-Type: text/plain\r\n" .
                "\r\n" .
                "Test content\r\n" .
                "------FormBoundary123--\r\n";

        $result = $this->parser->parseMultipart($body, $boundary);
        $file = $result->get('upload');
        $tmpPath = $file->tmpName;

        $this->assertTrue(file_exists($tmpPath));

        // Simulate script end - the shutdown function should clean up
        // We can't easily test this automatically, but the file path is registered
        // for cleanup in the shutdown function
        $this->assertStringStartsWith(sys_get_temp_dir() . '/upload_', $tmpPath);
    }

    // Security Tests

    public function testBoundaryTooLong(): void
    {
        $config = new ParserConfig(maxBoundaryLength: 10);
        $parser = new FileUploadParser($config);

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

        $longBoundary = str_repeat('a', 15);
        $body = "--{$longBoundary}\r\nContent-Disposition: form-data; name=\"test\"; filename=\"test.txt\"\r\n\r\nvalue\r\n--{$longBoundary}--\r\n";
        $parser->parseMultipart($body, $longBoundary);
    }

    public function testFileCountExceeded(): void
    {
        $config = new ParserConfig(maxFileCount: 2);
        $parser = new FileUploadParser($config);

        $this->expectException(ParserSecurityException::class);
        $this->expectExceptionMessage('File count exceeded');

        $boundary = '----FormBoundary123';
        $body = "------FormBoundary123\r\n" .
                "Content-Disposition: form-data; name=\"file1\"; filename=\"test1.txt\"\r\n\r\nContent 1\r\n" .
                "------FormBoundary123\r\n" .
                "Content-Disposition: form-data; name=\"file2\"; filename=\"test2.txt\"\r\n\r\nContent 2\r\n" .
                "------FormBoundary123\r\n" .
                "Content-Disposition: form-data; name=\"file3\"; filename=\"test3.txt\"\r\n\r\nContent 3\r\n" .
                "------FormBoundary123--\r\n";

        $parser->parseMultipart($body, $boundary);
    }

    public function testFileSizeExceeded(): void
    {
        $config = new ParserConfig(maxFileSize: new Byte(50));
        $parser = new FileUploadParser($config);

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

        $boundary = '----FormBoundary123';
        $largeContent = str_repeat('a', 100); // Exceeds 50 byte limit
        $body = "------FormBoundary123\r\n" .
                "Content-Disposition: form-data; name=\"upload\"; filename=\"large.txt\"\r\n\r\n" .
                "{$largeContent}\r\n" .
                "------FormBoundary123--\r\n";

        $parser->parseMultipart($body, $boundary);
    }

    public function testTotalUploadSizeExceeded(): void
    {
        $config = new ParserConfig(maxTotalUploadSize: new Byte(100));
        $parser = new FileUploadParser($config);

        $this->expectException(ParserSecurityException::class);
        $this->expectExceptionMessage('Total upload size exceeded');

        $boundary = '----FormBoundary123';
        $content1 = str_repeat('a', 60); // 60 bytes
        $content2 = str_repeat('b', 50); // 50 bytes - total 110 bytes > 100 limit
        $body = "------FormBoundary123\r\n" .
                "Content-Disposition: form-data; name=\"file1\"; filename=\"file1.txt\"\r\n\r\n" .
                "{$content1}\r\n" .
                "------FormBoundary123\r\n" .
                "Content-Disposition: form-data; name=\"file2\"; filename=\"file2.txt\"\r\n\r\n" .
                "{$content2}\r\n" .
                "------FormBoundary123--\r\n";

        $parser->parseMultipart($body, $boundary);
    }

    public function testBlockedFileExtension(): void
    {
        $config = new ParserConfig(blockedFileExtensions: ['php', 'exe']);
        $parser = new FileUploadParser($config);

        $this->expectException(ParserSecurityException::class);
        $this->expectExceptionMessage('File extension blocked');

        $boundary = '----FormBoundary123';
        $body = "------FormBoundary123\r\n" .
                "Content-Disposition: form-data; name=\"upload\"; filename=\"malicious.php\"\r\n\r\n" .
                "<?php echo 'hack'; ?>\r\n" .
                "------FormBoundary123--\r\n";

        $parser->parseMultipart($body, $boundary);
    }

    public function testFileExtensionNotAllowed(): void
    {
        $config = new ParserConfig(allowedFileExtensions: ['txt', 'jpg']);
        $parser = new FileUploadParser($config);

        $this->expectException(ParserSecurityException::class);
        $this->expectExceptionMessage('File extension not allowed');

        $boundary = '----FormBoundary123';
        $body = "------FormBoundary123\r\n" .
                "Content-Disposition: form-data; name=\"upload\"; filename=\"document.pdf\"\r\n\r\n" .
                "PDF content\r\n" .
                "------FormBoundary123--\r\n";

        $parser->parseMultipart($body, $boundary);
    }

    public function testMaliciousExecutableContent(): void
    {
        $config = new ParserConfig(
            scanForMaliciousContent: true,
            allowedFileExtensions: ['exe', 'txt'], // Allow exe to test content validation
            blockedFileExtensions: [] // Remove blocked extensions to test content
        );
        $parser = new FileUploadParser($config);

        $this->expectException(ParserSecurityException::class);
        $this->expectExceptionMessage('Executable content detected');

        $boundary = '----FormBoundary123';
        $body = "------FormBoundary123\r\n" .
                "Content-Disposition: form-data; name=\"upload\"; filename=\"malware.exe\"\r\n\r\n" .
                "\x4D\x5A" . str_repeat("x", 100) . "\r\n" . // PE executable signature
                "------FormBoundary123--\r\n";

        $parser->parseMultipart($body, $boundary);
    }

    public function testMaliciousPhpContent(): void
    {
        $config = new ParserConfig(
            scanForMaliciousContent: true,
            allowedFileExtensions: ['txt', 'php'], // Allow txt to test content validation
            blockedFileExtensions: [] // Remove blocked extensions to test content
        );
        $parser = new FileUploadParser($config);

        $this->expectException(ParserSecurityException::class);
        $this->expectExceptionMessage('PHP code detected');

        $boundary = '----FormBoundary123';
        $body = "------FormBoundary123\r\n" .
                "Content-Disposition: form-data; name=\"upload\"; filename=\"shell.txt\"\r\n\r\n" .
                "<?php system(\$_GET['cmd']); ?>\r\n" .
                "------FormBoundary123--\r\n";

        $parser->parseMultipart($body, $boundary);
    }

    public function testMaliciousScriptContent(): void
    {
        $config = new ParserConfig(
            scanForMaliciousContent: true,
            allowedFileExtensions: ['html', 'txt'], // Allow html to test content validation
            blockedFileExtensions: [] // Remove blocked extensions to test content
        );
        $parser = new FileUploadParser($config);

        $this->expectException(ParserSecurityException::class);
        $this->expectExceptionMessage('Suspicious script content detected');

        $boundary = '----FormBoundary123';
        $body = "------FormBoundary123\r\n" .
                "Content-Disposition: form-data; name=\"upload\"; filename=\"xss.html\"\r\n\r\n" .
                "<script>alert('xss')</script>\r\n" .
                "------FormBoundary123--\r\n";

        $parser->parseMultipart($body, $boundary);
    }

    public function testMaliciousEvalContent(): void
    {
        $config = new ParserConfig(
            scanForMaliciousContent: true,
            allowedFileExtensions: ['js', 'txt'], // Allow js to test content validation
            blockedFileExtensions: [] // Remove blocked extensions to test content
        );
        $parser = new FileUploadParser($config);

        $this->expectException(ParserSecurityException::class);
        $this->expectExceptionMessage('Suspicious script content detected');

        $boundary = '----FormBoundary123';
        $body = "------FormBoundary123\r\n" .
                "Content-Disposition: form-data; name=\"upload\"; filename=\"evil.js\"\r\n\r\n" .
                "eval(atob('YWxlcnQoJ2hhY2snKQ=='))\r\n" .
                "------FormBoundary123--\r\n";

        $parser->parseMultipart($body, $boundary);
    }

    public function testMimeTypeMismatch(): void
    {
        $config = new ParserConfig(scanForMaliciousContent: true, strictMimeTypeValidation: true);
        $parser = new FileUploadParser($config);

        $this->expectException(ParserSecurityException::class);
        $this->expectExceptionMessage('MIME type mismatch');

        $boundary = '----FormBoundary123';
        $body = "------FormBoundary123\r\n" .
                "Content-Disposition: form-data; name=\"upload\"; filename=\"fake.jpg\"\r\n" .
                "Content-Type: image/jpeg\r\n\r\n" .
                "%PDF-1.4 This is actually a PDF file\r\n" . // PDF signature but claiming to be JPEG
                "------FormBoundary123--\r\n";

        $parser->parseMultipart($body, $boundary);
    }

    public function testShellScriptDetection(): void
    {
        $config = new ParserConfig(scanForMaliciousContent: true);
        $parser = new FileUploadParser($config);

        $this->expectException(ParserSecurityException::class);
        $this->expectExceptionMessage('Executable content detected');

        $boundary = '----FormBoundary123';
        $body = "------FormBoundary123\r\n" .
                "Content-Disposition: form-data; name=\"upload\"; filename=\"script.txt\"\r\n\r\n" .
                "#!/bin/bash\nrm -rf /\r\n" .
                "------FormBoundary123--\r\n";

        $parser->parseMultipart($body, $boundary);
    }

    public function testSystemCallDetection(): void
    {
        $config = new ParserConfig(scanForMaliciousContent: true);
        $parser = new FileUploadParser($config);

        $this->expectException(ParserSecurityException::class);
        $this->expectExceptionMessage('Suspicious script content detected');

        $boundary = '----FormBoundary123';
        $body = "------FormBoundary123\r\n" .
                "Content-Disposition: form-data; name=\"upload\"; filename=\"backdoor.txt\"\r\n\r\n" .
                "system('cat /etc/passwd')\r\n" .
                "------FormBoundary123--\r\n";

        $parser->parseMultipart($body, $boundary);
    }

    // Security Configuration Tests

    public function testSecurityDisabled(): void
    {
        $config = new ParserConfig(
            scanForMaliciousContent: false,
            validateFileExtensions: false,
            allowedFileExtensions: [], // Empty to allow all when validation disabled
            blockedFileExtensions: [], // Empty to not block anything when validation disabled
            maxFileCount: 1000,
            maxFileSize: new Byte(10 * 1024 * 1024),
            maxTotalUploadSize: new Byte(100 * 1024 * 1024)
        );
        $parser = new FileUploadParser($config);

        // Should not throw exception when security is disabled
        $boundary = '----FormBoundary123';
        $body = "------FormBoundary123\r\n" .
                "Content-Disposition: form-data; name=\"upload\"; filename=\"malicious.php\"\r\n\r\n" .
                "<?php system(\$_GET['cmd']); ?>\r\n" .
                "------FormBoundary123--\r\n";

        $result = $parser->parseMultipart($body, $boundary);
        $file = $result->get('upload');
        $this->assertNotNull($file);
        $this->assertSame('malicious.php', $file->name);
    }

    public function testWithinSecurityLimits(): void
    {
        $config = new ParserConfig(
            maxFileCount: 5,
            maxFileSize: new Byte(1024),
            maxTotalUploadSize: new Byte(5 * 1024),
            allowedFileExtensions: ['txt', 'jpg', 'png'],
            scanForMaliciousContent: true
        );
        $parser = new FileUploadParser($config);

        // Should work fine within limits
        $boundary = '----FormBoundary123';
        $body = "------FormBoundary123\r\n" .
                "Content-Disposition: form-data; name=\"file1\"; filename=\"test1.txt\"\r\n\r\n" .
                "Safe content 1\r\n" .
                "------FormBoundary123\r\n" .
                "Content-Disposition: form-data; name=\"file2\"; filename=\"image.jpg\"\r\n\r\n" .
                "\xFF\xD8\xFF" . str_repeat('x', 10) . "\r\n" . // Valid JPEG signature
                "------FormBoundary123--\r\n";

        $result = $parser->parseMultipart($body, $boundary);
        $this->assertCount(2, $result->all());

        $file1 = $result->get('file1');
        $file2 = $result->get('file2');
        $this->assertNotNull($file1);
        $this->assertNotNull($file2);
        $this->assertSame('test1.txt', $file1->name);
        $this->assertSame('image.jpg', $file2->name);
    }

    public function testAllowedExtensionsEmptyList(): void
    {
        $config = new ParserConfig(
            allowedFileExtensions: [], // Empty list should allow all (except blocked)
            blockedFileExtensions: ['exe'],
            scanForMaliciousContent: false
        );
        $parser = new FileUploadParser($config);

        // Should allow PDF when allowed list is empty
        $boundary = '----FormBoundary123';
        $body = "------FormBoundary123\r\n" .
                "Content-Disposition: form-data; name=\"upload\"; filename=\"document.pdf\"\r\n\r\n" .
                "PDF content\r\n" .
                "------FormBoundary123--\r\n";

        $result = $parser->parseMultipart($body, $boundary);
        $file = $result->get('upload');
        $this->assertNotNull($file);
        $this->assertSame('document.pdf', $file->name);
    }

    public function testCompatibleMimeTypes(): void
    {
        $config = new ParserConfig(scanForMaliciousContent: true, strictMimeTypeValidation: true);
        $parser = new FileUploadParser($config);

        // Should allow compatible MIME types (image/jpg vs image/jpeg)
        $boundary = '----FormBoundary123';
        $body = "------FormBoundary123\r\n" .
                "Content-Disposition: form-data; name=\"upload\"; filename=\"photo.jpg\"\r\n" .
                "Content-Type: image/jpg\r\n\r\n" .
                "\xFF\xD8\xFF" . str_repeat('x', 10) . "\r\n" . // Valid JPEG signature
                "------FormBoundary123--\r\n";

        $result = $parser->parseMultipart($body, $boundary);
        $file = $result->get('upload');
        $this->assertNotNull($file);
        $this->assertSame('photo.jpg', $file->name);
    }

    public function testNoExtensionFile(): void
    {
        $config = new ParserConfig(validateFileExtensions: true, allowedFileExtensions: ['txt']);
        $parser = new FileUploadParser($config);

        // Should allow files without extension (no validation performed)
        $boundary = '----FormBoundary123';
        $body = "------FormBoundary123\r\n" .
                "Content-Disposition: form-data; name=\"upload\"; filename=\"README\"\r\n\r\n" .
                "This is a README file\r\n" .
                "------FormBoundary123--\r\n";

        $result = $parser->parseMultipart($body, $boundary);
        $file = $result->get('upload');
        $this->assertNotNull($file);
        $this->assertSame('README', $file->name);
    }

    public function testEmptyFilename(): void
    {
        $config = new ParserConfig(validateFileExtensions: true, allowedFileExtensions: ['txt']);
        $parser = new FileUploadParser($config);

        // Should allow empty filename (no validation performed)
        $boundary = '----FormBoundary123';
        $body = "------FormBoundary123\r\n" .
                "Content-Disposition: form-data; name=\"upload\"; filename=\"\"\r\n\r\n" .
                "Content without filename\r\n" .
                "------FormBoundary123--\r\n";

        $result = $parser->parseMultipart($body, $boundary);
        $file = $result->get('upload');
        $this->assertNotNull($file);
        $this->assertSame('', $file->name);
    }
}
