<?php

namespace MintyPHP\Tests\Service\Access;

use MintyPHP\Service\Access\PermissionAuthorizationPolicy;
use MintyPHP\Service\Access\PermissionService;
use PHPUnit\Framework\TestCase;

class PermissionAuthorizationPolicyTest extends TestCase
{
    use AuthorizationPolicyTestSupport;

    public function testAdminViewRequiresPermissionsViewPermission(): void
    {
        $permissionService = $this->createMock(PermissionService::class);
        $permissionService->expects($this->once())
            ->method('userHas')
            ->with(9, PermissionService::PERMISSIONS_VIEW)
            ->willReturn(false);

        $policy = new PermissionAuthorizationPolicy($permissionService);
        $decision = $policy->authorize(PermissionAuthorizationPolicy::ABILITY_ADMIN_PERMISSIONS_VIEW, [
            'actor_user_id' => 9,
        ]);

        $this->assertForbiddenDecision($decision);
    }

    public function testAdminViewIncludesCreateCapability(): void
    {
        $permissionService = $this->permissionGatewayAllowing([
            9 => [PermissionService::PERMISSIONS_VIEW, PermissionService::PERMISSIONS_CREATE],
        ]);

        $policy = new PermissionAuthorizationPolicy($permissionService);
        $decision = $policy->authorize(PermissionAuthorizationPolicy::ABILITY_ADMIN_PERMISSIONS_VIEW, [
            'actor_user_id' => 9,
        ]);

        $this->assertTrue($decision->isAllowed());
        $this->assertTrue((bool) ($decision->attribute('capabilities', [])['can_create_permission'] ?? false));
    }

    public function testAdminCreateRequiresPermissionsCreatePermission(): void
    {
        $permissionService = $this->createMock(PermissionService::class);
        $permissionService->expects($this->once())
            ->method('userHas')
            ->with(3, PermissionService::PERMISSIONS_CREATE)
            ->willReturn(false);

        $policy = new PermissionAuthorizationPolicy($permissionService);
        $decision = $policy->authorize(PermissionAuthorizationPolicy::ABILITY_ADMIN_PERMISSIONS_CREATE, [
            'actor_user_id' => 3,
        ]);

        $this->assertDeniedDecision($decision, 403, 'forbidden');
    }

    public function testEditContextReturnsCapabilitiesAndSystemDeleteRestriction(): void
    {
        $permissionService = $this->permissionGatewayAllowing([
            5 => [
                PermissionService::PERMISSIONS_VIEW,
                PermissionService::PERMISSIONS_UPDATE,
                PermissionService::PERMISSIONS_DELETE,
            ],
        ]);

        $policy = new PermissionAuthorizationPolicy($permissionService);
        $decision = $policy->authorize(PermissionAuthorizationPolicy::ABILITY_ADMIN_PERMISSIONS_EDIT_CONTEXT, [
            'actor_user_id' => 5,
            'target_permission_id' => 21,
            'target_is_system' => true,
        ]);

        $capabilities = $decision->attribute('capabilities', []);
        $this->assertTrue($decision->isAllowed());
        $this->assertTrue((bool) ($capabilities['can_update_permission'] ?? false));
        $this->assertFalse((bool) ($capabilities['can_delete_permission'] ?? true));
    }

    public function testEditSubmitRequiresPermissionsUpdatePermission(): void
    {
        $permissionService = $this->permissionGatewayAllowing([
            5 => [PermissionService::PERMISSIONS_VIEW],
        ]);

        $policy = new PermissionAuthorizationPolicy($permissionService);
        $decision = $policy->authorize(PermissionAuthorizationPolicy::ABILITY_ADMIN_PERMISSIONS_EDIT_SUBMIT, [
            'actor_user_id' => 5,
            'target_permission_id' => 21,
            'target_is_system' => false,
        ]);

        $this->assertForbiddenDecision($decision);
    }

    public function testDeleteRequiresPermissionsDeletePermission(): void
    {
        $permissionService = $this->createMock(PermissionService::class);
        $permissionService->expects($this->once())
            ->method('userHas')
            ->with(12, PermissionService::PERMISSIONS_DELETE)
            ->willReturn(false);

        $policy = new PermissionAuthorizationPolicy($permissionService);
        $decision = $policy->authorize(PermissionAuthorizationPolicy::ABILITY_ADMIN_PERMISSIONS_DELETE, [
            'actor_user_id' => 12,
        ]);

        $this->assertDeniedDecision($decision, 403, 'forbidden');
    }
}
