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

use Chill\PersonBundle\Entity\Identifier\PersonIdentifier;
use Chill\PersonBundle\Entity\Identifier\PersonIdentifierDefinition;
use Chill\PersonBundle\Entity\Person;
use Chill\PersonBundle\PersonIdentifier\Identifier\StringIdentifier;
use Chill\PersonBundle\PersonIdentifier\PersonIdentifierEngineInterface;
use Chill\PersonBundle\PersonIdentifier\PersonIdentifierManager;
use Chill\PersonBundle\PersonIdentifier\PersonIdentifierManagerInterface;
use Chill\PersonBundle\PersonIdentifier\PersonIdentifierWorker;
use Chill\PersonBundle\PersonIdentifier\Rendering\PersonIdRendering;
use PHPUnit\Framework\TestCase;
use Prophecy\Argument;
use Prophecy\PhpUnit\ProphecyTrait;
use Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface;

/**
 * @internal
 *
 * @coversNothing
 */
class PersonIdRenderingTest extends TestCase
{
    use ProphecyTrait;

    /**
     * @dataProvider provideRenderCases
     */
    public function testRenderPersonId(Person $person, string $idContentText, string $expected): void
    {
        // Parameter bag mock returning the provided id_content_text
        $parameterBag = $this->prophesize(ParameterBagInterface::class);
        $parameterBag->get('chill_person')
            ->willReturn(['person_render' => ['id_content_text' => $idContentText]]);

        // PersonIdentifierManager is explicitly requested to be mocked in the spec.
        // It will return a PersonIdentifierWorker whose renderAsString behaves like StringIdentifier::renderAsString
        $personIdentifierManager = $this->prophesize(PersonIdentifierManagerInterface::class);
        $personIdentifierManager
            ->buildWorkerByPersonIdentifierDefinition(Argument::type(PersonIdentifierDefinition::class))
            ->will(function ($args) {
                /** @var PersonIdentifierDefinition $definition */
                $definition = $args[0];

                $engine = new class () implements PersonIdentifierEngineInterface {
                    public static function getName(): string
                    {
                        return 'test';
                    }

                    public function canonicalizeValue(array $value, PersonIdentifierDefinition $definition): ?string
                    {
                        return $value['content'] ?? '';
                    }

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

                    public function renderAsString(?PersonIdentifier $identifier, PersonIdentifierDefinition $definition): string
                    {
                        // same behavior as StringIdentifier::renderAsString
                        return $identifier?->getValue()['content'] ?? '';
                    }

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

                    public function validate(PersonIdentifier $identifier, PersonIdentifierDefinition $definition): array
                    {
                        return [];
                    }

                    public function getDefaultValue(PersonIdentifierDefinition $definition): array
                    {
                        return [];
                    }
                };

                return new PersonIdentifierWorker($engine, $definition);
            });

        $service = new PersonIdRendering($parameterBag->reveal(), $personIdentifierManager->reveal());

        self::assertSame($expected, $service->renderPersonId($person));
    }

    public function provideRenderCases(): iterable
    {
        // Case 1: one active identifier, one inactive identifier, should render person id and only active identifier
        $person1 = new Person();
        $this->setEntityId($person1, 123);

        $defActive = new PersonIdentifierDefinition(label: ['en' => 'Active'], engine: 'string');
        $this->setEntityId($defActive, 10);
        $defActive->setActive(true);

        $idActive = new PersonIdentifier($defActive);
        $idActive->setPerson($person1);
        $idActive->setValue(['content' => 'ABC']);
        $person1->addIdentifier($idActive);

        $defInactive = new PersonIdentifierDefinition(label: ['en' => 'Inactive'], engine: 'string');
        $this->setEntityId($defInactive, 99);
        $defInactive->setActive(false);

        $idInactive = new PersonIdentifier($defInactive);
        $idInactive->setPerson($person1);
        $idInactive->setValue(['content' => 'SHOULD_NOT_APPEAR']);
        $person1->addIdentifier($idInactive);

        $template1 = 'ID: [[ person_id ]] - Active: [[ identifier_10 ]] - Inactive: [[ identifier_99 ]]';
        $expected1 = 'ID: 123 - Active: ABC - Inactive: [[ identifier_99 ]]';

        yield
            'with active and inactive identifiers' => [$person1, $template1, $expected1]
        ;

        $template2 = 'ID: [[ person_id ]][[ if:identifier_10 ]] - Active: [[ identifier_10 ]][[ endif:identifier_10 ]]';
        $expected2 = 'ID: 123 - Active: ABC';

        yield
            'rendering with conditional: condition are removed' => [$person1, $template2, $expected2]
        ;

        $template3 = 'ID: [[ person_id ]][[ if:identifier_99 ]] - Inactive: [[ identifier_10 ]][[ endif:identifier_99 ]]';
        $expected3 = 'ID: 123';

        yield
            'rendering with conditional: the content between condition is removed' => [$person1, $template3, $expected3]
        ;

        $template4 = 'ID: [[ person_id ]][[ if:identifier_105 ]] - not present: [[ identifier_105 ]][[ endif:identifier_105 ]]';
        $expected4 = 'ID: 123';

        yield
            'rendering with conditional: the content between condition is removed, the identifier is not associated with the person' => [$person1, $template4, $expected4]
        ;

    }

    private function setEntityId(object $entity, int $id): void
    {
        $refl = new \ReflectionClass($entity);
        $prop = $refl->getProperty('id');
        $prop->setAccessible(true);
        $prop->setValue($entity, $id);
    }
}
