<?php

declare(strict_types=1);

use App\Framework\Core\ValueObjects\FileSize;
use App\Framework\Filesystem\FileStorage;
use App\Framework\Filesystem\FileValidator;
use App\Framework\Filesystem\Exceptions\FileValidationException;
use App\Framework\Filesystem\Exceptions\FileNotFoundException;

describe('FileStorage Integration', function () {
    beforeEach(function () {
        // Create temp directory for tests
        $this->testDir = sys_get_temp_dir() . '/filesystem_test_' . uniqid();
        mkdir($this->testDir, 0777, true);
    });

    afterEach(function () {
        // Clean up temp directory
        if (is_dir($this->testDir)) {
            $files = glob($this->testDir . '/*');
            foreach ($files as $file) {
                if (is_file($file)) {
                    unlink($file);
                }
            }
            rmdir($this->testDir);
        }
    });

    // Storage without Validator or Logger
    it('works without validator or logger', function () {
        $storage = new FileStorage($this->testDir);

        $storage->put('test.txt', 'Hello World');
        $content = $storage->get('test.txt');

        expect($content)->toBe('Hello World');
        expect($storage->exists('test.txt'))->toBeTrue();
    });

    // Storage with FileValidator integration
    it('validates path traversal attempts with validator', function () {
        $validator = FileValidator::createDefault();
        $storage = new FileStorage($this->testDir, validator: $validator);

        try {
            $storage->get('../../../etc/passwd');
            expect(true)->toBeFalse('Should have thrown exception');
        } catch (FileValidationException $e) {
            expect($e->getMessage())->toContain('Path traversal');
        }
    });

    it('validates file extensions with validator on write', function () {
        $validator = FileValidator::createStrict(['txt', 'json']);
        $storage = new FileStorage($this->testDir, validator: $validator);

        // Allowed extension should work
        $storage->put('allowed.txt', 'content');
        expect($storage->exists('allowed.txt'))->toBeTrue();

        // Disallowed extension should fail
        try {
            $storage->put('blocked.exe', 'malicious');
            expect(true)->toBeFalse('Should have thrown exception');
        } catch (FileValidationException $e) {
            expect($e->getMessage())->toContain('not allowed');
        }
    });

    it('validates file size with validator on write', function () {
        $validator = FileValidator::forUploads(FileSize::fromKilobytes(1)); // 1KB limit
        $storage = new FileStorage($this->testDir, validator: $validator);

        // Small file should work
        $storage->put('small.txt', 'Small content');
        expect($storage->exists('small.txt'))->toBeTrue();

        // Large file should fail
        $largeContent = str_repeat('X', 2048); // 2KB
        try {
            $storage->put('large.txt', $largeContent);
            expect(true)->toBeFalse('Should have thrown exception');
        } catch (FileValidationException $e) {
            expect($e->getMessage())->toContain('exceeds maximum');
        }
    });

    // Note: validateRead() does not validate extensions by design
    // Extensions are validated on write operations only
    // This is the correct behavior - when reading files, extension doesn't matter

    // Edge cases
    it('handles nonexistent files with validator', function () {
        $validator = FileValidator::createDefault();
        $storage = new FileStorage($this->testDir, validator: $validator);

        try {
            $storage->get('nonexistent.txt');
            expect(true)->toBeFalse('Should have thrown exception');
        } catch (FileNotFoundException $e) {
            expect($e)->toBeInstanceOf(FileNotFoundException::class);
        }
    });

    it('validates before attempting file operations', function () {
        $validator = FileValidator::forUploads();
        $storage = new FileStorage($this->testDir, validator: $validator);

        // Path traversal should fail validation before attempting filesystem operation
        try {
            $storage->put('../../../tmp/malicious.txt', 'content');
            expect(true)->toBeFalse('Should have thrown exception');
        } catch (FileValidationException $e) {
            expect($e->getMessage())->toContain('Path traversal');
        }

        // Verify file was NOT created (path traversal prevented)
        expect(file_exists($this->testDir . '/../../../tmp/malicious.txt'))->toBeFalse();
    });

    // Performance: Large file handling
    it('handles large files efficiently', function () {
        $validator = FileValidator::forUploads(FileSize::fromMegabytes(10));
        $storage = new FileStorage($this->testDir, validator: $validator);

        // Create 1MB file
        $largeContent = str_repeat('X', 1024 * 1024);
        $storage->put('large.txt', $largeContent);

        $content = $storage->get('large.txt');
        expect(strlen($content))->toBe(1024 * 1024);
    });

    // Multiple operations workflow
    it('handles complete file lifecycle with validation', function () {
        $validator = FileValidator::createStrict(['txt', 'json']);
        $storage = new FileStorage($this->testDir, validator: $validator);

        // Create
        $storage->put('lifecycle.txt', 'initial content');
        expect($storage->exists('lifecycle.txt'))->toBeTrue();

        // Read
        $content = $storage->get('lifecycle.txt');
        expect($content)->toBe('initial content');

        // Update
        $storage->put('lifecycle.txt', 'updated content');
        $updatedContent = $storage->get('lifecycle.txt');
        expect($updatedContent)->toBe('updated content');

        // Copy
        $storage->copy('lifecycle.txt', 'lifecycle-copy.txt');
        expect($storage->exists('lifecycle-copy.txt'))->toBeTrue();

        // Delete
        $storage->delete('lifecycle.txt');
        expect($storage->exists('lifecycle.txt'))->toBeFalse();

        // Copy still exists
        expect($storage->exists('lifecycle-copy.txt'))->toBeTrue();
    });

    // Validator prevents invalid operations
    it('prevents path traversal on read operations', function () {
        $validator = FileValidator::createDefault();
        $storage = new FileStorage($this->testDir, validator: $validator);

        try {
            $storage->get('../../sensitive/file.txt');
            expect(true)->toBeFalse('Should have thrown exception');
        } catch (FileValidationException $e) {
            expect($e->getMessage())->toContain('Path traversal');
        }
    });

    it('prevents path traversal on copy operations', function () {
        $validator = FileValidator::createDefault();
        $storage = new FileStorage($this->testDir, validator: $validator);

        $storage->put('source.txt', 'content');

        try {
            $storage->copy('source.txt', '../../../etc/target.txt');
            expect(true)->toBeFalse('Should have thrown exception');
        } catch (FileValidationException $e) {
            expect($e->getMessage())->toContain('Path traversal');
        }
    });

    it('prevents path traversal on delete operations', function () {
        $validator = FileValidator::createDefault();
        $storage = new FileStorage($this->testDir, validator: $validator);

        try {
            $storage->delete('../../../important/file.txt');
            expect(true)->toBeFalse('Should have thrown exception');
        } catch (FileValidationException $e) {
            expect($e->getMessage())->toContain('Path traversal');
        }
    });

    // URL-encoded path traversal prevention
    it('prevents URL-encoded path traversal attacks', function () {
        $validator = FileValidator::createDefault();
        $storage = new FileStorage($this->testDir, validator: $validator);

        try {
            $storage->put('%2e%2e/%2e%2e/malicious.txt', 'content');
            expect(true)->toBeFalse('Should have thrown exception');
        } catch (FileValidationException $e) {
            expect($e->getMessage())->toContain('Path traversal');
        }
    });

    // Multiple validator configurations
    it('enforces strict whitelist validation', function () {
        $validator = FileValidator::createStrict(['txt']);
        $storage = new FileStorage($this->testDir, validator: $validator);

        // Only .txt should work
        $storage->put('allowed.txt', 'content');
        expect($storage->exists('allowed.txt'))->toBeTrue();

        // Everything else should fail
        $blocked = ['file.json', 'file.pdf', 'file.exe', 'file.jpg'];

        foreach ($blocked as $filename) {
            try {
                $storage->put($filename, 'content');
                expect(true)->toBeFalse("Should have blocked {$filename}");
            } catch (FileValidationException $e) {
                expect($e->getMessage())->toContain('not allowed');
            }
        }
    });

    // Image validator integration
    it('works with image validator', function () {
        $validator = FileValidator::forImages();
        $storage = new FileStorage($this->testDir, validator: $validator);

        // Image extensions should work
        $allowedImages = ['photo.jpg', 'image.png', 'graphic.gif', 'icon.webp'];

        foreach ($allowedImages as $filename) {
            $storage->put($filename, 'fake image data');
            expect($storage->exists($filename))->toBeTrue();
        }

        // Non-image should fail
        try {
            $storage->put('document.pdf', 'content');
            expect(true)->toBeFalse('Should have thrown exception');
        } catch (FileValidationException $e) {
            expect($e->getMessage())->toContain('not allowed');
        }
    });

    // Upload validator integration
    it('works with upload validator', function () {
        $validator = FileValidator::forUploads();
        $storage = new FileStorage($this->testDir, validator: $validator);

        // Common upload types should work
        $allowedFiles = ['data.json', 'report.pdf', 'document.txt', 'data.csv'];

        foreach ($allowedFiles as $filename) {
            $storage->put($filename, 'content');
            expect($storage->exists($filename))->toBeTrue();
        }

        // Executable should fail
        try {
            $storage->put('malware.exe', 'malicious');
            expect(true)->toBeFalse('Should have thrown exception');
        } catch (FileValidationException $e) {
            expect($e->getMessage())->toContain('not allowed');
        }
    });
});
