<?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\EventSubscriber;

use Chill\DocStoreBundle\Entity\StoredObject;
use Chill\DocStoreBundle\Entity\StoredObjectPointInTime;
use Chill\DocStoreBundle\Entity\StoredObjectPointInTimeReasonEnum;
use Chill\DocStoreBundle\Service\StoredObjectRestoreInterface;
use Chill\MainBundle\Entity\Workflow\EntityWorkflow;
use Chill\MainBundle\Workflow\EntityWorkflowManager;
use Chill\MainBundle\Workflow\EntityWorkflowMarkingStore;
use Chill\MainBundle\Workflow\EventSubscriber\OnCancelRestoreDocumentToEditableEventSubscriber;
use Chill\MainBundle\Workflow\WorkflowTransitionContextDTO;
use PHPUnit\Framework\TestCase;
use Symfony\Component\EventDispatcher\EventDispatcher;
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 OnCancelRestoreDocumentToEditableEventSubscriberTest extends TestCase
{
    private function buildRegistry(StoredObjectRestoreInterface $storedObjectRestore, ?StoredObject $storedObject): Registry
    {
        $builder = new DefinitionBuilder(
            ['initial', 'intermediate', 'final', 'cancel'],
            [
                new Transition('to_intermediate', ['initial'], ['intermediate']),
                new Transition('intermediate_to_final', ['intermediate'], ['final']),
                new Transition('to_final', ['initial'], ['final']),
                new Transition('to_cancel', ['initial'], ['cancel']),
            ]
        );

        $builder->setMetadataStore(
            new InMemoryMetadataStore(
                placesMetadata: [
                    'final' => ['isFinal' => true],
                    'cancel' => ['isFinal' => true, 'isFinalPositive' => false],
                ]
            )
        );

        $registry = new Registry();
        $workflow = new Workflow($builder->build(), new EntityWorkflowMarkingStore(), $eventDispatcher = new EventDispatcher(), 'dummy');

        $manager = $this->createMock(EntityWorkflowManager::class);
        $manager->method('getAssociatedStoredObject')->willReturn($storedObject);

        $eventSubscriber = new OnCancelRestoreDocumentToEditableEventSubscriber(
            $registry,
            $manager,
            $storedObjectRestore
        );
        $eventDispatcher->addSubscriber($eventSubscriber);

        $registry->addWorkflow($workflow, new class () implements WorkflowSupportStrategyInterface {
            public function supports(WorkflowInterface $workflow, object $subject): bool
            {
                return true;
            }
        });

        return $registry;
    }

    public function testOnCancelRestoreDocumentToEditableExpectsRestoring(): void
    {
        $storedObject = new StoredObject();
        $version = $storedObject->registerVersion();
        new StoredObjectPointInTime($version, StoredObjectPointInTimeReasonEnum::KEEP_BEFORE_CONVERSION);
        $storedObject->registerVersion();

        $restore = $this->createMock(StoredObjectRestoreInterface::class);
        $restore->expects($this->once())->method('restore')->with($version);

        $registry = $this->buildRegistry($restore, $storedObject);
        $entityWorkflow = (new EntityWorkflow())->setWorkflowName('dummy');

        $workflow = $registry->get($entityWorkflow, $entityWorkflow->getWorkflowName());
        $context = new WorkflowTransitionContextDTO($entityWorkflow);

        $workflow->apply($entityWorkflow, 'to_cancel', [
            'context' => $context,
            'transition' => 'to_cancel',
            'transitionAt' => new \DateTimeImmutable('now'),
        ]);
    }

    public function testOnCancelRestoreDocumentDoNotExpectRestoring(): void
    {
        $storedObject = new StoredObject();
        $version = $storedObject->registerVersion();
        new StoredObjectPointInTime($version, StoredObjectPointInTimeReasonEnum::KEEP_BEFORE_CONVERSION);
        $storedObject->registerVersion();

        $restore = $this->createMock(StoredObjectRestoreInterface::class);
        $restore->expects($this->never())->method('restore')->withAnyParameters();

        $registry = $this->buildRegistry($restore, $storedObject);
        $entityWorkflow = (new EntityWorkflow())->setWorkflowName('dummy');

        $workflow = $registry->get($entityWorkflow, $entityWorkflow->getWorkflowName());
        $context = new WorkflowTransitionContextDTO($entityWorkflow);

        $workflow->apply($entityWorkflow, 'to_intermediate', [
            'context' => $context,
            'transition' => 'to_intermediate',
            'transitionAt' => new \DateTimeImmutable('now'),
        ]);

        $workflow->apply($entityWorkflow, 'intermediate_to_final', [
            'context' => $context,
            'transition' => 'intermediate_to_final',
            'transitionAt' => new \DateTimeImmutable('now'),
        ]);
    }

    public function testOnCancelRestoreDocumentToEditableToCancelStoredObjectWithoutKepts(): void
    {
        $storedObject = new StoredObject();
        $storedObject->registerVersion();

        $restore = $this->createMock(StoredObjectRestoreInterface::class);
        $restore->expects($this->never())->method('restore')->withAnyParameters();

        $registry = $this->buildRegistry($restore, $storedObject);
        $entityWorkflow = (new EntityWorkflow())->setWorkflowName('dummy');

        $workflow = $registry->get($entityWorkflow, $entityWorkflow->getWorkflowName());
        $context = new WorkflowTransitionContextDTO($entityWorkflow);

        $workflow->apply($entityWorkflow, 'to_cancel', [
            'context' => $context,
            'transition' => 'to_cancel',
            'transitionAt' => new \DateTimeImmutable('now'),
        ]);
    }
}
