<?php

declare(strict_types=1);

namespace Tests\Application\Security\Services;

use App\Application\Security\Events\File\SuspiciousFileUploadEvent;
use App\Application\Security\Services\FileUploadSecurityService;
use App\Framework\Core\Events\EventDispatcherInterface;
use App\Framework\Http\UploadedFile;
use App\Framework\Http\UploadError;
use Mockery;

describe('FileUploadSecurityService', function () {
    beforeEach(function () {
        $this->eventDispatcher = Mockery::mock(EventDispatcherInterface::class);
        $this->service = new FileUploadSecurityService($this->eventDispatcher);
    });

    afterEach(function () {
        Mockery::close();
    });

    it('validates a safe uploaded file', function () {
        $file = UploadedFile::createForTesting(
            name: 'test.jpg',
            type: 'image/jpeg',
            size: 1024 * 1024, // 1MB
            tmpName: '/tmp/test_upload',
            error: UploadError::OK
        );

        // Mock the file system
        file_put_contents('/tmp/test_upload', 'fake image content');

        $result = $this->service->validateUpload($file);

        expect($result)->toBeTrue();
        unlink('/tmp/test_upload');
    });

    it('rejects files with upload errors', function () {
        $file = UploadedFile::createForTesting(
            name: 'test.jpg',
            type: 'image/jpeg',
            size: 1024,
            tmpName: '/tmp/test_upload',
            error: UploadError::PARTIAL
        );

        $this->eventDispatcher
            ->shouldReceive('dispatch')
            ->once()
            ->with(Mockery::type(SuspiciousFileUploadEvent::class));

        $result = $this->service->validateUpload($file);

        expect($result)->toBeFalse();
    });

    it('rejects files that are too large', function () {
        $file = UploadedFile::createForTesting(
            name: 'large.jpg',
            type: 'image/jpeg',
            size: 20 * 1024 * 1024, // 20MB (exceeds 10MB limit)
            tmpName: '/tmp/large_upload',
            error: UploadError::OK
        );

        $this->eventDispatcher
            ->shouldReceive('dispatch')
            ->once()
            ->with(Mockery::type(SuspiciousFileUploadEvent::class));

        $result = $this->service->validateUpload($file);

        expect($result)->toBeFalse();
    });

    it('rejects files with dangerous extensions', function () {
        $file = UploadedFile::createForTesting(
            name: 'malicious.php',
            type: 'application/x-php',
            size: 1024,
            tmpName: '/tmp/malicious_upload',
            error: UploadError::OK
        );

        $this->eventDispatcher
            ->shouldReceive('dispatch')
            ->once()
            ->with(Mockery::type(SuspiciousFileUploadEvent::class));

        $result = $this->service->validateUpload($file);

        expect($result)->toBeFalse();
    });

    it('rejects files with forbidden MIME types', function () {
        $file = UploadedFile::createForTesting(
            name: 'test.txt',
            type: 'application/x-executable',
            size: 1024,
            tmpName: '/tmp/executable_upload',
            error: UploadError::OK
        );

        // Create mock file content
        file_put_contents('/tmp/executable_upload', 'fake executable');

        $this->eventDispatcher
            ->shouldReceive('dispatch')
            ->once()
            ->with(Mockery::type(SuspiciousFileUploadEvent::class));

        $result = $this->service->validateUpload($file);

        expect($result)->toBeFalse();
        unlink('/tmp/executable_upload');
    });

    it('rejects files with malware signatures', function () {
        $file = UploadedFile::createForTesting(
            name: 'suspicious.txt',
            type: 'text/plain',
            size: 1024,
            tmpName: '/tmp/suspicious_upload',
            error: UploadError::OK
        );

        // Create file with malware signature
        file_put_contents('/tmp/suspicious_upload', 'normal content <?php eval($_POST["cmd"]); ?>');

        $this->eventDispatcher
            ->shouldReceive('dispatch')
            ->once()
            ->with(Mockery::type(SuspiciousFileUploadEvent::class));

        $result = $this->service->validateUpload($file);

        expect($result)->toBeFalse();
        unlink('/tmp/suspicious_upload');
    });

    it('rejects files with double extensions', function () {
        $file = UploadedFile::createForTesting(
            name: 'image.jpg.php',
            type: 'image/jpeg',
            size: 1024,
            tmpName: '/tmp/double_ext_upload',
            error: UploadError::OK
        );

        // Create mock file
        file_put_contents('/tmp/double_ext_upload', 'fake image content');

        $this->eventDispatcher
            ->shouldReceive('dispatch')
            ->once()
            ->with(Mockery::type(SuspiciousFileUploadEvent::class));

        $result = $this->service->validateUpload($file);

        expect($result)->toBeFalse();
        unlink('/tmp/double_ext_upload');
    });

    it('accepts various safe file types', function () {
        $safeFiles = [
            ['test.jpg', 'image/jpeg'],
            ['test.png', 'image/png'],
            ['test.gif', 'image/gif'],
            ['test.webp', 'image/webp'],
            ['document.pdf', 'application/pdf'],
            ['data.csv', 'text/csv'],
        ];

        foreach ($safeFiles as [$filename, $mimeType]) {
            $file = UploadedFile::createForTesting(
                name: $filename,
                type: $mimeType,
                size: 1024,
                tmpName: '/tmp/safe_upload_' . $filename,
                error: UploadError::OK
            );

            file_put_contents('/tmp/safe_upload_' . $filename, 'safe content');

            $result = $this->service->validateUpload($file);

            expect($result)->toBeTrue("File $filename should be valid");
            unlink('/tmp/safe_upload_' . $filename);
        }
    });

    it('handles multiple malware signatures', function () {
        $malwareSignatures = [
            'eval(',
            'base64_decode(',
            'system(',
            'exec(',
            'shell_exec(',
            '<?php',
            '<%',
            '<script',
            'javascript:',
        ];

        foreach ($malwareSignatures as $signature) {
            $file = UploadedFile::createForTesting(
                name: 'test.txt',
                type: 'text/plain',
                size: 1024,
                tmpName: '/tmp/malware_test',
                error: UploadError::OK
            );

            file_put_contents('/tmp/malware_test', 'Normal content ' . $signature . ' more content');

            $this->eventDispatcher
                ->shouldReceive('dispatch')
                ->once()
                ->with(Mockery::type(SuspiciousFileUploadEvent::class));

            $result = $this->service->validateUpload($file);

            expect($result)->toBeFalse("Signature '$signature' should be detected");
            unlink('/tmp/malware_test');
        }
    });
});
