<?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\PersonBundle\Security\Authorization;

use Chill\MainBundle\Entity\Center;
use Chill\MainBundle\Security\Authorization\ChillVoterInterface;
use Chill\MainBundle\Security\Authorization\VoterHelperFactoryInterface;
use Chill\MainBundle\Security\Authorization\VoterHelperInterface;
use Chill\MainBundle\Security\ProvideRoleHierarchyInterface;
use Chill\PersonBundle\Entity\Household\Household;
use Chill\PersonBundle\Entity\Household\HouseholdMember;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
use Symfony\Component\Security\Core\Authorization\Voter\Voter;
use Symfony\Component\Security\Core\Security;

class HouseholdVoter extends Voter implements ProvideRoleHierarchyInterface, ChillVoterInterface
{
    final public const EDIT = 'CHILL_PERSON_HOUSEHOLD_EDIT';

    final public const SEE = 'CHILL_PERSON_HOUSEHOLD_SEE';

    /**
     * @deprecated use @see{self::SEE} instead
     */
    final public const SHOW = self::SEE;

    final public const STATS = 'CHILL_PERSON_HOUSEHOLD_STATS';

    private const ALL = [
        self::SEE,
        self::EDIT,
        self::STATS,
    ];

    private readonly VoterHelperInterface $helper;

    public function __construct(private readonly Security $security, VoterHelperFactoryInterface $voterHelperFactory)
    {
        $this->helper = $voterHelperFactory
            ->generate(self::class)
            ->addCheckFor(Center::class, [self::STATS])
            ->build();
    }

    public function getRoles(): array
    {
        return [self::STATS];
    }

    public function getRolesWithHierarchy(): array
    {
        return ['Household' => $this->getRoles()];
    }

    public function getRolesWithoutScope(): array
    {
        return $this->getRoles();
    }

    protected function supports($attribute, $subject)
    {
        return ($subject instanceof Household
            && \in_array($attribute, self::ALL, true))
            || $this->helper->supports($attribute, $subject);
    }

    protected function voteOnAttribute($attribute, $subject, TokenInterface $token): bool
    {
        return match ($attribute) {
            self::SEE => $this->checkAssociatedMembersRole($subject, PersonVoter::SEE),
            self::EDIT => $this->checkAssociatedMembersRole($subject, PersonVoter::UPDATE),
            self::STATS => $this->helper->voteOnAttribute($attribute, $subject, $token),
            default => throw new \UnexpectedValueException('attribute not supported'),
        };
    }

    private function checkAssociatedMembersRole(Household $household, string $attribute): bool
    {
        foreach ($household->getCurrentMembers()->map(static fn (HouseholdMember $member) => $member->getPerson()) as $person) {
            if ($this->security->isGranted($attribute, $person)) {
                return true;
            }
        }

        return false;
    }
}
