<?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\Form\Type;

use Chill\MainBundle\Entity\Center;
use Chill\MainBundle\Entity\User;
use Chill\MainBundle\Form\Type\DataTransformer\ScopeTransformer;
use Chill\MainBundle\Security\Authorization\AuthorizationHelper;
use Chill\MainBundle\Templating\TranslatableStringHelper;
use Doctrine\Persistence\ObjectManager;
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\Form\FormEvent;
use Symfony\Component\Form\FormEvents;
use Symfony\Component\OptionsResolver\OptionsResolver;
use Symfony\Component\Security\Core\Role\Role;

/**
 * Trait to add an input with reachable scope for a given center and role.
 *
 * Example usage :
 *
 * ```
 * class AbcType extends Symfony\Component\Form\AbstractType
 * {
 *     use AppendScopeChoiceTypeTrait;
 *     protected $authorizationHelper;
 *     protected $translatableStringHelper;
 *     protected $user;
 *     protected $om;
 *
 *     public function __construct(AuthorizationHelper $helper,
 *       TokenStorageInterface $tokenStorage,
 *       TranslatableStringHelper $translatableStringHelper,
 *       ObjectManager $om)
 *     {
 *         $this->authorizationHelper = $helper;
 *         $this->user = $tokenStorage->getToken()->getUser();
 *         $this->translatableStringHelper = $translatableStringHelper;
 *         $this->om = $om;
 *     }
 *
 *     public function buildForm(FormBuilder $builder, array $options)
 *     {
 *           // ... add your form there
 *
 *         // append the scope using FormEvents: PRE_SET_DATA
 *         $this->appendScopeChoices($builder, $options['role'],
 *              $options['center'], $this->user, $this->authorizationHelper,
 *              $this->translatableStringHelper, $this->om);
 *      }
 *
 *      public function configureOptions(OptionsResolver $resolver)
 *      {
 *              // ... add your options
 *
 *         // add an option 'role' and 'center' to your form (optional)
 *         $this->appendScopeChoicesOptions($resolver);
 *      }
 *
 *  }
 * ```
 */
trait AppendScopeChoiceTypeTrait
{
    /**
     * Append a `role` and `center` option to the form.
     *
     * The allowed types are :
     * - Chill\MainBundle\Entity\Center for center
     * - Symfony\Component\Security\Core\Role\Role for role
     */
    public function appendScopeChoicesOptions(OptionsResolver $resolver)
    {
        $resolver
            ->setRequired(['center', 'role'])
            ->setAllowedTypes('center', Center::class)
            ->setAllowedTypes('role', 'string');
    }

    /**
     * Append a scope choice field, with the scopes reachable by given
     * user for the given role and center.
     *
     * The field is added on event FormEvents::PRE_SET_DATA
     *
     * @param string $name
     */
    protected function appendScopeChoices(
        FormBuilderInterface $builder,
        string $role,
        Center $center,
        User $user,
        AuthorizationHelper $authorizationHelper,
        TranslatableStringHelper $translatableStringHelper,
        ObjectManager $om,
        $name = 'scope',
    ) {
        $reachableScopes = $authorizationHelper
            ->getReachableScopes($user, $role, $center);

        $choices = [];

        foreach ($reachableScopes as $scope) {
            $choices[$scope->getId()] = $translatableStringHelper
                ->localize($scope->getName());
        }

        $dataTransformer = new ScopeTransformer($om);

        $builder->addEventListener(
            FormEvents::PRE_SET_DATA,
            static function (FormEvent $event) use ($choices, $name, $dataTransformer, $builder) {
                $form = $event->getForm();
                $form->add(
                    $builder
                        ->create(
                            $name,
                            ChoiceType::class,
                            [
                                'choices' => array_combine(array_values($choices), array_keys($choices)),
                                'auto_initialize' => false,
                            ]
                        )
                        ->addModelTransformer($dataTransformer)
                        ->getForm()
                );
            }
        );
    }
}
