<?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 Action\PersonEdit\Service;

use Chill\MainBundle\Entity\Civility;
use Chill\MainBundle\Entity\Country;
use Chill\MainBundle\Entity\Embeddable\CommentEmbeddable;
use Chill\MainBundle\Entity\Gender;
use Chill\MainBundle\Entity\Language;
use Chill\PersonBundle\Actions\PersonEdit\PersonEditDTO;
use Chill\PersonBundle\Actions\PersonEdit\Service\PersonEditDTOFactory;
use Chill\PersonBundle\Config\ConfigPersonAltNamesHelper;
use Chill\PersonBundle\Entity\AdministrativeStatus;
use Chill\PersonBundle\Entity\EmploymentStatus;
use Chill\PersonBundle\Entity\MaritalStatus;
use Chill\PersonBundle\Entity\Person;
use Chill\PersonBundle\Entity\PersonAltName;
use Chill\PersonBundle\PersonIdentifier\PersonIdentifierManagerInterface;
use Doctrine\Common\Collections\ArrayCollection;
use libphonenumber\PhoneNumber;
use PHPUnit\Framework\TestCase;
use Prophecy\PhpUnit\ProphecyTrait;

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

    public function testMapPersonEditDTOtoPersonCopiesAllFields(): void
    {
        $configHelper = $this->createMock(ConfigPersonAltNamesHelper::class);
        $identifierManager = $this->createMock(PersonIdentifierManagerInterface::class);
        $factory = new PersonEditDTOFactory($configHelper, $identifierManager);

        $dto = new PersonEditDTO();
        $dto->firstName = 'John';
        $dto->lastName = 'Doe';
        $dto->birthdate = new \DateTime('1980-05-10');
        $dto->deathdate = new \DateTimeImmutable('2050-01-01');
        $dto->gender = new Gender();
        $dto->genderComment = new CommentEmbeddable('gender comment');
        $dto->numberOfChildren = 2;
        $dto->memo = 'Some memo';
        $dto->employmentStatus = new EmploymentStatus();
        $dto->administrativeStatus = new AdministrativeStatus();
        $dto->placeOfBirth = 'Cityville';
        $dto->contactInfo = 'Some contact info';
        $phone = new PhoneNumber();
        $dto->phonenumber = $phone;
        $mobile = new PhoneNumber();
        $dto->mobilenumber = $mobile;
        $dto->acceptSms = true;
        $dto->otherPhonenumbers = new ArrayCollection();
        $dto->email = 'john.doe@example.org';
        $dto->acceptEmail = true;
        $dto->countryOfBirth = new Country();
        $dto->nationality = new Country();
        $dto->spokenLanguages = new ArrayCollection([new Language()]);
        $dto->civility = new Civility();
        $dto->maritalStatus = new MaritalStatus();
        $dto->maritalStatusDate = new \DateTime('2010-01-01');
        $dto->maritalStatusComment = new CommentEmbeddable('married');
        $dto->cFData = ['foo' => 'bar'];

        $person = new Person();

        $factory->mapPersonEditDTOtoPerson($dto, $person);

        self::assertSame('John', $person->getFirstName());
        self::assertSame('Doe', $person->getLastName());
        self::assertSame($dto->birthdate, $person->getBirthdate());
        self::assertSame($dto->deathdate, $person->getDeathdate());
        self::assertSame($dto->gender, $person->getGender());
        self::assertSame($dto->genderComment, $person->getGenderComment());
        self::assertSame($dto->numberOfChildren, $person->getNumberOfChildren());
        self::assertSame('Some memo', $person->getMemo());
        self::assertSame($dto->employmentStatus, $person->getEmploymentStatus());
        self::assertSame($dto->administrativeStatus, $person->getAdministrativeStatus());
        self::assertSame('Cityville', $person->getPlaceOfBirth());
        self::assertSame('Some contact info', $person->getcontactInfo());
        self::assertSame($phone, $person->getPhonenumber());
        self::assertSame($mobile, $person->getMobilenumber());
        self::assertTrue($person->getAcceptSMS());
        self::assertSame($dto->otherPhonenumbers, $person->getOtherPhoneNumbers());
        self::assertSame('john.doe@example.org', $person->getEmail());
        self::assertTrue($person->getAcceptEmail());
        self::assertSame($dto->countryOfBirth, $person->getCountryOfBirth());
        self::assertSame($dto->nationality, $person->getNationality());
        self::assertSame($dto->spokenLanguages, $person->getSpokenLanguages());
        self::assertSame($dto->civility, $person->getCivility());
        self::assertSame($dto->maritalStatus, $person->getMaritalStatus());
        self::assertSame($dto->maritalStatusDate, $person->getMaritalStatusDate());
        self::assertSame($dto->maritalStatusComment, $person->getMaritalStatusComment());
        self::assertSame($dto->cFData, $person->getCFData());
    }

    public function testAltNamesHandlingWithConfigHelper(): void
    {
        $configHelper = $this->createMock(ConfigPersonAltNamesHelper::class);
        $configHelper->method('getChoices')->willReturn([
            'aka' => ['en' => 'Also Known As'],
            'nickname' => ['en' => 'Nickname'],
        ]);

        $identifierManager = $this->createMock(PersonIdentifierManagerInterface::class);
        $identifierManager->method('getWorkers')->willReturn([]);

        $factory = new PersonEditDTOFactory($configHelper, $identifierManager);
        $person = new Person();

        $dto = $factory->createPersonEditDTO($person);

        // Assert DTO has two altNames keys from helper
        self::assertCount(2, $dto->altNames);
        self::assertContainsOnlyInstancesOf(PersonAltName::class, $dto->altNames);
        self::assertSame(['aka', 'nickname'], array_keys($dto->altNames));
        self::assertSame(['aka' => 'aka', 'nickname' => 'nickname'], array_map(fn (PersonAltName $altName) => $altName->getKey(), $dto->altNames));

        // Fill only one label and leave the other empty
        $dto->altNames['aka']->setLabel('The Boss');
        // 'nickname' remains empty by default

        // Map DTO back to person
        $factory->mapPersonEditDTOtoPerson($dto, $person);

        // Assert only the filled alt name is persisted on the person
        $altNames = $person->getAltNames();
        self::assertCount(1, $altNames);
        $altNameArray = $altNames->toArray();
        self::assertSame('aka', $altNameArray[0]->getKey());
        self::assertSame('The Boss', $altNameArray[0]->getLabel());
    }
}
