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

use Chill\PersonBundle\Entity\Identifier\PersonIdentifier;
use Chill\PersonBundle\Entity\Identifier\PersonIdentifierDefinition;
use Chill\PersonBundle\PersonIdentifier\IdentifierViolationDTO;
use Chill\PersonBundle\PersonIdentifier\PersonIdentifierEngineInterface;
use Chill\PersonBundle\PersonIdentifier\PersonIdentifierManagerInterface;
use Chill\PersonBundle\PersonIdentifier\PersonIdentifierWorker;
use Chill\PersonBundle\PersonIdentifier\Validator\ValidIdentifierConstraint;
use Chill\PersonBundle\PersonIdentifier\Validator\ValidIdentifierConstraintValidator;
use PHPUnit\Framework\Attributes\CoversClass;
use Prophecy\PhpUnit\ProphecyTrait;
use Prophecy\Prophecy\ObjectProphecy;
use Symfony\Component\Validator\Test\ConstraintValidatorTestCase;

/**
 * @internal
 */
#[CoversClass(ValidIdentifierConstraintValidator::class)]
final class ValidIdentifierConstraintValidatorTest extends ConstraintValidatorTestCase
{
    use ProphecyTrait;

    /**
     * @var ObjectProphecy|PersonIdentifierManagerInterface
     */
    private ObjectProphecy $manager;

    protected function setUp(): void
    {
        $this->manager = $this->prophesize(PersonIdentifierManagerInterface::class);
        parent::setUp();
    }

    protected function createValidator(): ValidIdentifierConstraintValidator
    {
        return new ValidIdentifierConstraintValidator($this->manager->reveal());
    }

    public function testAddsViolationFromWorker(): void
    {
        $definition = new PersonIdentifierDefinition(['en' => 'SSN'], 'string');
        // set definition id via reflection for definition_id parameter
        $ref = new \ReflectionClass($definition);
        $prop = $ref->getProperty('id');
        $prop->setValue($definition, 1);

        $identifier = new PersonIdentifier($definition);
        $identifier->setValue(['value' => 'bad']);

        $violation = new IdentifierViolationDTO('Invalid Identifier', '0000-1111-2222-3333', ['{{ foo }}' => 'bar']);

        // engine that returns one violation
        $engine = new class ([$violation]) implements PersonIdentifierEngineInterface {
            public function __construct(private readonly array $violations) {}

            public static function getName(): string
            {
                return 'dummy';
            }

            public function canonicalizeValue(array $value, PersonIdentifierDefinition $definition): ?string
            {
                return null;
            }

            public function buildForm(\Symfony\Component\Form\FormBuilderInterface $builder, PersonIdentifierDefinition $personIdentifierDefinition): void {}

            public function renderAsString(?PersonIdentifier $identifier, PersonIdentifierDefinition $definition): string
            {
                return '';
            }

            public function isEmpty(PersonIdentifier $identifier): bool
            {
                return false;
            }

            public function validate(PersonIdentifier $identifier, PersonIdentifierDefinition $definition): array
            {
                return $this->violations;
            }

            public function getDefaultValue(PersonIdentifierDefinition $definition): array
            {
                return [];
            }
        };
        $worker = new PersonIdentifierWorker($engine, $definition);

        $this->manager
            ->buildWorkerByPersonIdentifierDefinition($definition)
            ->willReturn($worker);

        $constraint = new ValidIdentifierConstraint();
        $this->validator->validate($identifier, $constraint);

        $this->buildViolation('Invalid Identifier')
            ->setParameters(['{{ foo }}' => 'bar'])
            ->setParameter('{{ code }}', '0000-1111-2222-3333')
            ->setParameter('definition_id', '1')
            ->assertRaised();
    }
}
