<?php

declare(strict_types=1);

/*
 * Chill is a software for social workers
 *
 * For the full copyright and license information, please view
 * the LICENSE file that was distributed with this source code.
 */

namespace Chill\MainBundle\Security;

use Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface;
use Symfony\Component\Security\Core\Role\Role;
use Symfony\Component\Security\Core\Role\RoleHierarchyInterface;

/**
 * Helper which traverse all role to find parents.
 */
class ParentRoleHelper
{
    /**
     * The role in a hierarchy, given by the parameter
     * `security.role_hierarchy.roles` from the container.
     *
     * @var string[]
     */
    protected array $hierarchy;

    public function __construct(
        protected RoleHierarchyInterface $roleHierarchy,
        ParameterBagInterface $parameterBag,
    ) {
        $this->hierarchy = $parameterBag->get('security.role_hierarchy.roles');
    }

    /**
     * Return all the role which give access to the given role. Only the role
     * which are registered into Chill are taken into account.
     *
     * The array contains always the current $role (which give access to himself)
     *
     * @return string[] the role which give access to the given $role
     */
    public function getParentRoles(string $role): array
    {
        $parentRoles = [$role];
        // transform the roles from role hierarchy from string to Role
        $roles = \array_keys($this->hierarchy);

        foreach ($roles as $r) {
            $childRoles = $this->roleHierarchy->getReachableRoleNames([$r]);

            if (\in_array($role, $childRoles, true)) {
                $parentRoles[] = $r;
            }
        }

        return $parentRoles;
    }

    /**
     * Test if a parent role may give access to a given child role.
     *
     * @param string $childRole  The role we want to test if he is reachable
     * @param string $parentRole The role which should give access to $childRole
     *
     * @return bool true if the child role is granted by parent role
     */
    public function isRoleReached($childRole, $parentRole): bool
    {
        $reachableRoles = $this->roleHierarchy
            ->getReachableRoleNames([$parentRole]);

        return \in_array($childRole, $reachableRoles, true);
    }
}
