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

use Chill\DocStoreBundle\Entity\StoredObject;
use Chill\MainBundle\Entity\Workflow\EntityWorkflowAttachment;
use Chill\MainBundle\Repository\Workflow\EntityWorkflowAttachmentRepository;
use Chill\MainBundle\Workflow\Helper\WorkflowRelatedEntityPermissionHelper;
use Chill\MainBundle\Entity\User;
use Chill\MainBundle\Entity\Workflow\EntityWorkflow;
use Chill\MainBundle\Entity\Workflow\EntityWorkflowSignatureStateEnum;
use Chill\MainBundle\Workflow\EntityWorkflowManager;
use Chill\MainBundle\Workflow\EntityWorkflowMarkingStore;
use Chill\MainBundle\Workflow\WorkflowTransitionContextDTO;
use Chill\PersonBundle\Entity\Person;
use PHPUnit\Framework\TestCase;
use Prophecy\Argument;
use Prophecy\PhpUnit\ProphecyTrait;
use Symfony\Component\Clock\MockClock;
use Symfony\Component\Security\Core\Security;
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\Workflow;
use Symfony\Component\Workflow\WorkflowInterface;

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

    /**
     * @dataProvider provideDataAllowedByWorkflowReadOperation
     *
     * @param list<EntityWorkflow> $entityWorkflows
     */
    public function testAllowedByWorkflowRead(
        array $entityWorkflows,
        User $user,
        string $expected,
        ?\DateTimeImmutable $atDate,
        string $message,
    ): void {
        // all entities must have this workflow name, so we are ok to set it here
        foreach ($entityWorkflows as $entityWorkflow) {
            $entityWorkflow->setWorkflowName('dummy');
        }
        $helper = $this->buildHelper($entityWorkflows, $user, $atDate);

        self::assertEquals($expected, $helper->isAllowedByWorkflowForReadOperation(new \stdClass()), $message);
    }

    public static function provideDataAllowedByWorkflowReadOperation(): iterable
    {
        $entityWorkflow = new EntityWorkflow();
        $dto = new WorkflowTransitionContextDTO($entityWorkflow);
        $entityWorkflow->setStep('test', $dto, 'to_test', new \DateTimeImmutable(), new User());

        yield [[$entityWorkflow], new User(), WorkflowRelatedEntityPermissionHelper::ABSTAIN, new \DateTimeImmutable(),
            'abstain because the user is not present as a dest user'];

        $entityWorkflow = new EntityWorkflow();
        $dto = new WorkflowTransitionContextDTO($entityWorkflow);
        $dto->futureDestUsers[] = $user = new User();
        $entityWorkflow->setStep('test', $dto, 'to_test', new \DateTimeImmutable(), new User());

        yield [[$entityWorkflow], $user, WorkflowRelatedEntityPermissionHelper::FORCE_GRANT, new \DateTimeImmutable(),
            'force grant because the user is a current user'];

        $entityWorkflow = new EntityWorkflow();
        $dto = new WorkflowTransitionContextDTO($entityWorkflow);
        $dto->futureDestUsers[] = $user = new User();
        $entityWorkflow->setStep('test', $dto, 'to_test', new \DateTimeImmutable(), new User());

        $dto = new WorkflowTransitionContextDTO($entityWorkflow);
        $dto->futureDestUsers[] = new User();
        $entityWorkflow->setStep('test', $dto, 'to_test', new \DateTimeImmutable(), new User());

        yield [[$entityWorkflow], $user, WorkflowRelatedEntityPermissionHelper::FORCE_GRANT, new \DateTimeImmutable(),
            'force grant because the user was a previous user'];

        $entityWorkflow = new EntityWorkflow();
        $dto = new WorkflowTransitionContextDTO($entityWorkflow);
        $dto->futurePersonSignatures[] = new Person();
        $entityWorkflow->setStep('test', $dto, 'to_test', new \DateTimeImmutable(), new User());

        yield [[$entityWorkflow], new User(), WorkflowRelatedEntityPermissionHelper::FORCE_GRANT, new \DateTimeImmutable(),
            'force grant because there is a signature for person'];

        $entityWorkflow = new EntityWorkflow();
        $dto = new WorkflowTransitionContextDTO($entityWorkflow);
        $dto->futurePersonSignatures[] = new Person();
        $entityWorkflow->setStep('test', $dto, 'to_test', new \DateTimeImmutable(), new User());
        $signature = $entityWorkflow->getCurrentStep()->getSignatures()->first();
        $signature->setState(EntityWorkflowSignatureStateEnum::SIGNED)->setStateDate(new \DateTimeImmutable('2024-01-01T12:00:00'));

        yield [[$entityWorkflow], new User(), WorkflowRelatedEntityPermissionHelper::ABSTAIN, new \DateTimeImmutable('2024-01-10T12:00:00'),
            'abstain because there is a signature for person, already signed, and for a long time ago'];

        $entityWorkflow = new EntityWorkflow();
        $dto = new WorkflowTransitionContextDTO($entityWorkflow);
        $dto->futurePersonSignatures[] = new Person();
        $entityWorkflow->setStep('test', $dto, 'to_test', new \DateTimeImmutable(), new User());
        $signature = $entityWorkflow->getCurrentStep()->getSignatures()->first();
        $signature->setState(EntityWorkflowSignatureStateEnum::SIGNED)
            ->setStateDate(new \DateTimeImmutable('2024-01-01T12:00:00'));

        yield [[$entityWorkflow], new User(), WorkflowRelatedEntityPermissionHelper::FORCE_GRANT,
            new \DateTimeImmutable('2024-01-01T12:30:00'),
            'force grant because there is a signature for person, already signed, a short time ago'];
    }

    /**
     * @dataProvider provideDataAllowedByWorkflowWriteOperation
     *
     * @param list<EntityWorkflow> $entityWorkflows
     */
    public function testAllowedByWorkflowWrite(
        array $entityWorkflows,
        User $user,
        string $expected,
        ?\DateTimeImmutable $atDate,
        string $message,
    ): void {
        // all entities must have this workflow name, so we are ok to set it here
        foreach ($entityWorkflows as $entityWorkflow) {
            $entityWorkflow->setWorkflowName('dummy');
        }
        $helper = $this->buildHelper($entityWorkflows, $user, $atDate);

        self::assertEquals($expected, $helper->isAllowedByWorkflowForWriteOperation(new \stdClass()), $message);
    }

    public static function provideDataAllowedByWorkflowWriteOperation(): iterable
    {
        $entityWorkflow = new EntityWorkflow();
        $dto = new WorkflowTransitionContextDTO($entityWorkflow);
        $entityWorkflow->setStep('test', $dto, 'to_test', new \DateTimeImmutable(), new User());

        yield [[], new User(), WorkflowRelatedEntityPermissionHelper::ABSTAIN, new \DateTimeImmutable(),
            'abstain because there is no workflow'];

        yield [[$entityWorkflow], new User(), WorkflowRelatedEntityPermissionHelper::FORCE_DENIED, new \DateTimeImmutable(),
            'force deny because the user is not present as a dest user'];

        $entityWorkflow = new EntityWorkflow();
        $dto = new WorkflowTransitionContextDTO($entityWorkflow);
        $dto->futureDestUsers[] = $user = new User();
        $entityWorkflow->setStep('test', $dto, 'to_test', new \DateTimeImmutable(), new User());

        yield [[$entityWorkflow], $user, WorkflowRelatedEntityPermissionHelper::FORCE_GRANT, new \DateTimeImmutable(),
            'force grant because the user is a current user'];

        $entityWorkflow = new EntityWorkflow();
        $dto = new WorkflowTransitionContextDTO($entityWorkflow);
        $dto->futureDestUsers[] = $user = new User();
        $entityWorkflow->setStep('test', $dto, 'to_test', new \DateTimeImmutable(), new User());

        $dto = new WorkflowTransitionContextDTO($entityWorkflow);
        $dto->futureDestUsers[] = new User();
        $entityWorkflow->setStep('test', $dto, 'to_test', new \DateTimeImmutable(), new User());

        yield [[$entityWorkflow], $user, WorkflowRelatedEntityPermissionHelper::FORCE_GRANT, new \DateTimeImmutable(),
            'force grant because the user was a previous user'];

        yield [[$entityWorkflow], new User(), WorkflowRelatedEntityPermissionHelper::FORCE_DENIED, new \DateTimeImmutable(),
            'force denied because the user was not a previous user'];

        $entityWorkflow = new EntityWorkflow();
        $dto = new WorkflowTransitionContextDTO($entityWorkflow);
        $dto->futureDestUsers[] = $user = new User();
        $entityWorkflow->setStep('test', $dto, 'to_test', new \DateTimeImmutable(), new User());
        $dto = new WorkflowTransitionContextDTO($entityWorkflow);
        $entityWorkflow->setStep('final_positive', $dto, 'to_final_positive', new \DateTimeImmutable(), new User());
        $entityWorkflow->getCurrentStep()->setIsFinal(true);

        yield [[$entityWorkflow], $user, WorkflowRelatedEntityPermissionHelper::FORCE_DENIED, new \DateTimeImmutable(),
            'force denied: user was a previous user, but it is finalized positive'];

        $entityWorkflow = new EntityWorkflow();
        $dto = new WorkflowTransitionContextDTO($entityWorkflow);
        $dto->futureDestUsers[] = $user = new User();
        $entityWorkflow->setStep('test', $dto, 'to_test', new \DateTimeImmutable());
        $dto = new WorkflowTransitionContextDTO($entityWorkflow);
        $entityWorkflow->setStep('final_negative', $dto, 'to_final_negative', new \DateTimeImmutable());
        $entityWorkflow->getCurrentStep()->setIsFinal(true);

        yield [[$entityWorkflow], $user, WorkflowRelatedEntityPermissionHelper::ABSTAIN, new \DateTimeImmutable(),
            'abstain: user was a previous user, it is finalized, but finalized negative'];

        $entityWorkflow = new EntityWorkflow();
        $dto = new WorkflowTransitionContextDTO($entityWorkflow);
        $dto->futureDestUsers[] = $user = new User();
        $entityWorkflow->setStep('test', $dto, 'to_test', new \DateTimeImmutable(), new User());
        $dto = new WorkflowTransitionContextDTO($entityWorkflow);
        $dto->futurePersonSignatures[] = new Person();
        $entityWorkflow->setStep('test', $dto, 'to_test', new \DateTimeImmutable(), new User());
        $signature = $entityWorkflow->getCurrentStep()->getSignatures()->first();
        $signature->setState(EntityWorkflowSignatureStateEnum::SIGNED)->setStateDate(new \DateTimeImmutable());

        yield [[$entityWorkflow], $user, WorkflowRelatedEntityPermissionHelper::FORCE_DENIED, new \DateTimeImmutable(),
            'force denied because there is a signature'];

        $entityWorkflow = new EntityWorkflow();
        $dto = new WorkflowTransitionContextDTO($entityWorkflow);
        $dto->futureDestUsers[] = $user = new User();
        $entityWorkflow->setStep('test', $dto, 'to_test', new \DateTimeImmutable(), new User());
        $dto = new WorkflowTransitionContextDTO($entityWorkflow);
        $dto->futurePersonSignatures[] = new Person();
        $entityWorkflow->setStep('test', $dto, 'to_test', new \DateTimeImmutable(), new User());

        yield [[$entityWorkflow], $user, WorkflowRelatedEntityPermissionHelper::FORCE_GRANT, new \DateTimeImmutable(),
            'force grant: there is a signature, but still pending'];

        $entityWorkflow = new EntityWorkflow();
        $dto = new WorkflowTransitionContextDTO($entityWorkflow);
        $dto->futureDestUsers[] = $user = new User();
        $entityWorkflow->setStep('test', $dto, 'to_test', new \DateTimeImmutable(), new User());
        $dto = new WorkflowTransitionContextDTO($entityWorkflow);
        $dto->futurePersonSignatures[] = new Person();
        $entityWorkflow->setStep('test', $dto, 'to_test', new \DateTimeImmutable(), new User());
        $signature = $entityWorkflow->getCurrentStep()->getSignatures()->first();
        $signature->setState(EntityWorkflowSignatureStateEnum::SIGNED)->setStateDate(new \DateTimeImmutable());
        $dto = new WorkflowTransitionContextDTO($entityWorkflow);
        $entityWorkflow->setStep('final_negative', $dto, 'to_final_negative', new \DateTimeImmutable(), new User());
        $entityWorkflow->getCurrentStep()->setIsFinal(true);

        yield [[$entityWorkflow], $user, WorkflowRelatedEntityPermissionHelper::ABSTAIN, new \DateTimeImmutable(),
            'abstain: there is a signature on a canceled workflow'];

        $entityWorkflow = new EntityWorkflow();
        $dto = new WorkflowTransitionContextDTO($entityWorkflow);
        $dto->futureDestUsers[] = $user = new User();
        $entityWorkflow->setStep('sent_external', $dto, 'to_sent_external', new \DateTimeImmutable(), $user);

        yield [[$entityWorkflow], $user, WorkflowRelatedEntityPermissionHelper::FORCE_DENIED, new \DateTimeImmutable(), 'force denied: the workflow is sent to an external user'];
    }

    public function testNoWorkflow(): void
    {
        $helper = $this->buildHelper([], new User(), null);

        self::assertEquals(WorkflowRelatedEntityPermissionHelper::ABSTAIN, $helper->isAllowedByWorkflowForWriteOperation(new \stdClass()));
        self::assertEquals(WorkflowRelatedEntityPermissionHelper::ABSTAIN, $helper->isAllowedByWorkflowForReadOperation(new \stdClass()));
    }

    /**
     * @param list<EntityWorkflow> $entityWorkflows
     */
    private function buildHelper(array $entityWorkflows, User $user, ?\DateTimeImmutable $atDateTime): WorkflowRelatedEntityPermissionHelper
    {
        $security = $this->prophesize(Security::class);
        $security->getUser()->willReturn($user);

        $entityWorkflowManager = $this->prophesize(EntityWorkflowManager::class);
        $entityWorkflowManager->findByRelatedEntity(Argument::type('object'))->willReturn($entityWorkflows);

        $repository = $this->prophesize(EntityWorkflowAttachmentRepository::class);
        $repository->findByStoredObject(Argument::type(StoredObject::class))->willReturn([]);

        return new WorkflowRelatedEntityPermissionHelper($security->reveal(), $entityWorkflowManager->reveal(), $repository->reveal(), $this->buildRegistry(), new MockClock($atDateTime ?? new \DateTimeImmutable()));
    }

    /**
     * @dataProvider provideDataAllowedByWorkflowReadOperationByAttachment
     *
     * @param list<EntityWorkflow> $entityWorkflows
     */
    public function testAllowedByWorkflowReadByAttachment(
        array $entityWorkflows,
        User $user,
        string $expected,
        ?\DateTimeImmutable $atDate,
        string $message,
    ): void {
        // all entities must have this workflow name, so we are ok to set it here
        foreach ($entityWorkflows as $entityWorkflow) {
            $entityWorkflow->setWorkflowName('dummy');
        }
        $helper = $this->buildHelperForAttachment($entityWorkflows, $user, $atDate);

        self::assertEquals($expected, $helper->isAllowedByWorkflowForReadOperation(new StoredObject()), $message);
    }

    public static function provideDataAllowedByWorkflowReadOperationByAttachment(): iterable
    {
        $entityWorkflow = new EntityWorkflow();
        $dto = new WorkflowTransitionContextDTO($entityWorkflow);
        $entityWorkflow->setStep('test', $dto, 'to_test', new \DateTimeImmutable(), new User());

        yield [[$entityWorkflow], new User(), WorkflowRelatedEntityPermissionHelper::ABSTAIN, new \DateTimeImmutable(),
            'abstain because the user is not present as a dest user'];

        $entityWorkflow = new EntityWorkflow();
        $dto = new WorkflowTransitionContextDTO($entityWorkflow);
        $dto->futureDestUsers[] = $user = new User();
        $entityWorkflow->setStep('test', $dto, 'to_test', new \DateTimeImmutable(), new User());

        yield [[$entityWorkflow], $user, WorkflowRelatedEntityPermissionHelper::FORCE_GRANT, new \DateTimeImmutable(),
            'force grant because the user is a current user'];

        $entityWorkflow = new EntityWorkflow();
        $dto = new WorkflowTransitionContextDTO($entityWorkflow);
        $dto->futureDestUsers[] = $user = new User();
        $entityWorkflow->setStep('test', $dto, 'to_test', new \DateTimeImmutable(), new User());

        $dto = new WorkflowTransitionContextDTO($entityWorkflow);
        $dto->futureDestUsers[] = new User();
        $entityWorkflow->setStep('test', $dto, 'to_test', new \DateTimeImmutable(), new User());

        yield [[$entityWorkflow], $user, WorkflowRelatedEntityPermissionHelper::FORCE_GRANT, new \DateTimeImmutable(),
            'force grant because the user was a previous user'];

        $entityWorkflow = new EntityWorkflow();
        $dto = new WorkflowTransitionContextDTO($entityWorkflow);
        $dto->futurePersonSignatures[] = new Person();
        $entityWorkflow->setStep('test', $dto, 'to_test', new \DateTimeImmutable(), new User());

        yield [[$entityWorkflow], new User(), WorkflowRelatedEntityPermissionHelper::ABSTAIN, new \DateTimeImmutable(),
            'Abstain: there is a signature for person, but the attachment is not concerned'];
    }

    /**
     * @dataProvider provideDataAllowedByWorkflowWriteOperationByAttachment
     *
     * @param list<EntityWorkflow> $entityWorkflows
     */
    public function testAllowedByWorkflowWriteByAttachment(
        array $entityWorkflows,
        User $user,
        string $expected,
        ?\DateTimeImmutable $atDate,
        string $message,
    ): void {
        // all entities must have this workflow name, so we are ok to set it here
        foreach ($entityWorkflows as $entityWorkflow) {
            $entityWorkflow->setWorkflowName('dummy');
        }
        $helper = $this->buildHelperForAttachment($entityWorkflows, $user, $atDate);

        self::assertEquals($expected, $helper->isAllowedByWorkflowForWriteOperation(new StoredObject()), $message);
    }

    public static function provideDataAllowedByWorkflowWriteOperationByAttachment(): iterable
    {
        $entityWorkflow = new EntityWorkflow();
        $dto = new WorkflowTransitionContextDTO($entityWorkflow);
        $entityWorkflow->setStep('test', $dto, 'to_test', new \DateTimeImmutable(), new User());

        yield [[], new User(), WorkflowRelatedEntityPermissionHelper::ABSTAIN, new \DateTimeImmutable(),
            'abstain because there is no workflow'];

        yield [[$entityWorkflow], new User(), WorkflowRelatedEntityPermissionHelper::ABSTAIN, new \DateTimeImmutable(),
            'abstain because the user is not present as a dest user (and attachment)'];

        $entityWorkflow = new EntityWorkflow();
        $dto = new WorkflowTransitionContextDTO($entityWorkflow);
        $dto->futureDestUsers[] = $user = new User();
        $entityWorkflow->setStep('test', $dto, 'to_test', new \DateTimeImmutable(), new User());

        yield [[$entityWorkflow], $user, WorkflowRelatedEntityPermissionHelper::FORCE_GRANT, new \DateTimeImmutable(),
            'force grant because the user is a current user'];

        $entityWorkflow = new EntityWorkflow();
        $dto = new WorkflowTransitionContextDTO($entityWorkflow);
        $dto->futureDestUsers[] = $user = new User();
        $entityWorkflow->setStep('test', $dto, 'to_test', new \DateTimeImmutable(), new User());

        $dto = new WorkflowTransitionContextDTO($entityWorkflow);
        $dto->futureDestUsers[] = new User();
        $entityWorkflow->setStep('test', $dto, 'to_test', new \DateTimeImmutable(), new User());

        yield [[$entityWorkflow], $user, WorkflowRelatedEntityPermissionHelper::FORCE_GRANT, new \DateTimeImmutable(),
            'force grant because the user was a previous user'];

        yield [[$entityWorkflow], new User(), WorkflowRelatedEntityPermissionHelper::ABSTAIN, new \DateTimeImmutable(),
            'abstain because the user was not a previous user'];

        $entityWorkflow = new EntityWorkflow();
        $dto = new WorkflowTransitionContextDTO($entityWorkflow);
        $dto->futureDestUsers[] = $user = new User();
        $entityWorkflow->setStep('test', $dto, 'to_test', new \DateTimeImmutable(), new User());
        $dto = new WorkflowTransitionContextDTO($entityWorkflow);
        $entityWorkflow->setStep('final_positive', $dto, 'to_final_positive', new \DateTimeImmutable(), new User());
        $entityWorkflow->getCurrentStep()->setIsFinal(true);

        yield [[$entityWorkflow], $user, WorkflowRelatedEntityPermissionHelper::FORCE_DENIED, new \DateTimeImmutable(),
            'force denied: user was a previous user, but it is finalized positive'];

        $entityWorkflow = new EntityWorkflow();
        $dto = new WorkflowTransitionContextDTO($entityWorkflow);
        $dto->futureDestUsers[] = $user = new User();
        $entityWorkflow->setStep('test', $dto, 'to_test', new \DateTimeImmutable());
        $dto = new WorkflowTransitionContextDTO($entityWorkflow);
        $entityWorkflow->setStep('final_negative', $dto, 'to_final_negative', new \DateTimeImmutable());
        $entityWorkflow->getCurrentStep()->setIsFinal(true);

        yield [[$entityWorkflow], $user, WorkflowRelatedEntityPermissionHelper::ABSTAIN, new \DateTimeImmutable(),
            'abstain: user was a previous user, it is finalized, but finalized negative'];

        $entityWorkflow = new EntityWorkflow();
        $dto = new WorkflowTransitionContextDTO($entityWorkflow);
        $dto->futureDestUsers[] = $user = new User();
        $entityWorkflow->setStep('test', $dto, 'to_test', new \DateTimeImmutable(), new User());
        $dto = new WorkflowTransitionContextDTO($entityWorkflow);
        $dto->futurePersonSignatures[] = new Person();
        $entityWorkflow->setStep('test', $dto, 'to_test', new \DateTimeImmutable(), new User());
        $signature = $entityWorkflow->getCurrentStep()->getSignatures()->first();
        $signature->setState(EntityWorkflowSignatureStateEnum::SIGNED)->setStateDate(new \DateTimeImmutable());

        yield [[$entityWorkflow], new User(), WorkflowRelatedEntityPermissionHelper::ABSTAIN, new \DateTimeImmutable(),
            'abstain: there is a signature, but not on the attachment'];

        $entityWorkflow = new EntityWorkflow();
        $dto = new WorkflowTransitionContextDTO($entityWorkflow);
        $dto->futureDestUsers[] = $user = new User();
        $entityWorkflow->setStep('test', $dto, 'to_test', new \DateTimeImmutable(), new User());
        $dto = new WorkflowTransitionContextDTO($entityWorkflow);
        $dto->futurePersonSignatures[] = new Person();
        $entityWorkflow->setStep('test', $dto, 'to_test', new \DateTimeImmutable(), new User());

        yield [[$entityWorkflow], new User(), WorkflowRelatedEntityPermissionHelper::ABSTAIN, new \DateTimeImmutable(),
            'abstain: there is a signature, but the signature is not on the attachment'];

        $entityWorkflow = new EntityWorkflow();
        $dto = new WorkflowTransitionContextDTO($entityWorkflow);
        $dto->futureDestUsers[] = $user = new User();
        $entityWorkflow->setStep('test', $dto, 'to_test', new \DateTimeImmutable(), new User());
        $dto = new WorkflowTransitionContextDTO($entityWorkflow);
        $dto->futurePersonSignatures[] = new Person();
        $entityWorkflow->setStep('test', $dto, 'to_test', new \DateTimeImmutable(), new User());
        $signature = $entityWorkflow->getCurrentStep()->getSignatures()->first();
        $signature->setState(EntityWorkflowSignatureStateEnum::SIGNED)->setStateDate(new \DateTimeImmutable());
        $dto = new WorkflowTransitionContextDTO($entityWorkflow);
        $entityWorkflow->setStep('final_negative', $dto, 'to_final_negative', new \DateTimeImmutable(), new User());
        $entityWorkflow->getCurrentStep()->setIsFinal(true);

        yield [[$entityWorkflow], $user, WorkflowRelatedEntityPermissionHelper::ABSTAIN, new \DateTimeImmutable(),
            'abstain: there is a signature on a canceled workflow'];

        $entityWorkflow = new EntityWorkflow();
        $dto = new WorkflowTransitionContextDTO($entityWorkflow);
        $dto->futureDestUsers[] = $user = new User();
        $entityWorkflow->setStep('sent_external', $dto, 'to_sent_external', new \DateTimeImmutable(), $user);

        yield [[$entityWorkflow], $user, WorkflowRelatedEntityPermissionHelper::FORCE_DENIED, new \DateTimeImmutable(),
            'force denied: the workflow is sent to an external user'];
    }

    /**
     * @param list<EntityWorkflow> $entityWorkflows
     */
    private function buildHelperForAttachment(array $entityWorkflows, User $user, ?\DateTimeImmutable $atDateTime): WorkflowRelatedEntityPermissionHelper
    {
        $security = $this->prophesize(Security::class);
        $security->getUser()->willReturn($user);

        $entityWorkflowManager = $this->prophesize(EntityWorkflowManager::class);
        $entityWorkflowManager->findByRelatedEntity(Argument::type('object'))->shouldNotBeCalled();

        $repository = $this->prophesize(EntityWorkflowAttachmentRepository::class);
        $attachments = [];
        foreach ($entityWorkflows as $entityWorkflow) {
            $attachments[] = new EntityWorkflowAttachment('dummy', ['id' => 1], $entityWorkflow, new StoredObject());
        }
        $repository->findByStoredObject(Argument::type(StoredObject::class))->willReturn($attachments);

        return new WorkflowRelatedEntityPermissionHelper($security->reveal(), $entityWorkflowManager->reveal(), $repository->reveal(), $this->buildRegistry(), new MockClock($atDateTime ?? new \DateTimeImmutable()));
    }

    private static function buildRegistry(): Registry
    {
        $builder = new DefinitionBuilder();
        $builder
            ->setInitialPlaces(['initial'])
            ->addPlaces(['initial', 'test', 'sent_external', 'final_positive', 'final_negative'])
            ->setMetadataStore(
                new InMemoryMetadataStore(
                    placesMetadata: [
                        'sent_external' => [
                            'isSentExternal' => true,
                        ],
                        'final_positive' => [
                            'isFinal' => true,
                            'isFinalPositive' => true,
                        ],
                        'final_negative' => [
                            'isFinal' => true,
                            'isFinalPositive' => false,
                        ],
                    ]
                )
            );

        $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;
    }
}
