<?php

namespace App\EventSubscriber;

use App\Entity\Comment;
use App\Entity\Task;
use App\Service\OpenStreetMapClient;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Bundle\SecurityBundle\Security;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\Workflow\Event\EnteredEvent;
use Symfony\Component\Workflow\Event\Event;
use Symfony\Component\Workflow\Event\GuardEvent;
use Symfony\Component\Workflow\Event\TransitionEvent;
use Symfony\Component\Workflow\WorkflowInterface;
use Twig\Environment;

// Surveille la vie des tâches et réalise automatiquement des actions selon les
// circonstances
class TaskLifecycleSubscriber implements EventSubscriberInterface
{
    public function __construct(
        protected EntityManagerInterface $entityManager,
        protected Environment $twig,
        protected OpenStreetMapClient $osmClient,
        protected Security $security,
        protected WorkflowInterface $taskLifecycleStateMachine,
    ) {
    }

    // Cas où la tâche change d’état
    public function onTransition(Event $event): void
    {
        $transition = $event->getTransition();
        $task = $event->getSubject();

        // Gère le verrouillage de la tâche

        $shouldLock = $this->taskLifecycleStateMachine->getMetadataStore()->getTransitionMetadata($transition)['lock'];
        $shouldUnlock = $this->taskLifecycleStateMachine->getMetadataStore()->getTransitionMetadata($transition)['unlock'];

        if ($shouldLock) {
            $task->lock($this->security->getUser());
        } elseif ($shouldUnlock) {
            $task->unlock();
        }

        // Commentaire automatique
        $user = $this->security->getUser();
        $task = $event->getSubject();
        $comment = new Comment();
        $comment->setTask($task);
        $comment->setCreatedBy($user);
        $comment->setContent($this->twig->render(sprintf('comment/%s.md.twig', $transition->getName()), [
            'user' => $user,
            'task' => $task,
        ]));
        $this->entityManager->persist($comment);
        $this->entityManager->flush();
    }

    // Bloque certaines transitions
    public function onGuard(Event $event): void
    {
        $transition = $event->getTransition();
        $task = $event->getSubject();

        $shouldLock = $this->taskLifecycleStateMachine->getMetadataStore()->getTransitionMetadata($transition)['lock'];
        $shouldUnlock = $this->taskLifecycleStateMachine->getMetadataStore()->getTransitionMetadata($transition)['unlock'];

        $subject = $event->getSubject();

        if ($shouldLock and $task->isLocked()) {
            $event->setBlocked(true, 'La tâche est déjà verrouillée.');
        } elseif ($shouldUnlock and !$task->isLocked()) {
            $event->setBlocked(true, 'La tâche n’est pas déjà verrouillée.');
        }

        $event->setBlocked(false);
    }

    // Cas où on commence une tâche
    public function onStart(Event $event): void
    {
        $task = $event->getSubject();

        $task->setStartAt(new \DateTimeImmutable('now'));
    }

    // Cas où on termine une tâche
    public function onFinish(Event $event): void
    {
        $task = $event->getSubject();

        $task->setFinishAt(new \DateTimeImmutable('now'));
    }

    // Cas où une tâche est terminée
    public function onDone(Event $event): void
    {
        $task = $event->getSubject();
        $user = $this->security->getUser();

        $task->setDoneBy($user);

        // Essaie de trouver les changesets correspondants à la tâche
        $task->setChangesetsResult(
            json_encode($this->osmClient->getChangesets(
                // On part du principe que les changesets correspondants sont
                // ceux de l’utilisateur entre le moment où la tâche a été
                // commencée et celui où elle a été terminée
                // TODO C’est pas très précis, on doit pouvoir faire mieux
                // (hashatg avec un id spécifique à l’app dans le commentaire
                // du changeset, c’est moche mais ça marcherait)
                $user->getUsername(),
                $task->getStartAt(),
                $task->getFinishAt()
            ))
        );
    }

    public static function getSubscribedEvents(): array
    {
        return [
            TransitionEvent::getName('task_lifecycle', null) => 'onTransition',
            GuardEvent::getName('task_lifecycle', null) => 'onGuard',
            TransitionEvent::getName('task_lifecycle', Task::TRANSITION_START) => 'onStart',
            TransitionEvent::getName('task_lifecycle', Task::TRANSITION_FINISH) => 'onFinish',
            EnteredEvent::getName('task_lifecycle', Task::STATUS_DONE) => 'onDone',
        ];
    }
}
