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

use Chill\MainBundle\Entity\Center;
use Chill\MainBundle\Entity\Gender;
use Chill\MainBundle\Entity\GenderEnum;
use Chill\MainBundle\Entity\GenderIconEnum;
use Chill\MainBundle\Repository\CenterRepositoryInterface;
use Chill\MainBundle\Test\PrepareClientTrait;
use Chill\PersonBundle\Entity\Person;
use Chill\PersonBundle\Repository\PersonRepository;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Bundle\FrameworkBundle\KernelBrowser;
use Symfony\Bundle\FrameworkBundle\Test\WebTestCase;

/**
 * Test the edition of persons.
 *
 * As I am logged in as "center a_social"
 *
 * @internal
 *
 * @coversNothing
 */
final class PersonControllerUpdateTest extends WebTestCase
{
    use PrepareClientTrait;

    private readonly KernelBrowser $client;

    /**
     * @var list<array<class-string, int>>
     */
    private static array $toDelete = [];

    /**
     * Prepare client and create a random person.
     */
    protected function setUp(): void {}

    protected function tearDown(): void
    {
        self::ensureKernelShutdown();
    }

    public static function tearDownAfterClass(): void
    {
        self::bootKernel();

        $em = self::getContainer()->get(EntityManagerInterface::class);

        foreach (self::$toDelete as [$class, $id]) {
            $entity = $em->find($class, $id);

            if (null === $entity) {
                throw new \RuntimeException(sprintf('entity not found: %s, %d', $class, $id));
            }

            $em->remove($entity);
        }

        $em->flush();
        self::ensureKernelShutdown();
    }

    /**
     * Test the edit page of a given person are not accessible for an
     * administrative user.
     *
     * @dataProvider providePerson
     */
    public function testEditPageDeniedForUnauthorizedInsideCenter(int $personId)
    {
        $client = $this->getClientAuthenticated('center a_administrative');

        $client->request('GET', $this->makeEditPath($personId));

        self::assertResponseStatusCodeSame(403);
    }

    /**
     * Test if the edit page of a given person is not accessible for a user
     * of another center of the person.
     *
     * @dataProvider providePerson
     */
    public function testEditPageDeniedForUnauthorizedOutsideCenter(int $personId)
    {
        $client = $this->getClientAuthenticated('center b_social');

        $client->request('GET', $this->makeEditPath($personId));

        self::assertResponseStatusCodeSame(
            403,
            'The edit page of a person of a center A must not be accessible for user of center B'
        );
    }

    /**
     * Test the edit page are accessible.
     *
     * @dataProvider providePerson
     */
    public function testEditPageIsSuccessful(int $personId)
    {
        $client = $this->getClientAuthenticated();

        $crawler = $client->request('GET', $this->makeEditPath($personId));

        self::assertResponseIsSuccessful('The person edit form is accessible');

        $form = $crawler->selectButton('Enregistrer')->form();

        $form['chill_personbundle_person[firstName]'] = 'tagada';

        $client->submit($form);

        self::assertResponseRedirects($this->makeViewPath($personId));

        $person = self::getContainer()->get(PersonRepository::class)->find($personId);

        self::assertEquals('tagada', $person->getFirstName());
    }

    /**
     * @dataProvider providePerson
     */
    public function testEditPageWithErrorIsNotProcessable(int $personId)
    {
        $client = $this->getClientAuthenticated();

        $crawler = $client->request('GET', $this->makeEditPath($personId));

        self::assertResponseIsSuccessful('The person edit form is accessible');

        $form = $crawler->selectButton('Enregistrer')->form();

        $form['chill_personbundle_person[firstName]'] = '';

        $crawler = $client->submit($form);

        self::assertResponseIsSuccessful('the page is successful but not redirected');

        $alerts = $crawler->filter('.alert-danger');

        self::assertEquals(1, $alerts->count(), 'there is an alert message displayed on the page');
    }

    public static function providePerson(): iterable
    {
        self::bootKernel();
        $centerRepository = self::getContainer()->get(CenterRepositoryInterface::class);
        $em = self::getContainer()->get(EntityManagerInterface::class);
        $center = $centerRepository->findOneBy(['name' => 'Center A']);

        $gender = new Gender();
        $gender->setGenderTranslation(GenderEnum::MALE);
        $gender->setLabel(['fr' => 'homme']);
        $gender->setIcon(GenderIconEnum::MALE);
        $em->persist($gender);

        $person = new Person();
        $person
            ->setFirstName('Foo')
            ->setLastName('Bar')
            ->setBirthdate(new \DateTime('2017-09-30'))
            ->setGender($gender)
            ->setCenter($center);

        $em->persist($person);

        self::$toDelete[] = [Person::class, $person->getId()];

        $em->flush();

        yield [$person->getId()];

        self::ensureKernelShutdown();
    }

    private function makeEditPath(int $personId): string
    {
        return "/fr/person/{$personId}/general/edit";
    }

    private function makeViewPath(int $personId): string
    {
        return "/fr/person/{$personId}/general";
    }
}
