<?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\Workflow\Messenger;

use Chill\MainBundle\Repository\EntityWorkflowSendViewRepository;
use Chill\MainBundle\Workflow\WorkflowTransitionContextDTO;
use Doctrine\ORM\EntityManagerInterface;
use Psr\Log\LoggerInterface;
use Symfony\Component\Messenger\Handler\MessageHandlerInterface;
use Symfony\Component\Workflow\Registry;

/**
 * Handle the behaviour after a EntityWorkflowSentView was created.
 *
 * This handler apply a transition if the workflow's configuration defines one.
 */
final readonly class PostPublicViewMessageHandler implements MessageHandlerInterface
{
    private const LOG_PREFIX = '[PostPublicViewMessageHandler] ';

    private const TRANSITION_ON_VIEW = 'onExternalView';

    public function __construct(
        private EntityWorkflowSendViewRepository $sendViewRepository,
        private Registry $registry,
        private LoggerInterface $logger,
        private EntityManagerInterface $entityManager,
    ) {}

    public function __invoke(PostPublicViewMessage $message): void
    {
        $view = $this->sendViewRepository->find($message->entityWorkflowSendViewId);

        if (null === $view) {
            throw new \RuntimeException("EntityworkflowSendViewId {$message->entityWorkflowSendViewId} not found");
        }

        $step = $view->getSend()->getEntityWorkflowStep();
        $entityWorkflow = $step->getEntityWorkflow();

        if ($step !== $entityWorkflow->getCurrentStep()) {
            $this->logger->info(self::LOG_PREFIX."Do not handle view, as the current's step for the associated EntityWorkflow has already moved", [
                'id' => $message->entityWorkflowSendViewId,
                'entityWorkflow' => $entityWorkflow->getId(),
            ]);

            $this->entityManager->clear();

            return;
        }

        $workflow = $this->registry->get($entityWorkflow, $entityWorkflow->getWorkflowName());
        $metadata = $workflow->getMetadataStore();

        foreach ($workflow->getMarking($entityWorkflow)->getPlaces() as $place => $key) {
            $placeMetadata = $metadata->getPlaceMetadata($place);
            if (array_key_exists(self::TRANSITION_ON_VIEW, $placeMetadata)) {
                if ($workflow->can($entityWorkflow, $placeMetadata[self::TRANSITION_ON_VIEW])) {
                    $dto = new WorkflowTransitionContextDTO($entityWorkflow);
                    $dto->transition = $workflow->getEnabledTransition($entityWorkflow, $placeMetadata[self::TRANSITION_ON_VIEW]);

                    $this->logger->info(
                        self::LOG_PREFIX.'Apply transition to workflow after a first view',
                        ['transition' => $placeMetadata[self::TRANSITION_ON_VIEW], 'entityWorkflowId' => $entityWorkflow->getId(),
                            'viewId' => $view->getId()]
                    );

                    $workflow->apply($entityWorkflow, $placeMetadata[self::TRANSITION_ON_VIEW], [
                        'context' => $dto,
                        'transitionAt' => $view->getViewAt(),
                        'transition' => $placeMetadata[self::TRANSITION_ON_VIEW],
                    ]);

                    $this->entityManager->flush();
                    $this->entityManager->clear();

                    return;
                }
                $this->logger->info(self::LOG_PREFIX.'Not able to apply this transition', ['transition' => $placeMetadata[self::TRANSITION_ON_VIEW],
                    'entityWorkflowId' => $entityWorkflow->getId(), 'viewId' => $view->getId()]);

            }
        }

        $this->logger->info(
            self::LOG_PREFIX.'No transition applyied for this entityWorkflow after a view',
            ['entityWorkflowId' => $entityWorkflow->getId(), 'viewId' => $view->getId()]
        );

        $this->entityManager->clear();
    }
}
