<?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\TaskBundle\Controller;

use Chill\TaskBundle\Entity\AbstractTask;
use Chill\TaskBundle\Event\UI\UIEvent;
use Chill\TaskBundle\Repository\SingleTaskRepository;
use Chill\TaskBundle\Security\Authorization\TaskVoter;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
use Symfony\Component\Form\Extension\Core\Type\SubmitType;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;
use Symfony\Component\Workflow\Registry;
use Symfony\Component\Workflow\Transition;
use Symfony\Contracts\Translation\TranslatorInterface;
use function array_filter;
use function array_values;

/**
 * Class TaskController.
 */
class TaskController extends AbstractController
{
    /**
     * Apply a transition to a task.
     *
     * @param string $kind
     * @param int    $taskId
     * @param string $transition
     *
     * @return Response
     */
    #[Route(path: '/{_locale}/task/transition/{kind}/{taskId}/{transition}', name: 'chill_task_task_transition')]
    public function applyTransitionAction(
        $kind,
        $taskId,
        $transition,
        SingleTaskRepository $singleTaskRepository,
        Registry $registry,
        EntityManagerInterface $em,
        Request $request,
        TranslatorInterface $translator,
        EventDispatcherInterface $eventDispatcher,
    ) {
        switch ($kind) {
            case 'single-task':
                $task = $singleTaskRepository
                    ->find($taskId);
                $defaultReturnPath = $this->generateUrl(
                    'chill_task_single_task_show',
                    [
                        'id' => $task->getId(),
                        'list_params' => $request->query->all('list_params'),
                    ]
                );
                null === $task->getCourse() ? $defaultTemplate = '@ChillTask/SingleTask/Person/transition.html.twig' : $defaultTemplate = '@ChillTask/SingleTask/AccompanyingCourse/transition.html.twig';

                break;

            default:
                return new Response(
                    "The type '{$kind}' is not implemented",
                    Response::HTTP_BAD_REQUEST
                );
        }

        if (null === $task) {
            throw $this->createNotFoundException("task with id '{$taskId}' and type '{$kind}' does not exists");
        }

        $workflow = $registry->get($task);

        if (!$workflow->can($task, $transition)) {
            throw $this->createAccessDeniedException('You are not allowed to apply this transition');
        }
        $transitionInstance = \array_values( // array_values needed to reset keys (array_filter preserves keys)
            \array_filter(
                $workflow->getEnabledTransitions($task),
                static fn (Transition $t) => $t->getName() === $transition
            )
        )[0];

        $form = $this->createTransitionForm($task);

        $form->handleRequest($request);

        if ($form->isSubmitted() && $form->isValid()) {
            if ($workflow->can($task, $transition)) {
                $workflow->apply($task, $transition);

                $em->flush();

                $this->addFlash('success', $translator->trans('The transition is successfully applied'));
            } else {
                $this->addFlash('error', $translator->trans('The transition could not be applied'));
            }

            return $this->redirect($defaultReturnPath);
        }
        $event = (new UIEvent($kind, $task))
            ->setForm($form)
            ->setTransition($transitionInstance);

        $eventDispatcher->dispatch($event, UIEvent::SHOW_TRANSITION_PAGE);

        if ($event->hasResponse()) {
            return $event->getResponse();
        }
        // we simply check that the user can see the task. Other ACL checks
        // should be performed using `guard` events.
        $this->denyAccessUnlessGranted(TaskVoter::SHOW, $task);

        return $this->render($defaultTemplate, [
            'task' => $task,
            'form' => $form->createView(),
            'transition' => $transitionInstance,
        ]);
    }

    /**
     * @return \Symfony\Component\Form\FormInterface
     */
    protected function createTransitionForm(AbstractTask $task)
    {
        $builder = $this->createFormBuilder($task);
        $builder->add('submit', SubmitType::class);

        return $builder->getForm();
    }
}
