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

use Chill\MainBundle\Entity\User;
use Chill\MainBundle\Entity\Workflow\EntityWorkflow;
use Chill\MainBundle\Workflow\EntityWorkflowMarkingStore;
use Chill\MainBundle\Workflow\Validator\TransitionHasSignerIfSignature;
use Chill\MainBundle\Workflow\Validator\TransitionHasSignerIfSignatureValidator;
use Chill\MainBundle\Workflow\WorkflowTransitionContextDTO;
use Chill\PersonBundle\Entity\Person;
use Symfony\Component\Validator\Test\ConstraintValidatorTestCase;
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 TransitionHasSignerIfSignatureValidatorTest extends ConstraintValidatorTestCase
{
    public function testMovingToNotSignatureDoesNotGenerateViolation(): void
    {
        $dto = new WorkflowTransitionContextDTO($entityWorkflow = new EntityWorkflow());
        $entityWorkflow->setWorkflowName('dummy');
        $registry = $this->buildRegistry();
        $workflow = $registry->get($entityWorkflow, $entityWorkflow->getWorkflowName());
        $dto->transition = $workflow->getEnabledTransition($entityWorkflow, 'to_not_signature');

        $constraint = new TransitionHasSignerIfSignature();

        $this->validator->validate($dto, $constraint);

        self::assertNoViolation();
    }

    public function testMovingToNotSignatureWithAPersonGenerateViolation(): void
    {
        $dto = new WorkflowTransitionContextDTO($entityWorkflow = new EntityWorkflow());
        $dto->futurePersonSignatures = [new Person()];
        $entityWorkflow->setWorkflowName('dummy');
        $registry = $this->buildRegistry();
        $workflow = $registry->get($entityWorkflow, $entityWorkflow->getWorkflowName());
        $dto->transition = $workflow->getEnabledTransition($entityWorkflow, 'to_not_signature');

        $constraint = new TransitionHasSignerIfSignature();

        $this->validator->validate($dto, $constraint);

        self::buildViolation($constraint->messageShouldBeEmpty)
            ->setCode('81d9e284-9d31-11ef-a078-6767f100370b')
            ->atPath('property.path.futurePersonSignatures')
            ->assertRaised();
    }

    public function testMovingToNotSignatureWithAUserGenerateViolation(): void
    {
        $dto = new WorkflowTransitionContextDTO($entityWorkflow = new EntityWorkflow());
        $dto->futureUserSignature = new User();
        $entityWorkflow->setWorkflowName('dummy');
        $registry = $this->buildRegistry();
        $workflow = $registry->get($entityWorkflow, $entityWorkflow->getWorkflowName());
        $dto->transition = $workflow->getEnabledTransition($entityWorkflow, 'to_not_signature');

        $constraint = new TransitionHasSignerIfSignature();

        $this->validator->validate($dto, $constraint);

        self::buildViolation($constraint->messageShouldBeEmpty)
            ->setCode('81d9e284-9d31-11ef-a078-6767f100370b')
            ->atPath('property.path.futureUserSignature')
            ->assertRaised();
    }

    public function testStepMovingToSignatureWithoutDestineeDoesGenerateViolation(): void
    {
        $dto = new WorkflowTransitionContextDTO($entityWorkflow = new EntityWorkflow());
        $entityWorkflow->setWorkflowName('dummy');
        $registry = $this->buildRegistry();
        $workflow = $registry->get($entityWorkflow, $entityWorkflow->getWorkflowName());
        $dto->transition = $workflow->getEnabledTransition($entityWorkflow, 'to_signature');

        $constraint = new TransitionHasSignerIfSignature();
        $constraint->message = 'test';

        $this->validator->validate($dto, $constraint);

        self::buildViolation('test')
            ->setCode('81d9e284-9d31-11ef-a078-6767f100370b')
            ->assertRaised();
    }

    public function testStepMovingToSignatureWithPersonDoesGenerateViolation(): void
    {
        $dto = new WorkflowTransitionContextDTO($entityWorkflow = new EntityWorkflow());
        $dto->futurePersonSignatures = [new Person()];
        $entityWorkflow->setWorkflowName('dummy');
        $registry = $this->buildRegistry();
        $workflow = $registry->get($entityWorkflow, $entityWorkflow->getWorkflowName());
        $dto->transition = $workflow->getEnabledTransition($entityWorkflow, 'to_signature');

        $constraint = new TransitionHasSignerIfSignature();
        $constraint->message = 'test';

        $this->validator->validate($dto, $constraint);

        self::assertNoViolation();
    }

    public function testStepMovingToSignatureWithUserDoesGenerateViolation(): void
    {
        $dto = new WorkflowTransitionContextDTO($entityWorkflow = new EntityWorkflow());
        $dto->futureUserSignature = new User();
        $entityWorkflow->setWorkflowName('dummy');
        $registry = $this->buildRegistry();
        $workflow = $registry->get($entityWorkflow, $entityWorkflow->getWorkflowName());
        $dto->transition = $workflow->getEnabledTransition($entityWorkflow, 'to_signature');

        $constraint = new TransitionHasSignerIfSignature();
        $constraint->message = 'test';

        $this->validator->validate($dto, $constraint);

        self::assertNoViolation();
    }

    protected function createValidator()
    {
        return new TransitionHasSignerIfSignatureValidator($this->buildRegistry());
    }

    private function buildRegistry(): Registry
    {
        $builder = new DefinitionBuilder();
        $builder
            ->addPlaces(['initial', 'signature', 'not_signature'])
            ->addTransition(new Transition('to_signature', 'initial', 'signature'))
            ->addTransition(new Transition('to_not_signature', 'initial', 'not_signature'))
            ->setMetadataStore(
                new InMemoryMetadataStore(
                    placesMetadata: [
                        'signature' => [
                            'isSignature' => ['person', 'user'],
                        ],
                    ]
                )
            );

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