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

use Chill\MainBundle\Entity\User;
use Chill\MainBundle\Entity\Workflow\EntityWorkflow;
use Chill\MainBundle\Entity\Workflow\EntityWorkflowSendView;
use Chill\MainBundle\Repository\EntityWorkflowSendViewRepository;
use Chill\MainBundle\Workflow\EntityWorkflowMarkingStore;
use Chill\MainBundle\Workflow\Messenger\PostPublicViewMessage;
use Chill\MainBundle\Workflow\Messenger\PostPublicViewMessageHandler;
use Chill\MainBundle\Workflow\WorkflowTransitionContextDTO;
use Chill\ThirdPartyBundle\Entity\ThirdParty;
use Doctrine\ORM\EntityManagerInterface;
use PHPUnit\Framework\TestCase;
use Prophecy\PhpUnit\ProphecyTrait;
use Psr\Log\NullLogger;
use Symfony\Component\Workflow\DefinitionBuilder;
use Symfony\Component\Workflow\Metadata\InMemoryMetadataStore;
use Symfony\Component\Workflow\Registry;
use Symfony\Component\Workflow\SupportStrategy\WorkflowSupportStrategyInterface;
use Symfony\Component\Workflow\Transition;
use Symfony\Component\Workflow\Workflow;
use Symfony\Component\Workflow\WorkflowInterface;

/**
 * @internal
 *
 * @coversNothing
 */
class PostPublicViewMessageHandlerTest extends TestCase
{
    use ProphecyTrait;

    private function buildRegistry(): Registry
    {
        $builder = new DefinitionBuilder();
        $builder
            ->setInitialPlaces(['initial'])
            ->addPlaces(['initial', 'waiting_for_views', 'waiting_for_views_transition_unavailable', 'post_view'])
            ->addTransitions([
                new Transition('post_view', 'waiting_for_views', 'post_view'),
            ])
            ->setMetadataStore(
                new InMemoryMetadataStore(
                    placesMetadata: [
                        'waiting_for_views' => [
                            'isSentExternal' => true,
                            'onExternalView' => 'post_view',
                        ],
                        'waiting_for_views_transition_unavailable' => [
                            'isSentExternal' => true,
                            'onExternalView' => 'post_view',
                        ],
                    ]
                )
            );

        $workflow = new Workflow($builder->build(), new EntityWorkflowMarkingStore(), name: 'dummy');
        $registry = new Registry();
        $registry
            ->addWorkflow($workflow, new class () implements WorkflowSupportStrategyInterface {
                public function supports(WorkflowInterface $workflow, object $subject): bool
                {
                    return true;
                }
            });

        return $registry;
    }

    private function buildEntityManager(bool $mustBeFlushed = false): EntityManagerInterface
    {
        $entityManager = $this->prophesize(EntityManagerInterface::class);
        $flush = $entityManager->flush();
        $entityManager->clear()->shouldBeCalled();

        if ($mustBeFlushed) {
            $flush->shouldBeCalled();
        }

        return $entityManager->reveal();
    }

    public function testHandleTransitionToPostViewSuccessful(): void
    {
        $entityWorkflow = new EntityWorkflow();
        $entityWorkflow->setWorkflowName('dummy');
        $dto = new WorkflowTransitionContextDTO($entityWorkflow);
        $dto->futureDestineeThirdParties = [new ThirdParty()];
        $entityWorkflow->setStep('waiting_for_views', $dto, 'to_waiting_for_views', new \DateTimeImmutable(), new User());
        $send = $entityWorkflow->getCurrentStep()->getSends()->first();
        $view = new EntityWorkflowSendView($send, new \DateTimeImmutable(), '127.0.0.1');

        $repository = $this->prophesize(EntityWorkflowSendViewRepository::class);
        $repository->find(6)->willReturn($view);

        $handler = new PostPublicViewMessageHandler($repository->reveal(), $this->buildRegistry(), new NullLogger(), $this->buildEntityManager(true));

        $handler(new PostPublicViewMessage(6));

        self::assertEquals('post_view', $entityWorkflow->getStep());
    }

    public function testHandleTransitionToPostViewAlreadyMoved(): void
    {
        $entityWorkflow = new EntityWorkflow();
        $entityWorkflow->setWorkflowName('dummy');
        $dto = new WorkflowTransitionContextDTO($entityWorkflow);
        $dto->futureDestineeThirdParties = [new ThirdParty()];
        $entityWorkflow->setStep('waiting_for_views', $dto, 'to_waiting_for_views', new \DateTimeImmutable(), new User());
        $send = $entityWorkflow->getCurrentStep()->getSends()->first();
        $view = new EntityWorkflowSendView($send, new \DateTimeImmutable(), '127.0.0.1');
        // move again
        $dto = new WorkflowTransitionContextDTO($entityWorkflow);
        $entityWorkflow->setStep('post_view', $dto, 'post_view', new \DateTimeImmutable(), new User());
        $lastStep = $entityWorkflow->getCurrentStep();


        $repository = $this->prophesize(EntityWorkflowSendViewRepository::class);
        $repository->find(6)->willReturn($view);

        $handler = new PostPublicViewMessageHandler($repository->reveal(), $this->buildRegistry(), new NullLogger(), $this->buildEntityManager());

        $handler(new PostPublicViewMessage(6));

        self::assertEquals('post_view', $entityWorkflow->getStep());
        self::assertSame($lastStep, $entityWorkflow->getCurrentStep());
    }

    public function testHandleTransitionToPostViewBlocked(): void
    {
        $entityWorkflow = new EntityWorkflow();
        $entityWorkflow->setWorkflowName('dummy');
        $dto = new WorkflowTransitionContextDTO($entityWorkflow);
        $dto->futureDestineeThirdParties = [new ThirdParty()];
        $entityWorkflow->setStep('waiting_for_views_transition_unavailable', $dto, 'to_waiting_for_views', new \DateTimeImmutable(), new User());
        $send = $entityWorkflow->getCurrentStep()->getSends()->first();
        $view = new EntityWorkflowSendView($send, new \DateTimeImmutable(), '127.0.0.1');

        $repository = $this->prophesize(EntityWorkflowSendViewRepository::class);
        $repository->find(6)->willReturn($view);

        $handler = new PostPublicViewMessageHandler($repository->reveal(), $this->buildRegistry(), new NullLogger(), $this->buildEntityManager());

        $handler(new PostPublicViewMessage(6));

        self::assertEquals('waiting_for_views_transition_unavailable', $entityWorkflow->getStep());
    }
}
