<?php

declare(strict_types=1);

use App\Framework\Core\ValueObjects\ClassName;

describe('ClassName::exists()', function () {
    it('returns false for empty strings', function () {
        // This should be caught by constructor validation, but let's test the edge case
        expect(function () {
            ClassName::create('');
        })->toThrow(InvalidArgumentException::class, 'Class name cannot be empty');
    });

    it('returns false for whitespace-only strings', function () {
        expect(function () {
            ClassName::create('   ');
        })->toThrow(InvalidArgumentException::class, 'Invalid class name:    ');

        expect(function () {
            ClassName::create("\t");
        })->toThrow(InvalidArgumentException::class, 'Invalid class name: 	');

        expect(function () {
            ClassName::create("\n");
        })->toThrow(InvalidArgumentException::class, 'Invalid class name: 
');
    });

    it('returns true for existing classes', function () {
        $className = ClassName::create('stdClass');
        expect($className->exists())->toBeTrue();

        $className = ClassName::create('Exception');
        expect($className->exists())->toBeTrue();

        $className = ClassName::create('DateTime');
        expect($className->exists())->toBeTrue();
    });

    it('returns true for existing interfaces', function () {
        $className = ClassName::create('Countable');
        expect($className->exists())->toBeTrue();

        $className = ClassName::create('Traversable');
        expect($className->exists())->toBeTrue();

        $className = ClassName::create('JsonSerializable');
        expect($className->exists())->toBeTrue();
    });

    it('returns true for existing traits', function () {
        // PHP doesn't have many built-in traits, but let's test with a custom one
        // We'll create a simple trait for testing
        eval('trait TestTrait { public function testMethod() { return "test"; } }');

        $className = ClassName::create('TestTrait');
        expect($className->exists())->toBeTrue();
    });

    it('returns false for non-existent classes', function () {
        $className = ClassName::create('NonExistentClass');
        expect($className->exists())->toBeFalse();

        $className = ClassName::create('App\\NonExistent\\Class\\Name');
        expect($className->exists())->toBeFalse();

        $className = ClassName::create('Some\\Random\\ClassName');
        expect($className->exists())->toBeFalse();
    });

    it('handles fully qualified class names correctly', function () {
        $className = ClassName::create('\\stdClass');
        expect($className->exists())->toBeTrue();

        $className = ClassName::create('\\Exception');
        expect($className->exists())->toBeTrue();

        $className = ClassName::create('\\NonExistentClass');
        expect($className->exists())->toBeFalse();
    });

    it('handles framework classes correctly', function () {
        $className = ClassName::create('App\\Framework\\Core\\ValueObjects\\ClassName');
        expect($className->exists())->toBeTrue();

        $className = ClassName::create('App\\Framework\\DI\\Container');
        expect($className->exists())->toBeTrue();

        $className = ClassName::create('App\\Framework\\NonExistent\\Class');
        expect($className->exists())->toBeFalse();
    });

    it('handles different case variations (PHP classes are case-insensitive)', function () {
        $className = ClassName::create('stdclass'); // lowercase
        expect($className->exists())->toBeTrue(); // PHP classes are case-insensitive

        $className = ClassName::create('STDCLASS'); // uppercase
        expect($className->exists())->toBeTrue(); // PHP classes are case-insensitive

        $className = ClassName::create('stdClass'); // correct case
        expect($className->exists())->toBeTrue();
    });

    it('handles invalid class name formats gracefully', function () {
        // These should be caught by constructor validation
        expect(function () {
            ClassName::create('123InvalidName');
        })->toThrow(InvalidArgumentException::class);

        expect(function () {
            ClassName::create('Invalid-Name');
        })->toThrow(InvalidArgumentException::class);

        expect(function () {
            ClassName::create('Invalid Name');
        })->toThrow(InvalidArgumentException::class);
    });

    it('performance test - does not cause memory leaks with many calls', function () {
        $memoryBefore = memory_get_usage();

        // Test with many exists() calls
        for ($i = 0; $i < 1000; $i++) {
            $className = ClassName::create('stdClass');
            $className->exists();

            $className = ClassName::create('NonExistentClass' . $i);
            $className->exists();
        }

        $memoryAfter = memory_get_usage();
        $memoryIncrease = $memoryAfter - $memoryBefore;

        // Memory increase should be reasonable (less than 1MB for 2000 calls)
        expect($memoryIncrease)->toBeLessThan(1024 * 1024);
    });

    it('works correctly with autoloader edge cases', function () {
        // Test that exists() doesn't trigger warnings or errors with edge cases
        $className = ClassName::create('App\\NonExistent\\VeryLongClassNameThatDefinitelyDoesNotExist\\WithMultipleNamespaces\\AndMore\\Classes');
        expect($className->exists())->toBeFalse();

        // Test with special characters that are valid in namespaces
        $className = ClassName::create('App\\Test\\ClassName123');
        expect($className->exists())->toBeFalse();
    });

    it('handles concurrent access correctly', function () {
        // Simulate concurrent access to exists() method
        $results = [];

        for ($i = 0; $i < 10; $i++) {
            $className = ClassName::create('stdClass');
            $results[] = $className->exists();
        }

        // All results should be true
        foreach ($results as $result) {
            expect($result)->toBeTrue();
        }

        // Test with non-existent class
        $results = [];
        for ($i = 0; $i < 10; $i++) {
            $className = ClassName::create('NonExistentClass');
            $results[] = $className->exists();
        }

        // All results should be false
        foreach ($results as $result) {
            expect($result)->toBeFalse();
        }
    });
});

describe('ClassName::exists() integration with container', function () {
    it('prevents empty string issues in DI container context', function () {
        // This test ensures that ClassName::exists() works correctly when used
        // in the context that was causing the original "Uninitialized string offset 0" issue

        // Test the exact scenario that was failing
        $testCases = [
            'stdClass',                    // Should exist
            'Exception',                   // Should exist
            'NonExistentClass',           // Should not exist
            'App\\Framework\\DI\\Container', // Should exist (interface)
            'App\\NonExistent\\Interface',   // Should not exist
        ];

        foreach ($testCases as $testClass) {
            $className = ClassName::create($testClass);
            $result = $className->exists();

            // The important thing is that no warnings or errors are generated
            expect($result)->toBeIn([true, false]);
        }
    });

    it('handles the AtomicStorage interface case that was failing', function () {
        // Test the specific case that was causing issues
        $className = ClassName::create('App\\Framework\\Filesystem\\AtomicStorage');
        expect($className->exists())->toBeTrue(); // Interface should exist

        $className = ClassName::create('App\\Framework\\Filesystem\\Storage');
        expect($className->exists())->toBeTrue(); // Interface should exist

        $className = ClassName::create('App\\Framework\\Filesystem\\FileStorage');
        expect($className->exists())->toBeTrue(); // Implementation should exist
    });

    it('regression test - ensures no "Uninitialized string offset 0" warnings', function () {
        // This is a regression test for the specific bug we fixed
        // The bug occurred when empty strings were passed to class_exists()

        // Capture any warnings or errors
        $errorLevel = error_reporting(E_ALL);
        $errors = [];

        set_error_handler(function ($severity, $message, $file, $line) use (&$errors) {
            $errors[] = [
                'severity' => $severity,
                'message' => $message,
                'file' => $file,
                'line' => $line,
            ];
        });

        try {
            // Test various scenarios that could potentially cause the issue
            $testCases = [
                'stdClass',
                'Exception',
                'NonExistentClass',
                'App\\Framework\\Filesystem\\AtomicStorage',
                'App\\Framework\\Filesystem\\Storage',
                'App\\Framework\\DI\\Container',
                'App\\NonExistent\\Class\\Name',
                'Very\\Long\\NonExistent\\Namespace\\ClassName',
            ];

            foreach ($testCases as $testCase) {
                $className = ClassName::create($testCase);
                $result = $className->exists();

                // Result should be boolean, no errors should occur
                expect($result)->toBeBool();
            }

            // Ensure no "Uninitialized string offset" or similar warnings occurred
            foreach ($errors as $error) {
                expect($error['message'])->not->toContain('Uninitialized string offset');
                expect($error['message'])->not->toContain('ClassLoader.php');
            }

            // Should have no errors at all
            expect($errors)->toBeEmpty();

        } finally {
            restore_error_handler();
            error_reporting($errorLevel);
        }
    });
});
