<?php

namespace MintyPHP\Tests\Service\Access;

use MintyPHP\App\AppContainer;
use MintyPHP\App\Module\ModuleClassResolver;
use MintyPHP\App\Module\ModuleRegistry;
use MintyPHP\Service\Access\AccessPolicyFactory;
use MintyPHP\Service\Access\AuthorizationDecision;
use MintyPHP\Service\Access\AuthorizationPolicyInterface;
use MintyPHP\Service\Access\PermissionService;
use MintyPHP\Service\Tenant\TenantScopeService;
use PHPUnit\Framework\TestCase;

final class AccessPolicyFactoryModuleResolverTest extends TestCase
{
    public function testModulePolicyWithMultipleDependenciesIsResolvedViaContainer(): void
    {
        $fixturesDir = sys_get_temp_dir() . '/access-policy-factory-' . uniqid('', true);
        $moduleDir = $fixturesDir . '/mod-policy';
        mkdir($moduleDir, 0777, true);

        file_put_contents(
            $moduleDir . '/module.php',
            '<?php return ' . var_export([
                'id' => 'mod-policy',
                'authorization_policies' => [TestMultiDependencyPolicy::class],
            ], true) . ';'
        );

        try {
            $registry = ModuleRegistry::boot($fixturesDir, ['mod-policy']);

            $permissionService = $this->createMock(PermissionService::class);
            $tenantScopeService = $this->createMock(TenantScopeService::class);
            $dependency = new TestPolicyDependency();

            $container = new AppContainer();
            $container->set(
                TestMultiDependencyPolicy::class,
                static fn () => new TestMultiDependencyPolicy($permissionService, $dependency)
            );

            $factory = new AccessPolicyFactory(
                $permissionService,
                $tenantScopeService,
                $registry,
                static fn (string $class): ?object => ModuleClassResolver::resolve($container, $class)
            );

            $authorizationService = $factory->createAuthorizationService();
            $decision = $authorizationService->authorize(TestMultiDependencyPolicy::ABILITY, [
                'actor_user_id' => 7,
            ]);

            self::assertTrue($decision->isAllowed());
            self::assertSame('container', $decision->attribute('resolver'));
        } finally {
            @unlink($moduleDir . '/module.php');
            @rmdir($moduleDir);
            @rmdir($fixturesDir);
        }
    }
}

final class TestPolicyDependency
{
    public function value(): string
    {
        return 'container';
    }
}

final class TestMultiDependencyPolicy implements AuthorizationPolicyInterface
{
    public const ABILITY = 'module.multi.dep';

    public function __construct(
        private readonly PermissionService $permissionService,
        private readonly TestPolicyDependency $dependency
    ) {
    }

    public function supports(string $ability): bool
    {
        return $ability === self::ABILITY;
    }

    public function authorize(string $ability, array $context = []): AuthorizationDecision
    {
        if ($ability !== self::ABILITY) {
            return AuthorizationDecision::deny(500, 'authorization_ability_not_supported');
        }

        if ((int) ($context['actor_user_id'] ?? 0) <= 0) {
            return AuthorizationDecision::deny(403, 'forbidden');
        }

        return AuthorizationDecision::allow([
            'resolver' => $this->dependency->value(),
            'permission_service' => $this->permissionService::class,
        ]);
    }
}
