<?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\Entity\Address;
use Chill\MainBundle\Form\Type\AddressDateType;
use Chill\PersonBundle\Entity\Household\Household;
use Chill\PersonBundle\Form\HouseholdType;
use Chill\PersonBundle\Repository\Household\PositionRepository;
use Chill\PersonBundle\Security\Authorization\AccompanyingPeriodVoter;
use Chill\PersonBundle\Security\Authorization\HouseholdVoter;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\ParamConverter;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\Form\FormInterface;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpKernel\Exception\BadRequestHttpException;
use Symfony\Component\Routing\Annotation\Route;
use Symfony\Component\Security\Core\Security;
use Symfony\Component\Serializer\Normalizer\AbstractNormalizer;
use Symfony\Component\Serializer\SerializerInterface;
use Symfony\Contracts\Translation\TranslatorInterface;

#[Route(path: '/{_locale}/person/household')]
class HouseholdController extends AbstractController
{
    public function __construct(private readonly TranslatorInterface $translator, private readonly PositionRepository $positionRepository, private readonly SerializerInterface $serializer, private readonly Security $security, private readonly \Doctrine\Persistence\ManagerRegistry $managerRegistry) {}

    /**
     * @ParamConverter("household", options={"id": "household_id"})
     */
    #[Route(path: '/{household_id}/accompanying-period', name: 'chill_person_household_accompanying_period', methods: ['GET', 'HEAD'])]
    public function accompanyingPeriod(Request $request, Household $household)
    {
        $currentMembers = $household->getCurrentPersons();
        $accompanyingPeriods = [];

        foreach ($currentMembers as $p) {
            $accompanyingPeriodsMember = $p->getCurrentAccompanyingPeriods();

            foreach ($accompanyingPeriodsMember as $accompanyingPeriod) {
                if (!$this->security->isGranted(AccompanyingPeriodVoter::SEE, $accompanyingPeriod)) {
                    continue;
                }
                $accompanyingPeriods[$accompanyingPeriod->getId()] = $accompanyingPeriod;
            }
        }

        usort($accompanyingPeriods, static fn ($a, $b) => $b->getOpeningDate() <=> $a->getOpeningDate());

        $oldMembers = $household->getNonCurrentMembers();
        $accompanyingPeriodsOld = [];

        foreach ($oldMembers as $m) {
            $accompanyingPeriodsOldMember = $m->getPerson()->getAccompanyingPeriods();

            foreach ($accompanyingPeriodsOldMember as $accompanyingPeriod) {
                if (!$this->security->isGranted(AccompanyingPeriodVoter::SEE, $accompanyingPeriod)) {
                    continue;
                }
                $id = $accompanyingPeriod->getId();

                if (!\array_key_exists($id, $accompanyingPeriodsOld) && !\array_key_exists($id, $accompanyingPeriods)) {
                    $accompanyingPeriodsOld[$id] = $accompanyingPeriod;
                }
            }
        }

        return $this->render(
            '@ChillPerson/Household/accompanying_period.html.twig',
            [
                'household' => $household,
                'accompanying_periods' => $accompanyingPeriods,
                'accompanying_periods_old' => $accompanyingPeriodsOld,
            ]
        );
    }

    /**
     * @ParamConverter("household", options={"id": "household_id"})
     */
    #[Route(path: '/{household_id}/address/edit', name: 'chill_person_household_address_edit', methods: ['GET', 'HEAD', 'POST'])]
    public function addressEdit(Request $request, Household $household)
    {
        // TODO ACL

        $address_id = $request->query->get('address_id');
        $address = $this->managerRegistry->getManager()
            ->getRepository(Address::class)
            ->find($address_id);

        return $this->render(
            '@ChillPerson/Household/address_edit.html.twig',
            [
                'household' => $household,
                'address' => $address,
            ]
        );
    }

    /**
     * @ParamConverter("household", options={"id": "household_id"})
     */
    #[Route(path: '/{household_id}/addresses', name: 'chill_person_household_addresses', methods: ['GET', 'HEAD'])]
    public function addresses(Request $request, Household $household)
    {
        // TODO ACL

        // TODO put these lines into a validator constraint on household->getAddress
        $addresses = $household->getAddresses();
        $cond = true;

        for ($i = 0; \count($addresses) - 1 > $i; ++$i) {
            if ($addresses[$i]->getValidFrom() !== $addresses[$i + 1]->getValidTo()) {
                $cond = false;
            }
        }

        return $this->render(
            '@ChillPerson/Household/addresses.html.twig',
            [
                'household' => $household,
            ]
        );
    }

