<?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\Validator\TransitionHasDestUserIfRequired;
use Chill\MainBundle\Workflow\Validator\TransitionHasDestUserIfRequiredValidator;
use Chill\MainBundle\Workflow\WorkflowTransitionContextDTO;
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 TransitionHasDestUserIfRequiredValidatorTest extends ConstraintValidatorTestCase
{
    private Transition $transitionToSent;
    private Transition $transitionRegular;
    private Transition $transitionSignature;
    private Transition $transitionFinal;

    protected function setUp(): void
    {
        $this->transitionToSent = new Transition('send', 'initial', 'sent');
        $this->transitionSignature = new Transition('signature', 'initial', 'signature');
        $this->transitionRegular = new Transition('regular', 'initial', 'regular');
        $this->transitionFinal = new Transition('final', 'initial', 'final');

        parent::setUp();
    }

    public function testTransitionToRegularWithDestUsersRaiseNoViolation(): void
    {
        $dto = $this->buildDto();
        $dto->transition = $this->transitionRegular;
        $dto->futureDestUsers = [new User()];

        $constraint = new TransitionHasDestUserIfRequired();

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

        self::assertNoViolation();
    }

    public function testTransitionToRegularWithNoUsersRaiseViolation(): void
    {
        $dto = $this->buildDto();
        $dto->transition = $this->transitionRegular;

        $constraint = new TransitionHasDestUserIfRequired();
        $constraint->messageDestUserRequired = 'validation_message';

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

        self::buildViolation($constraint->messageDestUserRequired)
            ->setCode($constraint->codeDestUserRequired)
            ->atPath('property.path.futureDestUsers')
            ->assertRaised();
    }

    public function testTransitionToSignatureWithUserRaiseViolation(): void
    {
        $dto = $this->buildDto();
        $dto->transition = $this->transitionSignature;
        $dto->futureDestUsers = [new User()];

        $constraint = new TransitionHasDestUserIfRequired();

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

        self::buildViolation($constraint->messageDestUserNotAuthorized)
            ->setCode($constraint->codeDestUserNotAuthorized)
            ->atPath('property.path.futureDestUsers')
            ->assertRaised();
    }

    public function testTransitionToExternalSendWithUserRaiseViolation(): void
    {
        $dto = $this->buildDto();
        $dto->transition = $this->transitionToSent;
        $dto->futureDestUsers = [new User()];

        $constraint = new TransitionHasDestUserIfRequired();

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

        self::buildViolation($constraint->messageDestUserNotAuthorized)
            ->setCode($constraint->codeDestUserNotAuthorized)
            ->atPath('property.path.futureDestUsers')
            ->assertRaised();
    }

    public function testTransitionToFinalWithUserRaiseViolation(): void
    {
        $dto = $this->buildDto();
        $dto->transition = $this->transitionFinal;
        $dto->futureDestUsers = [new User()];

        $constraint = new TransitionHasDestUserIfRequired();

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

        self::buildViolation($constraint->messageDestUserNotAuthorized)
            ->setCode($constraint->codeDestUserNotAuthorized)
            ->atPath('property.path.futureDestUsers')
            ->assertRaised();
    }

    public function testTransitionToSignatureWithNoUserNoViolation(): void
    {
        $dto = $this->buildDto();
        $dto->transition = $this->transitionSignature;

        $constraint = new TransitionHasDestUserIfRequired();

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

        self::assertNoViolation();
    }

    public function testTransitionToExternalSendWithNoUserNoViolation(): void
    {
        $dto = $this->buildDto();
        $dto->transition = $this->transitionToSent;

        $constraint = new TransitionHasDestUserIfRequired();

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

        self::assertNoViolation();
    }

    public function testTransitionToFinalWithNoUserNoViolation(): void
    {
        $dto = $this->buildDto();
        $dto->transition = $this->transitionFinal;

        $constraint = new TransitionHasDestUserIfRequired();

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

        self::assertNoViolation();
    }

    private function buildDto(): WorkflowTransitionContextDTO
    {
        $entityWorkflow = new EntityWorkflow();
        $entityWorkflow->setWorkflowName('dummy');

        return new WorkflowTransitionContextDTO($entityWorkflow);
    }

    private function buildRegistry(): Registry
    {
        $builder = new DefinitionBuilder(
            ['initial', 'sent', 'signature', 'regular', 'final'],
            [
                $this->transitionToSent,
                $this->transitionSignature,
                $this->transitionRegular,
                $this->transitionFinal,
            ]
        );
        $builder
            ->setInitialPlaces('initial')
            ->setMetadataStore(new InMemoryMetadataStore(
                placesMetadata: [
                    'sent' => ['isSentExternal' => true],
                    'signature' => ['isSignature' => ['person', 'user']],
                    'final' => ['isFinal' => true],
                ]
            ))
        ;

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

        return $registry;
    }

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