<?php

declare(strict_types=1);

use App\Framework\Auth\ValueObjects\NamespaceAccessPolicy;

describe('NamespaceAccessPolicy', function () {
    describe('blocked()', function () {
        it('creates policy that blocks all controllers', function () {
            $policy = NamespaceAccessPolicy::blocked();

            expect($policy->isControllerBlocked('App\Application\Admin\Dashboard'))->toBeTrue();
            expect($policy->isControllerBlocked('App\Application\Admin\UserController'))->toBeTrue();
            expect($policy->hasRestrictions())->toBeTrue();
        });
    });

    describe('blockedExcept()', function () {
        it('blocks all except allowed controllers', function () {
            $policy = NamespaceAccessPolicy::blockedExcept(
                'App\Application\Admin\LoginController',
                'App\Application\Admin\HealthController'
            );

            expect($policy->isControllerBlocked('App\Application\Admin\LoginController'))->toBeFalse();
            expect($policy->isControllerBlocked('App\Application\Admin\HealthController'))->toBeFalse();
            expect($policy->isControllerBlocked('App\Application\Admin\Dashboard'))->toBeTrue();
            expect($policy->hasRestrictions())->toBeTrue();
        });

        it('handles empty allowlist', function () {
            $policy = NamespaceAccessPolicy::blockedExcept();

            expect($policy->isControllerBlocked('App\Application\Admin\Dashboard'))->toBeTrue();
        });
    });

    describe('allowed()', function () {
        it('allows all controllers', function () {
            $policy = NamespaceAccessPolicy::allowed();

            expect($policy->isControllerBlocked('App\Application\Admin\Dashboard'))->toBeFalse();
            expect($policy->isControllerBlocked('App\Application\Admin\UserController'))->toBeFalse();
            expect($policy->hasRestrictions())->toBeFalse();
        });
    });

    describe('withAllowedControllers()', function () {
        it('adds controllers to allowlist', function () {
            $policy = NamespaceAccessPolicy::blocked();

            $newPolicy = $policy->withAllowedControllers(
                'App\Application\Admin\LoginController'
            );

            expect($newPolicy->isControllerBlocked('App\Application\Admin\LoginController'))->toBeFalse();
            expect($newPolicy->isControllerBlocked('App\Application\Admin\Dashboard'))->toBeTrue();
        });

        it('preserves existing allowlist', function () {
            $policy = NamespaceAccessPolicy::blockedExcept(
                'App\Application\Admin\LoginController'
            );

            $newPolicy = $policy->withAllowedControllers(
                'App\Application\Admin\HealthController'
            );

            expect($newPolicy->isControllerBlocked('App\Application\Admin\LoginController'))->toBeFalse();
            expect($newPolicy->isControllerBlocked('App\Application\Admin\HealthController'))->toBeFalse();
            expect($newPolicy->isControllerBlocked('App\Application\Admin\Dashboard'))->toBeTrue();
        });

        it('handles duplicate controllers', function () {
            $policy = NamespaceAccessPolicy::blockedExcept(
                'App\Application\Admin\LoginController'
            );

            $newPolicy = $policy->withAllowedControllers(
                'App\Application\Admin\LoginController',
                'App\Application\Admin\HealthController'
            );

            expect($newPolicy->isControllerBlocked('App\Application\Admin\LoginController'))->toBeFalse();
            expect($newPolicy->isControllerBlocked('App\Application\Admin\HealthController'))->toBeFalse();
        });
    });

    describe('immutability', function () {
        it('does not modify original policy when adding controllers', function () {
            $original = NamespaceAccessPolicy::blocked();

            $modified = $original->withAllowedControllers(
                'App\Application\Admin\LoginController'
            );

            expect($original->isControllerBlocked('App\Application\Admin\LoginController'))->toBeTrue();
            expect($modified->isControllerBlocked('App\Application\Admin\LoginController'))->toBeFalse();
        });
    });
});
