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

use Chill\PersonBundle\Entity\Identifier\PersonIdentifier;
use Chill\PersonBundle\Entity\Identifier\PersonIdentifierDefinition;
use Chill\PersonBundle\PersonIdentifier\Identifier\StringIdentifier;
use PHPUnit\Framework\TestCase;

/**
 * @internal
 *
 * @coversNothing
 */
class StringIdentifierValidationTest extends TestCase
{
    private function makeDefinition(array $data = []): PersonIdentifierDefinition
    {
        $definition = new PersonIdentifierDefinition(label: ['en' => 'Test'], engine: StringIdentifier::NAME);
        if ([] !== $data) {
            $definition->setData($data);
        }

        return $definition;
    }

    private function makeIdentifier(PersonIdentifierDefinition $definition, ?string $content): PersonIdentifier
    {
        $identifier = new PersonIdentifier($definition);
        $identifier->setValue(['content' => $content]);

        return $identifier;
    }

    public function testValidateWithoutOptionsHasNoViolations(): void
    {
        $definition = $this->makeDefinition();
        $identifier = $this->makeIdentifier($definition, 'AB-123');

        $engine = new StringIdentifier();
        $violations = $engine->validate($identifier, $definition);

        self::assertIsArray($violations);
        self::assertCount(0, $violations);
    }

    public function testValidateOnlyNumbersOption(): void
    {
        $definition = $this->makeDefinition(['only_numbers' => true]);
        $engine = new StringIdentifier();

        // valid numeric content
        $identifierOk = $this->makeIdentifier($definition, '123456');
        $violationsOk = $engine->validate($identifierOk, $definition);
        self::assertCount(0, $violationsOk);

        // invalid alphanumeric content
        $identifierBad = $this->makeIdentifier($definition, '12AB');
        $violationsBad = $engine->validate($identifierBad, $definition);
        self::assertCount(1, $violationsBad);
        self::assertSame('person_identifier.only_number', $violationsBad[0]->message);
        self::assertSame('2a3352c0-a2b9-11f0-a767-b7a3f80e52f1', $violationsBad[0]->code);
    }

    public function testValidateFixedLengthOption(): void
    {
        $definition = $this->makeDefinition(['fixed_length' => 5]);
        $engine = new StringIdentifier();

        // valid exact length
        $identifierOk = $this->makeIdentifier($definition, 'ABCDE');
        $violationsOk = $engine->validate($identifierOk, $definition);
        self::assertCount(0, $violationsOk);

        // invalid length (too short)
        $identifierBad = $this->makeIdentifier($definition, 'AB');
        $violationsBad = $engine->validate($identifierBad, $definition);
        self::assertCount(1, $violationsBad);
        self::assertSame('person_identifier.fixed_length', $violationsBad[0]->message);
        self::assertSame('2b02a8fe-a2b9-11f0-bfe5-033300972783', $violationsBad[0]->code);
        self::assertSame(['limit' => '5'], $violationsBad[0]->parameters);
    }

    public function testValidateOnlyNumbersAndFixedLengthTogether(): void
    {
        $definition = $this->makeDefinition(['only_numbers' => true, 'fixed_length' => 4]);
        $engine = new StringIdentifier();

        // valid: numeric and correct length
        $identifierOk = $this->makeIdentifier($definition, '1234');
        $violationsOk = $engine->validate($identifierOk, $definition);
        self::assertCount(0, $violationsOk);

        // invalid: non-numeric and wrong length -> two violations expected
        $identifierBad = $this->makeIdentifier($definition, 'AB');
        $violationsBad = $engine->validate($identifierBad, $definition);
        self::assertCount(2, $violationsBad);
        // Order is defined by implementation: numbers check first, then length
        self::assertSame('person_identifier.only_number', $violationsBad[0]->message);
        self::assertSame('person_identifier.fixed_length', $violationsBad[1]->message);
    }
}
