<?php

declare(strict_types=1);

use App\Framework\OAuth\ValueObjects\TokenScope;

describe('TokenScope Value Object', function () {
    it('creates from array of scopes', function () {
        $scope = TokenScope::fromArray(['read', 'write', 'admin']);

        expect($scope->toArray())->toBe(['read', 'write', 'admin']);
        expect($scope->toString())->toBe('read write admin');
    });

    it('creates from space-separated string', function () {
        $scope = TokenScope::fromString('read write admin');

        expect($scope->toArray())->toBe(['read', 'write', 'admin']);
    });

    it('handles extra whitespace in string', function () {
        $scope = TokenScope::fromString('  read   write   admin  ');

        expect($scope->toArray())->toBe(['read', 'write', 'admin']);
    });

    it('rejects empty scope array', function () {
        TokenScope::fromArray([]);
    })->throws(\InvalidArgumentException::class, 'Token scope cannot be empty');

    it('rejects empty scope string', function () {
        TokenScope::fromString('');
    })->throws(\InvalidArgumentException::class, 'Token scope cannot be empty');

    it('rejects scope array with empty strings', function () {
        TokenScope::fromArray(['read', '', 'write']);
    })->throws(\InvalidArgumentException::class, 'Invalid scope value');

    it('rejects scope array with non-strings', function () {
        TokenScope::fromArray(['read', 123, 'write']);
    })->throws(\InvalidArgumentException::class, 'Invalid scope value');

    it('checks if scope includes specific permission', function () {
        $scope = TokenScope::fromArray(['read', 'write', 'admin']);

        expect($scope->includes('read'))->toBeTrue();
        expect($scope->includes('write'))->toBeTrue();
        expect($scope->includes('admin'))->toBeTrue();
        expect($scope->includes('delete'))->toBeFalse();
    });

    it('checks if scope includes all required permissions', function () {
        $scope = TokenScope::fromArray(['read', 'write', 'admin']);

        expect($scope->includesAll(['read', 'write']))->toBeTrue();
        expect($scope->includesAll(['read', 'admin']))->toBeTrue();
        expect($scope->includesAll(['read', 'write', 'admin']))->toBeTrue();
        expect($scope->includesAll(['read', 'delete']))->toBeFalse();
    });

    it('checks if scope includes any of specified permissions', function () {
        $scope = TokenScope::fromArray(['read', 'write']);

        expect($scope->includesAny(['read', 'admin']))->toBeTrue();
        expect($scope->includesAny(['write', 'delete']))->toBeTrue();
        expect($scope->includesAny(['admin', 'delete']))->toBeFalse();
    });

    it('adds additional scopes immutably', function () {
        $originalScope = TokenScope::fromArray(['read', 'write']);

        $extendedScope = $originalScope->withAdditional(['admin', 'delete']);

        // Original unchanged
        expect($originalScope->toArray())->toBe(['read', 'write']);

        // New instance extended
        expect($extendedScope->toArray())->toBe(['read', 'write', 'admin', 'delete']);
    });

    it('removes specific scopes immutably', function () {
        $originalScope = TokenScope::fromArray(['read', 'write', 'admin', 'delete']);

        $reducedScope = $originalScope->without(['admin', 'delete']);

        // Original unchanged
        expect($originalScope->toArray())->toBe(['read', 'write', 'admin', 'delete']);

        // New instance reduced
        expect($reducedScope->toArray())->toBe(['read', 'write']);
    });

    it('prevents removing all scopes', function () {
        $scope = TokenScope::fromArray(['read', 'write']);

        $scope->without(['read', 'write']);
    })->throws(\InvalidArgumentException::class, 'Cannot remove all scopes');

    it('converts to string correctly', function () {
        $scope = TokenScope::fromArray(['read', 'write', 'admin']);

        expect($scope->toString())->toBe('read write admin');
        expect((string) $scope)->toBe('read write admin');
    });
});
