<?php

declare(strict_types=1);

use App\Framework\Queue\ValueObjects\JobId;
use App\Framework\Queue\ValueObjects\LockKey;
use App\Framework\Queue\ValueObjects\QueueName;
use App\Framework\Queue\ValueObjects\WorkerId;

describe('LockKey Value Object', function () {
    it('can create lock keys from strings', function () {
        $key = 'test.lock.key';
        $lockKey = LockKey::fromString($key);

        expect($lockKey->toString())->toBe($key);
        expect((string) $lockKey)->toBe($key);
    });

    it('validates lock key constraints', function () {
        // Empty key
        expect(fn () => LockKey::fromString(''))
            ->toThrow(\InvalidArgumentException::class, 'Lock key cannot be empty');

        // Too long
        expect(fn () => LockKey::fromString(str_repeat('a', 256)))
            ->toThrow(\InvalidArgumentException::class, 'Lock key cannot exceed 255 characters');

        // Invalid characters
        expect(fn () => LockKey::fromString('invalid@key!'))
            ->toThrow(\InvalidArgumentException::class, 'Lock key contains invalid characters');

        expect(fn () => LockKey::fromString('key with spaces'))
            ->toThrow(\InvalidArgumentException::class, 'Lock key contains invalid characters');
    });

    it('allows valid characters only', function () {
        $validKeys = [
            'simple-key',
            'key_with_underscores',
            'key.with.dots',
            'key123',
            'UPPERCASE-key',
            'mixed-Key_123.test',
        ];

        foreach ($validKeys as $key) {
            $lockKey = LockKey::fromString($key);
            expect($lockKey->toString())->toBe($key);
        }
    });

    it('can create job-specific lock keys', function () {
        $jobId = JobId::generate();
        $lockKey = LockKey::forJob($jobId);

        expect($lockKey->toString())->toStartWith('job.');
        expect($lockKey->toString())->toContain($jobId->toString());
    });

    it('can create queue-specific lock keys', function () {
        $queueName = QueueName::defaultQueue();
        $lockKey = LockKey::forQueue($queueName);

        expect($lockKey->toString())->toStartWith('queue.');
        expect($lockKey->toString())->toContain($queueName->toString());
    });

    it('can create worker-specific lock keys', function () {
        $workerId = WorkerId::generate();
        $lockKey = LockKey::forWorker($workerId);

        expect($lockKey->toString())->toStartWith('worker.');
        expect($lockKey->toString())->toContain($workerId->toString());
    });

    it('can create resource-specific lock keys', function () {
        $lockKey = LockKey::forResource('database', 'user-table');

        expect($lockKey->toString())->toBe('database.user-table');
    });

    it('can create batch-specific lock keys', function () {
        $batchId = 'batch-123-abc';
        $lockKey = LockKey::forBatch($batchId);

        expect($lockKey->toString())->toBe('batch.' . $batchId);
    });

    it('supports prefix modification', function () {
        $lockKey = LockKey::fromString('original.key');
        $prefixed = $lockKey->withPrefix('tenant-1');

        expect($prefixed->toString())->toBe('tenant-1.original.key');
        expect($lockKey->toString())->toBe('original.key'); // Original unchanged
    });

    it('supports suffix modification', function () {
        $lockKey = LockKey::fromString('original.key');
        $suffixed = $lockKey->withSuffix('processing');

        expect($suffixed->toString())->toBe('original.key.processing');
        expect($lockKey->toString())->toBe('original.key'); // Original unchanged
    });

    it('supports pattern matching', function () {
        $lockKey = LockKey::fromString('job.email-queue.123');

        expect($lockKey->matches('job.*'))->toBeTrue();
        expect($lockKey->matches('job.email-queue.*'))->toBeTrue();
        expect($lockKey->matches('worker.*'))->toBeFalse();
        expect($lockKey->matches('*.123'))->toBeTrue();
    });

    it('supports equality comparison', function () {
        $key = 'test.lock.key';
        $lockKey1 = LockKey::fromString($key);
        $lockKey2 = LockKey::fromString($key);
        $lockKey3 = LockKey::fromString('different.key');

        expect($lockKey1->equals($lockKey2))->toBeTrue();
        expect($lockKey1->equals($lockKey3))->toBeFalse();
    });

    it('supports JSON serialization', function () {
        $key = 'serializable.lock.key';
        $lockKey = LockKey::fromString($key);

        expect($lockKey->jsonSerialize())->toBe($key);
        expect(json_encode($lockKey))->toBe('"' . $key . '"');
    });

    it('can chain modifications', function () {
        $lockKey = LockKey::fromString('base.key')
            ->withPrefix('tenant-1')
            ->withSuffix('processing')
            ->withSuffix('active');

        expect($lockKey->toString())->toBe('tenant-1.base.key.processing.active');
    });

    it('handles complex resource hierarchies', function () {
        // Simulate nested resource locks
        $databaseLock = LockKey::forResource('database', 'users');
        $tableLock = $databaseLock->withSuffix('table-lock');
        $rowLock = $tableLock->withSuffix('row-123');

        expect($databaseLock->toString())->toBe('database.users');
        expect($tableLock->toString())->toBe('database.users.table-lock');
        expect($rowLock->toString())->toBe('database.users.table-lock.row-123');

        // Pattern matching for hierarchical locks
        expect($rowLock->matches('database.users.*'))->toBeTrue();
        expect($rowLock->matches('*.row-123'))->toBeTrue();
    });
});