    /**
     * @ParamConverter("household", options={"id": "household_id"})
     */
    #[Route(path: '/{household_id}/address/move', name: 'chill_person_household_address_move', methods: ['GET', 'HEAD', 'POST'])]
    public function addressMove(Request $request, Household $household)
    {
        // TODO ACL

        return $this->render(
            '@ChillPerson/Household/address_move.html.twig',
            [
                'household' => $household,
            ]
        );
    }

    /**
     * @ParamConverter("household", options={"id": "household_id"})
     */
    #[Route(path: '/{household_id}/address/edit_valid_from', name: 'chill_person_household_address_valid_from_edit', methods: ['GET', 'HEAD', 'POST'])]
    public function addressValidFromEdit(Request $request, Household $household)
    {
        $this->denyAccessUnlessGranted(HouseholdVoter::EDIT, $household);

        if (!$request->query->has('address_id')) {
            throw new BadRequestHttpException('parameter address_id is missing');
        }

        $address_id = $request->query->getInt('address_id');

        // loop over adresses of the household, to be sure that the household is associated
        // to the edited address
        $address = null;

        foreach ($household->getAddresses() as $householdAddress) {
            if ($householdAddress->getId() === $address_id) {
                $address = $householdAddress;
            }
        }

        if (null === $address) {
            throw new BadRequestHttpException('The edited address does not belongs to the household');
        }

        $form = $this->createForm(AddressDateType::class, $address, []);

        $form->handleRequest($request);

        if ($form->isSubmitted() && $form->isValid()) {
            $household->makeAddressConsistent();

            $this->managerRegistry->getManager()->flush();

            return $this->redirectToRoute('chill_person_household_addresses', [
                'household_id' => $household->getId(),
            ]);
        }

        return $this->render(
            '@ChillPerson/Household/address_valid_from_edit.html.twig',
            [
                'household' => $household,
                'address' => $address,
                'form' => $form->createView(),
            ]
        );
    }

    /**
     * @ParamConverter("household", options={"id": "household_id"})
     */
    #[Route(path: '/{household_id}/members/metadata/edit', name: 'chill_person_household_members_metadata_edit', methods: ['GET', 'POST'])]
    public function editHouseholdMetadata(Request $request, Household $household)
    {
        // TODO ACL
        $form = $this->createMetadataForm($household);

        $form->handleRequest($request);

        if ($form->isSubmitted() && $form->isValid()) {
            $this->managerRegistry->getManager()->flush();

            $this->addFlash('success', $this->translator->trans('household.data_saved'));

            return $this->redirectToRoute('chill_person_household_summary', [
                'household_id' => $household->getId(),
            ]);
        }
        if ($form->isSubmitted() && !$form->isValid()) {
            $this->addFlash('error', $this->translator->trans('This form contains errors'));
            foreach ($form->getErrors() as $error) {
                $this->addFlash('error', $error->getMessage());
            }
        }

        return $this->render('@ChillPerson/Household/edit_member_metadata.html.twig', [
            'household' => $household,
            'form' => $form->createView(),
        ]);
    }

    /**
     * @ParamConverter("household", options={"id": "household_id"})
     */
    #[Route(path: '/{household_id}/relationship', name: 'chill_person_household_relationship', methods: ['GET', 'HEAD'])]
    public function showRelationship(Request $request, Household $household)
    {
        $jsonString = $this->serializer->serialize(
            $household->getCurrentPersons(),
            'json',
            [AbstractNormalizer::GROUPS => ['read']]
        );

        return $this->render(
            '@ChillPerson/Household/relationship.html.twig',
            [
                'household' => $household,
                'persons' => $jsonString,
            ]
        );
    }

    /**
     * @ParamConverter("household", options={"id": "household_id"})
     */
    #[Route(path: '/{household_id}/summary', name: 'chill_person_household_summary', methods: ['GET', 'HEAD'])]
    public function summary(Request $request, Household $household)
    {
        // TODO ACL

        $positions = $this->positionRepository
            ->findByActiveOrdered();

        // little performance improvement:
        // initialize members collection, which will avoid
        // some queries
        $household->getMembers()->initialize();

        if ($request->query->has('edit')) {
            $form = $this->createMetadataForm($household);
        } else {
            $form = null;
        }

        return $this->render(
            '@ChillPerson/Household/summary.html.twig',
            [
                'household' => $household,
                'positions' => $positions,
                'form' => $form?->createView(),
            ]
        );
    }

    private function createMetadataForm(Household $household): FormInterface
    {
        return $this->createForm(
            HouseholdType::class,
            $household,
            [
                'action' => $this->generateUrl(
                    'chill_person_household_members_metadata_edit',
                    [
                        'household_id' => $household->getId(),
                    ]
                ),
            ]
        );
    }
}
