<?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\Security\Authorization;

use Chill\MainBundle\Entity\User;
use Chill\MainBundle\Entity\Workflow\EntityWorkflow;
use Chill\MainBundle\Security\Authorization\EntityWorkflowAttachmentVoter;
use Chill\MainBundle\Workflow\EntityWorkflowMarkingStore;
use Chill\MainBundle\Workflow\WorkflowTransitionContextDTO;
use PHPUnit\Framework\TestCase;
use Prophecy\PhpUnit\ProphecyTrait;
use Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken;
use Symfony\Component\Security\Core\Authorization\Voter\VoterInterface;
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 EntityWorkflowAttachmentVoterTest extends TestCase
{
    use ProphecyTrait;

    /**
     * @dataProvider dataVoteOnAttribute
     */
    public function testVoteOnAttribute(EntityWorkflow $entityWorkflow, int $expected): void
    {
        $voter = new EntityWorkflowAttachmentVoter($this->buildRegistry());
        $actual = $voter->vote(
            new UsernamePasswordToken(new User(), 'default'),
            $entityWorkflow,
            ['CHILL_MAIN_WORKFLOW_ATTACHMENT_EDIT'],
        );
        $this->assertEquals($expected, $actual);
    }

    public static function dataVoteOnAttribute(): iterable
    {
        $entity = new EntityWorkflow();
        $entity->setWorkflowName('dummy');

        $workflow = static::buildRegistry()->get($entity, 'dummy');

        $dto = new WorkflowTransitionContextDTO($entity);
        $dto->futureDestUsers[] = new User();
        $workflow->apply(
            $entity,
            'to_final_positive',
            ['context' => $dto,
                'byUser' => new User(),
                'transition' => 'to_final_positive',
                'transitionAt' => new \DateTimeImmutable()],
        );
        // we need to mark manually as final, as the listener is not registered
        $entity->getCurrentStep()->setIsFinal(true);

        yield 'on final positive' => [
            $entity,
            VoterInterface::ACCESS_DENIED,
        ];

        $entity = new EntityWorkflow();
        $entity->setWorkflowName('dummy');

        $workflow = static::buildRegistry()->get($entity, 'dummy');

        $dto = new WorkflowTransitionContextDTO($entity);
        $dto->futureDestUsers[] = new User();
        $workflow->apply(
            $entity,
            'to_final_negative',
            ['context' => $dto,
                'byUser' => new User(),
                'transition' => 'to_final_negative',
                'transitionAt' => new \DateTimeImmutable()],
        );
        // we need to mark manually as final, as the listener is not registered
        $entity->getCurrentStep()->setIsFinal(true);

        yield 'on final negative' => [
            $entity,
            VoterInterface::ACCESS_DENIED,
        ];

        $entity = new EntityWorkflow();
        $entity->setWorkflowName('dummy');

        $workflow = static::buildRegistry()->get($entity, 'dummy');

        $dto = new WorkflowTransitionContextDTO($entity);
        $dto->futureDestUsers[] = new User();
        $workflow->apply(
            $entity,
            'to_sent_external',
            ['context' => $dto,
                'byUser' => new User(),
                'transition' => 'to_sent_external',
                'transitionAt' => new \DateTimeImmutable()],
        );

        yield 'on sent_external' => [
            $entity,
            VoterInterface::ACCESS_DENIED,
        ];

        $entity = new EntityWorkflow();
        $entity->setWorkflowName('dummy');

        yield 'on initial' => [
            $entity,
            VoterInterface::ACCESS_GRANTED,
        ];
    }

    private static function buildRegistry(): Registry
    {
        $builder = new DefinitionBuilder();
        $builder
            ->setInitialPlaces(['initial'])
            ->addPlaces(['initial', 'sent_external', 'final_positive', 'final_negative'])
            ->addTransitions([
                new Transition('to_final_positive', ['initial'], 'final_positive'),
                new Transition('to_sent_external', ['initial'], 'sent_external'),
                new Transition('to_final_negative', ['initial'], '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;
    }
}
