*
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see .
 */
namespace Chill\EventBundle\Security\Authorization;
use Chill\MainBundle\Security\Authorization\AbstractChillVoter;
use Chill\MainBundle\Security\ProvideRoleHierarchyInterface;
use Chill\EventBundle\Entity\Event;
use Chill\MainBundle\Security\Authorization\AuthorizationHelper;
use Chill\MainBundle\Entity\User;
use Chill\PersonBundle\Entity\Person;
use Chill\PersonBundle\Security\Authorization\PersonVoter;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
use Symfony\Component\Security\Core\Authorization\AccessDecisionManagerInterface;
use Symfony\Component\Security\Core\Role\Role;
use Psr\Log\LoggerInterface;
/**
 * Description of EventVoter
 *
 * @author Mathieu Jaumotte 
 * @author Champs Libres 
 */
class EventVoter extends AbstractChillVoter implements ProvideRoleHierarchyInterface
{
    const SEE = 'CHILL_EVENT_SEE';
    const SEE_DETAILS = 'CHILL_EVENT_SEE_DETAILS';
    const CREATE = 'CHILL_EVENT_CREATE';
    const UPDATE = 'CHILL_EVENT_UPDATE';
    
    const ROLES = [
        self::SEE,
        self::SEE_DETAILS,
        self::CREATE,
        self::UPDATE
    ];
    
    /**
     * @var AuthorizationHelper
     */
    protected $authorizationHelper;
    
    /**
     * @var AccessDecisionManagerInterface
     */
    protected $accessDecisionManager;
    
    /**
     * @var LoggerInterface
     */
    protected $logger;
    
    public function __construct(
        AccessDecisionManagerInterface $accessDecisionManager,
        AuthorizationHelper $authorizationHelper,
        LoggerInterface $logger
    )
    {
        $this->accessDecisionManager = $accessDecisionManager;
        $this->authorizationHelper = $authorizationHelper;
        $this->logger = $logger;
    }
    
    public function supports($attribute, $subject)
    {
        return ($subject instanceof Event && in_array($attribute, self::ROLES))
            ||
            ($subject instanceof Person && \in_array($attribute, [ self::CREATE, self::SEE ]))
            ||
            (NULL === $subject && $attribute === self::SEE )
            ;
    }
    
    /**
     *
     * @param string $attribute
     * @param Event $subject
     * @param TokenInterface $token
     * @return boolean
     */
    protected function voteOnAttribute($attribute, $subject, TokenInterface $token)
    {
        $this->logger->debug(sprintf("Voting from %s class", self::class));
        
        if (!$token->getUser() instanceof User) {
            return false;
        }
        
        if ($subject instanceof Event) {
            return $this->authorizationHelper->userHasAccess($token->getUser(), $subject, $attribute);
        
        } elseif ($subject instanceof Person) {
            return $this->authorizationHelper->userHasAccess($token->getUser(), $subject, $attribute);
        
        } else {
            
            // subject is null. We check that at least one center is reachable
            $centers = $this->authorizationHelper
                ->getReachableCenters($token->getUser(), new Role($attribute));
            
            return count($centers) > 0;
        }
    
        if (!$this->accessDecisionManager->decide($token, [PersonVoter::SEE], $person)) {
            return false;
        }
    
        return $this->authorizationHelper->userHasAccess(
            $token->getUser(),
            $subject,
            $attribute
        );
    }
    
    
    public function getRoles()
    {
        return self::ROLES;
    }
    
    public function getRolesWithHierarchy()
    {
        return [
            'Event' => self::ROLES
        ];
    }
    public function getRolesWithoutScope()
    {
        return [];
    }
    
}