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

use Chill\MainBundle\Test\PrepareClientTrait;
use Chill\PersonBundle\Entity\Household\Household;
use Chill\PersonBundle\Entity\Household\HouseholdMember;
use Chill\PersonBundle\Entity\Household\Position;
use Chill\PersonBundle\Entity\Person;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Bundle\FrameworkBundle\Test\WebTestCase;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;

/**
 * @internal
 *
 * @coversNothing
 */
final class HouseholdMemberControllerTest extends WebTestCase
{
    use PrepareClientTrait;

    /**
     * @dataProvider provideValidDataEditMember
     */
    public function testEditMember(int $memberId)
    {
        $client = $this->getClientAuthenticated();

        $crawler = $client->request(
            Request::METHOD_GET,
            "/fr/person/household/member/{$memberId}/edit"
        );

        $this->assertResponseIsSuccessful();

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

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

        $this->assertEquals(302, $client->getResponse()->getStatusCode());
    }

    public static function provideValidDataEditMember(): \Iterator
    {
        self::bootKernel();
        $em = self::getContainer()->get(EntityManagerInterface::class);

        $membershipIds = $em->createQuery(sprintf('SELECT m.id FROM %s m JOIN m.person p JOIN p.centerHistory ch JOIN ch.center c WHERE c.name = :center AND m.endDate IS NULL AND ch.endDate IS NULL', HouseholdMember::class))
            ->setParameter('center', 'Center A')
            ->getScalarResult();

        if ([] === $membershipIds) {
            throw new \RuntimeException("no memberships for person associated to 'Center A'");
        }

        \shuffle($membershipIds);

        yield [\array_pop($membershipIds)['id']];
    }

    /**
     * @dataProvider provideValidDataMove
     */
    public function testLeaveWithoutHousehold(mixed $personId, mixed $householdId, mixed $positionId, \DateTimeInterface $date)
    {
        $client = $this->getClientAuthenticated();

        $client->request(
            Request::METHOD_POST,
            '/api/1.0/person/household/members/move.json',
            [], // parameters
            [], // files
            [], // server
            \json_encode(
                [
                    'concerned' => [
                        [
                            'person' => [
                                'type' => 'person',
                                'id' => $personId,
                            ],
                            'start_date' => [
                                'datetime' => $date->format(\DateTimeInterface::RFC3339),
                            ],
                            'position' => [
                                'type' => 'household_position',
                                'id' => $positionId,
                            ],
                            'holder' => false,
                            'comment' => 'Introduced by automated test',
                        ],
                    ],
                    'destination' => null,
                    'composition' => null,
                ]
            )
        );

        $this->assertEquals(
            Response::HTTP_OK,
            $client->getResponse()->getStatusCode()
        );

        $data = \json_decode($client->getResponse()->getContent(), true, 512, JSON_THROW_ON_ERROR);

        $this->assertEquals(null, $data);
    }

    /**
     * @dataProvider provideValidDataMove
     */
    public function testMoveMember(mixed $personId, mixed $householdId, mixed $positionId, \DateTimeInterface $date)
    {
        $client = $this->getClientAuthenticated();

        $client->request(
            Request::METHOD_POST,
            '/api/1.0/person/household/members/move.json',
            [], // parameters
            [], // files
            [], // server
            \json_encode(
                [
                    'concerned' => [
                        [
                            'person' => [
                                'type' => 'person',
                                'id' => $personId,
                            ],
                            'start_date' => [
                                'datetime' => $date->format(\DateTimeInterface::RFC3339),
                            ],
                            'position' => [
                                'type' => 'household_position',
                                'id' => $positionId,
                            ],
                            'holder' => false,
                            'comment' => 'Introduced by automated test',
                        ],
                    ],
                    'destination' => [
                        'type' => 'household',
                        'id' => $householdId,
                    ],
                    'composition' => null,
                ]
            )
        );

        $this->assertEquals(
            Response::HTTP_OK,
            $client->getResponse()->getStatusCode()
        );
    }

    /**
     * @dataProvider provideValidDataMove
     */
    public function testMoveMemberToNewHousehold(mixed $personId, mixed $householdId, mixed $positionId, \DateTimeInterface $date)
    {
        $client = $this->getClientAuthenticated();

        $client->request(
            Request::METHOD_POST,
            '/api/1.0/person/household/members/move.json',
            [], // parameters
            [], // files
            [], // server
            \json_encode(
                [
                    'concerned' => [
                        [
                            'person' => [
                                'type' => 'person',
                                'id' => $personId,
                            ],
                            'start_date' => [
                                'datetime' => $date->format(\DateTimeInterface::RFC3339),
                            ],
                            'position' => [
                                'type' => 'household_position',
                                'id' => $positionId,
                            ],
                            'holder' => false,
                            'comment' => 'Introduced by automated test',
                        ],
                    ],
                    'destination' => [
                        'type' => 'household',
                    ],
                    'composition' => null,
                ]
            )
        );

        $this->assertEquals(
            Response::HTTP_OK,
            $client->getResponse()->getStatusCode()
        );

        $data = \json_decode($client->getResponse()->getContent(), true, 512, JSON_THROW_ON_ERROR);

        $this->assertIsArray($data);
        $this->assertArrayHasKey('members', $data);
        $this->assertIsArray($data['members']);
        $this->assertEquals(
            1,
            \count($data['members']),
            'assert new household count one member'
        );
        $this->assertArrayHasKey('person', $data['members'][0]);
        $this->assertArrayHasKey('id', $data['members'][0]['person']);
        $this->assertEquals($personId, $data['members'][0]['person']['id']);
    }

    public static function provideValidDataMove(): \Iterator
    {
        self::bootKernel();
        $em = self::getContainer()->get(EntityManagerInterface::class);
        $yesterday = new \DateTimeImmutable('yesterday');

        $personIds = $em->createQuery(
            sprintf('SELECT p.id FROM %s p JOIN p.centerHistory ch JOIN ch.center c WHERE c.name = :center AND ch.endDate IS NULL', Person::class)
        )
            ->setParameter('center', 'Center A')
            ->setMaxResults(100)
            ->getScalarResult();

        if ([] === $personIds) {
            throw new \RuntimeException('no person associated with "Center A"');
        }

        \shuffle($personIds);

        $household = new Household();
        $em->persist($household);
        $em->flush();

        $positions = $em->createQuery('SELECT pos.id FROM '.Position::class.' pos '.
            'WHERE pos.shareHouseHold = TRUE')
            ->getResult();

        $i = 0;

        do {
            $id = \array_pop($personIds)['id'];
            $person = self::getContainer()->get(EntityManagerInterface::class)
                ->getRepository(Person::class)
                ->find($id);

            $participation = $person->getCurrentHouseholdParticipationShareHousehold();

            if (null === $participation
                || (
                    null === $participation->getEndDate()
                    && $participation->getStartDate() <= $yesterday
                )) {
                ++$i;

                yield [
                    $id,
                    $household->getId(),
                    $positions[\random_int(0, \count($positions) - 1)]['id'],
                    new \DateTimeImmutable('tomorrow'),
                ];
            }
        } while (1 >= $i);
    }
}
