<?php

namespace MintyPHP\Tests\Service\Access;

use MintyPHP\Service\Access\AuthorizationDecision;
use MintyPHP\Service\Access\AuthorizationService;
use MintyPHP\Service\Access\OperationsAuthorizationPolicy;
use MintyPHP\Service\Access\UiAccessService;
use MintyPHP\Service\Access\UiCapabilityMap;
use PHPUnit\Framework\TestCase;

class UiAccessServiceTest extends TestCase
{
    public function testAllowCachesDecisionPerAbilityAndContext(): void
    {
        $authorizationService = $this->createMock(AuthorizationService::class);
        $authorizationService->expects($this->once())
            ->method('authorize')
            ->with(OperationsAuthorizationPolicy::ABILITY_ADMIN_JOBS_VIEW, [
                'actor_user_id' => 7,
                'scoped_tenant_id' => 3,
            ])
            ->willReturn(AuthorizationDecision::allow());

        $service = new UiAccessService($authorizationService);

        $this->assertTrue($service->allow(7, OperationsAuthorizationPolicy::ABILITY_ADMIN_JOBS_VIEW, ['scoped_tenant_id' => 3]));
        $this->assertTrue($service->allow(7, OperationsAuthorizationPolicy::ABILITY_ADMIN_JOBS_VIEW, ['scoped_tenant_id' => 3]));
    }

    public function testResolveMapSupportsStringAndStructuredDefinition(): void
    {
        $authorizationService = $this->createMock(AuthorizationService::class);
        $authorizationService->expects($this->exactly(2))
            ->method('authorize')
            ->willReturnCallback(static function (string $ability, array $context): AuthorizationDecision {
                if ($ability === OperationsAuthorizationPolicy::ABILITY_ADMIN_STATS_VIEW) {
                    return AuthorizationDecision::allow();
                }
                if ($ability === OperationsAuthorizationPolicy::ABILITY_ADMIN_MAIL_LOG_VIEW
                    && (int) ($context['target_tenant_id'] ?? 0) === 99) {
                    return AuthorizationDecision::allow();
                }

                return AuthorizationDecision::deny(403, 'forbidden');
            });

        $service = new UiAccessService($authorizationService);
        $result = $service->resolveMap(11, [
            'stats' => OperationsAuthorizationPolicy::ABILITY_ADMIN_STATS_VIEW,
            'mail_log' => [
                'ability' => OperationsAuthorizationPolicy::ABILITY_ADMIN_MAIL_LOG_VIEW,
                'context' => ['target_tenant_id' => 99],
            ],
        ]);

        $this->assertSame([
            'stats' => true,
            'mail_log' => true,
        ], $result);
    }

    public function testLayoutCapabilitiesExposeExpectedKeys(): void
    {
        $authorizationService = $this->createMock(AuthorizationService::class);
        $authorizationService->method('authorize')->willReturn(AuthorizationDecision::deny(403, 'forbidden'));

        $service = new UiAccessService($authorizationService);
        $result = $service->layoutCapabilities(5);

        foreach (array_keys(UiCapabilityMap::LAYOUT) as $key) {
            $this->assertArrayHasKey($key, $result);
            $this->assertFalse($result[$key]);
        }
    }

    public function testPageCapabilitiesUsesResolveMapContract(): void
    {
        $authorizationService = $this->createMock(AuthorizationService::class);
        $authorizationService->expects($this->once())
            ->method('authorize')
            ->with(OperationsAuthorizationPolicy::ABILITY_ADMIN_JOBS_VIEW, [
                'actor_user_id' => 9,
            ])
            ->willReturn(AuthorizationDecision::allow());

        $service = new UiAccessService($authorizationService);
        $result = $service->pageCapabilities(9, [
            'can_view_jobs' => OperationsAuthorizationPolicy::ABILITY_ADMIN_JOBS_VIEW,
        ]);

        $this->assertSame(['can_view_jobs' => true], $result);
    }
}
