<?php

namespace MintyPHP\Tests\Service\Access;

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

class OperationsAuthorizationPolicyTest extends TestCase
{
    use AuthorizationPolicyTestSupport;

    // --- supports() ---

    public function testSupportsReturnsTrueForKnownAbilities(): void
    {
        $policy = new OperationsAuthorizationPolicy($this->createMock(PermissionService::class));

        $this->assertTrue($policy->supports(OperationsAuthorizationPolicy::ABILITY_ADMIN_JOBS_VIEW));
        $this->assertTrue($policy->supports(OperationsAuthorizationPolicy::ABILITY_API_TENANTS_DELETE));
        $this->assertTrue($policy->supports(OperationsAuthorizationPolicy::ABILITY_API_TOKENS_SELF_MANAGE));
        $this->assertTrue($policy->supports(OperationsAuthorizationPolicy::ABILITY_ADMIN_USERS_CREATE_EDIT_CUSTOM_FIELDS));
    }

    public function testSupportsReturnsFalseForUnknownAbility(): void
    {
        $policy = new OperationsAuthorizationPolicy($this->createMock(PermissionService::class));

        $this->assertFalse($policy->supports('ops.nonexistent'));
        $this->assertFalse($policy->supports('admin.roles.view'));
        $this->assertFalse($policy->supports(''));
    }

    // --- authorize: zero/negative actor_user_id ---

    public function testAuthorizeDeniesWhenActorUserIdIsZero(): void
    {
        $permissionService = $this->createMock(PermissionService::class);
        $permissionService->expects($this->never())->method('userHas');

        $policy = new OperationsAuthorizationPolicy($permissionService);
        $decision = $policy->authorize(OperationsAuthorizationPolicy::ABILITY_ADMIN_JOBS_VIEW, [
            'actor_user_id' => 0,
        ]);

        $this->assertForbiddenDecision($decision);
    }

    public function testAuthorizeDeniesWhenActorUserIdIsMissing(): void
    {
        $permissionService = $this->createMock(PermissionService::class);
        $permissionService->expects($this->never())->method('userHas');

        $policy = new OperationsAuthorizationPolicy($permissionService);
        $decision = $policy->authorize(OperationsAuthorizationPolicy::ABILITY_ADMIN_STATS_VIEW, []);

        $this->assertForbiddenDecision($decision);
    }

    public function testAuthorizeDeniesWhenActorUserIdIsNegative(): void
    {
        $permissionService = $this->createMock(PermissionService::class);
        $permissionService->expects($this->never())->method('userHas');

        $policy = new OperationsAuthorizationPolicy($permissionService);
        $decision = $policy->authorize(OperationsAuthorizationPolicy::ABILITY_ADMIN_STATS_VIEW, [
            'actor_user_id' => -1,
        ]);

        $this->assertForbiddenDecision($decision);
    }

    // --- authorize: unsupported ability ---

    public function testAuthorizeReturns500ForUnsupportedAbility(): void
    {
        $policy = new OperationsAuthorizationPolicy($this->createMock(PermissionService::class));
        $decision = $policy->authorize('ops.nonexistent', ['actor_user_id' => 1]);

        $this->assertDeniedDecision($decision, 500, 'authorization_ability_not_supported');
    }

    // --- authorize: allowIfHas (simple permission check) ---

    // Address book ability tests moved to AddressBookAuthorizationPolicyTest (module-owned).

    public function testJobsViewAllowedWithPermission(): void
    {
        $permissionService = $this->permissionGatewayAllowing([
            10 => [PermissionService::JOBS_VIEW],
        ]);

        $policy = new OperationsAuthorizationPolicy($permissionService);
        $decision = $policy->authorize(OperationsAuthorizationPolicy::ABILITY_ADMIN_JOBS_VIEW, [
            'actor_user_id' => 10,
        ]);

        $this->assertTrue($decision->isAllowed());
    }

    public function testApiTenantsDeleteDeniedWithoutPermission(): void
    {
        $permissionService = $this->permissionGatewayAllowing([8 => []]);

        $policy = new OperationsAuthorizationPolicy($permissionService);
        $decision = $policy->authorize(OperationsAuthorizationPolicy::ABILITY_API_TENANTS_DELETE, [
            'actor_user_id' => 8,
        ]);

        $this->assertForbiddenDecision($decision);
    }

    // --- authorize: authorizeUsersCreateCustomFields (requires TWO permissions) ---

    public function testUsersCreateCustomFieldsAllowedWithBothPermissions(): void
    {
        $permissionService = $this->permissionGatewayAllowing([
            7 => [PermissionService::CUSTOM_FIELDS_EDIT_VALUES, PermissionService::USERS_UPDATE_ASSIGNMENTS],
        ]);

        $policy = new OperationsAuthorizationPolicy($permissionService);
        $decision = $policy->authorize(OperationsAuthorizationPolicy::ABILITY_ADMIN_USERS_CREATE_EDIT_CUSTOM_FIELDS, [
            'actor_user_id' => 7,
        ]);

        $this->assertTrue($decision->isAllowed());
    }

    public function testUsersCreateCustomFieldsDeniedWithoutCustomFieldsPermission(): void
    {
        $permissionService = $this->permissionGatewayAllowing([
            7 => [PermissionService::USERS_UPDATE_ASSIGNMENTS],
        ]);

        $policy = new OperationsAuthorizationPolicy($permissionService);
        $decision = $policy->authorize(OperationsAuthorizationPolicy::ABILITY_ADMIN_USERS_CREATE_EDIT_CUSTOM_FIELDS, [
            'actor_user_id' => 7,
        ]);

        $this->assertForbiddenDecision($decision);
    }

    public function testUsersCreateCustomFieldsDeniedWithoutAssignmentsPermission(): void
    {
        $permissionService = $this->permissionGatewayAllowing([
            7 => [PermissionService::CUSTOM_FIELDS_EDIT_VALUES],
        ]);

        $policy = new OperationsAuthorizationPolicy($permissionService);
        $decision = $policy->authorize(OperationsAuthorizationPolicy::ABILITY_ADMIN_USERS_CREATE_EDIT_CUSTOM_FIELDS, [
            'actor_user_id' => 7,
        ]);

        $this->assertForbiddenDecision($decision);
    }

    // --- authorize: authorizeApiTokensSelfManage (requires EITHER permission) ---

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

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

        $this->assertTrue($decision->isAllowed());
    }

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

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

        $this->assertTrue($decision->isAllowed());
    }

    public function testApiTokensSelfManageDeniedWithoutEitherPermission(): void
    {
        $permissionService = $this->permissionGatewayAllowing([9 => []]);

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

        $this->assertForbiddenDecision($decision);
    }
}
