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

use Chill\MainBundle\CRUD\Controller\ApiController;
use Chill\MainBundle\Entity\Address;
use Chill\PersonBundle\Config\ConfigPersonAltNamesHelper;
use Chill\PersonBundle\Entity\AccompanyingPeriodParticipation;
use Chill\PersonBundle\Entity\Person;
use Chill\PersonBundle\Security\Authorization\PersonVoter;
use Chill\PersonBundle\Security\AuthorizedCenterOnPersonCreationInterface;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\ParamConverter;
use Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;

class PersonApiController extends ApiController
{
    private readonly bool $showCenters;

    public function __construct(
        private readonly AuthorizedCenterOnPersonCreationInterface $authorizedCenterOnPersonCreation,
        private readonly ConfigPersonAltNamesHelper $configPersonAltNameHelper,
        ParameterBagInterface $parameterBag,
    ) {
        $this->showCenters = $parameterBag->get('chill_main')['acl']['form_show_centers'];
    }

    #[Route(path: '/api/1.0/person/creation/authorized-centers', name: 'chill_person_person_creation_authorized_centers')]
    public function authorizedCentersForCreation(): Response
    {
        $centers = $this->authorizedCenterOnPersonCreation->getCenters();

        return $this->json(
            ['showCenters' => $this->showCenters, 'centers' => $centers],
            Response::HTTP_OK,
            [],
            ['groups' => ['read']]
        );
    }

    #[Route(path: '/api/1.0/person/config/alt_names.{_format}', name: 'chill_person_config_alt_names', requirements: ['_format' => 'json'])]
    public function configAltNames(Request $request, string $_format): Response
    {
        $configAltNamesChoices = $this->configPersonAltNameHelper->getChoices();

        return $this->json(
            array_map(
                static fn (array $data, string $key): array => ['key' => $key, 'labels' => $data],
                $configAltNamesChoices,
                array_keys($configAltNamesChoices)
            ),
            Response::HTTP_OK,
            [],
            ['groups' => ['read']]
        );
    }

    public function personAddressApi($id, Request $request, string $_format): Response
    {
        return $this->addRemoveSomething('address', $id, $request, $_format, 'address', Address::class, ['groups' => ['read']]);
    }

    /**
     * @ParamConverter("person", options={"id": "person_id"})
     */
    #[Route(path: '/api/1.0/person/address/suggest/by-person/{person_id}.{_format}', name: 'chill_person_address_suggest_by_person', requirements: ['_format' => 'json'])]
    public function suggestAddress(Person $person, Request $request, string $_format): Response
    {
        $this->denyAccessUnlessGranted(PersonVoter::SEE, $person);

        $seenAddressIds = [];

        // collect addresses from location in courses
        $addresses = $person
            ->getAccompanyingPeriodParticipations()
            ->filter(
                static fn (AccompanyingPeriodParticipation $accompanyingPeriodParticipation): bool => null !== $accompanyingPeriodParticipation->getAccompanyingPeriod()->getAddressLocation()
            )
            ->map(
                static fn (AccompanyingPeriodParticipation $accompanyingPeriodParticipation): ?Address => $accompanyingPeriodParticipation->getAccompanyingPeriod()->getAddressLocation()
            )
            ->filter(
                // We remove potential null addresses.
                static fn (?Address $address): bool => null !== $address
            )
            ->filter(
                static function (Address $address) use (&$seenAddressIds): bool {
                    $id = $address->getId();

                    if (\in_array($id, $seenAddressIds, true)) {
                        return false;
                    }

                    $seenAddressIds[] = $id;

                    return true;
                }
            );

        // remove the actual address
        $actual = $person->getCurrentHouseholdAddress();

        if (null !== $actual) {
            $addresses = $addresses->filter(static fn (Address $address): bool => $address !== $actual);
        }

        return $this->json(
            $addresses->getValues(),
            Response::HTTP_OK,
            [],
            ['groups' => ['read']]
        );
    }
}
