<?php

namespace MintyPHP\Service\Access;

class RoleAuthorizationPolicy implements AuthorizationPolicyInterface
{
    public const ABILITY_ADMIN_ROLES_VIEW = 'admin.roles.view';
    public const ABILITY_ADMIN_ROLES_CREATE = 'admin.roles.create';
    public const ABILITY_ADMIN_ROLES_EDIT_CONTEXT = 'admin.roles.edit.context';
    public const ABILITY_ADMIN_ROLES_EDIT_SUBMIT = 'admin.roles.edit.submit';
    public const ABILITY_ADMIN_ROLES_DELETE = 'admin.roles.delete';

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

    public function supports(string $ability): bool
    {
        return in_array($ability, [
            self::ABILITY_ADMIN_ROLES_VIEW,
            self::ABILITY_ADMIN_ROLES_CREATE,
            self::ABILITY_ADMIN_ROLES_EDIT_CONTEXT,
            self::ABILITY_ADMIN_ROLES_EDIT_SUBMIT,
            self::ABILITY_ADMIN_ROLES_DELETE,
        ], true);
    }

    public function authorize(string $ability, array $context = []): AuthorizationDecision
    {
        return match ($ability) {
            self::ABILITY_ADMIN_ROLES_VIEW => $this->authorizeAdminRolesView($context),
            self::ABILITY_ADMIN_ROLES_CREATE => $this->authorizeAdminRolesCreate($context),
            self::ABILITY_ADMIN_ROLES_EDIT_CONTEXT => $this->authorizeAdminRolesEditContext($context),
            self::ABILITY_ADMIN_ROLES_EDIT_SUBMIT => $this->authorizeAdminRolesEditSubmit($context),
            self::ABILITY_ADMIN_ROLES_DELETE => $this->authorizeAdminRolesDelete($context),
            default => AuthorizationDecision::deny(500, 'authorization_ability_not_supported'),
        };
    }

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

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

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

        return AuthorizationDecision::allow();
    }

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

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

        return AuthorizationDecision::allow([
            'capabilities' => [
                'can_view_page' => true,
                'can_edit_role' => $this->hasPermission($actorUserId, PermissionService::ROLES_UPDATE),
                'can_delete_role' => $this->hasPermission($actorUserId, PermissionService::ROLES_DELETE),
            ],
        ]);
    }

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

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

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

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

        return AuthorizationDecision::allow();
    }

    private function actorUserId(array $context): int
    {
        return (int) ($context['actor_user_id'] ?? 0);
    }

    private function hasPermission(int $userId, string $permissionKey): bool
    {
        return $userId > 0 && $this->permissionService->userHas($userId, $permissionKey);
    }

    private function capabilitiesFromDecision(AuthorizationDecision $decision): array
    {
        $capabilities = $decision->attribute('capabilities', []);
        return is_array($capabilities) ? $capabilities : [];
    }
}
