<?php

namespace MintyPHP\Service\Access;

class PermissionAuthorizationPolicy implements AuthorizationPolicyInterface
{
    use AuthorizationPolicyContextTrait;

    public const ABILITY_ADMIN_PERMISSIONS_VIEW = 'admin.permissions.view';
    public const ABILITY_ADMIN_PERMISSIONS_CREATE = 'admin.permissions.create';
    public const ABILITY_ADMIN_PERMISSIONS_EDIT_CONTEXT = 'admin.permissions.edit.context';
    public const ABILITY_ADMIN_PERMISSIONS_EDIT_SUBMIT = 'admin.permissions.edit.submit';
    public const ABILITY_ADMIN_PERMISSIONS_DELETE = 'admin.permissions.delete';

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

    public function supports(string $ability): bool
    {
        return in_array($ability, [
            self::ABILITY_ADMIN_PERMISSIONS_VIEW,
            self::ABILITY_ADMIN_PERMISSIONS_CREATE,
            self::ABILITY_ADMIN_PERMISSIONS_EDIT_CONTEXT,
            self::ABILITY_ADMIN_PERMISSIONS_EDIT_SUBMIT,
            self::ABILITY_ADMIN_PERMISSIONS_DELETE,
        ], true);
    }

    public function authorize(string $ability, array $context = []): AuthorizationDecision
    {
        return match ($ability) {
            self::ABILITY_ADMIN_PERMISSIONS_VIEW => $this->authorizeAdminPermissionsView($context),
            self::ABILITY_ADMIN_PERMISSIONS_CREATE => $this->authorizeAdminPermissionsCreate($context),
            self::ABILITY_ADMIN_PERMISSIONS_EDIT_CONTEXT => $this->authorizeAdminPermissionsEditContext($context),
            self::ABILITY_ADMIN_PERMISSIONS_EDIT_SUBMIT => $this->authorizeAdminPermissionsEditSubmit($context),
            self::ABILITY_ADMIN_PERMISSIONS_DELETE => $this->authorizeAdminPermissionsDelete($context),
            default => AuthorizationDecision::deny(500, 'authorization_ability_not_supported'),
        };
    }

    private function authorizeAdminPermissionsView(array $context): AuthorizationDecision
    {
        $actorUserId = $this->actorUserId($context);
        if (!$this->hasPermission($actorUserId, PermissionService::PERMISSIONS_VIEW)) {
            return AuthorizationDecision::deny(403, 'forbidden');
        }

        return AuthorizationDecision::allow([
            'capabilities' => [
                'can_view_page' => true,
                'can_create_permission' => $this->hasPermission($actorUserId, PermissionService::PERMISSIONS_CREATE),
            ],
        ]);
    }

    private function authorizeAdminPermissionsCreate(array $context): AuthorizationDecision
    {
        $actorUserId = $this->actorUserId($context);
        if (!$this->hasPermission($actorUserId, PermissionService::PERMISSIONS_CREATE)) {
            return AuthorizationDecision::deny(403, 'forbidden');
        }

        return AuthorizationDecision::allow([
            'capabilities' => [
                'can_view_page' => true,
            ],
        ]);
    }

    private function authorizeAdminPermissionsEditContext(array $context): AuthorizationDecision
    {
        $actorUserId = $this->actorUserId($context);
        $targetPermissionId = (int) ($context['target_permission_id'] ?? 0);
        if ($actorUserId <= 0 || $targetPermissionId <= 0) {
            return AuthorizationDecision::deny(403, 'forbidden');
        }

        if (!$this->hasPermission($actorUserId, PermissionService::PERMISSIONS_VIEW)) {
            return AuthorizationDecision::deny(403, 'forbidden');
        }

        $targetIsSystem = (bool) ($context['target_is_system'] ?? false);

        return AuthorizationDecision::allow([
            'capabilities' => [
                'can_view_page' => true,
                'can_update_permission' => $this->hasPermission($actorUserId, PermissionService::PERMISSIONS_UPDATE),
                'can_delete_permission' => $this->hasPermission($actorUserId, PermissionService::PERMISSIONS_DELETE) && !$targetIsSystem,
            ],
        ]);
    }

    private function authorizeAdminPermissionsEditSubmit(array $context): AuthorizationDecision
    {
        $contextDecision = $this->authorizeAdminPermissionsEditContext($context);
        if (!$contextDecision->isAllowed()) {
            return $contextDecision;
        }

        $capabilities = $this->capabilitiesFromDecision($contextDecision);
        if (!($capabilities['can_update_permission'] ?? false)) {
            return AuthorizationDecision::deny(403, 'forbidden');
        }

        return AuthorizationDecision::allow(['capabilities' => $capabilities]);
    }

    private function authorizeAdminPermissionsDelete(array $context): AuthorizationDecision
    {
        $actorUserId = $this->actorUserId($context);
        if (!$this->hasPermission($actorUserId, PermissionService::PERMISSIONS_DELETE)) {
            return AuthorizationDecision::deny(403, 'forbidden');
        }

        return AuthorizationDecision::allow();
    }

}
