<?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\ActivityBundle\Entity\Activity;
use Chill\MainBundle\Entity\User;
use Chill\PersonBundle\Entity\AccompanyingPeriod;
use Chill\PersonBundle\Entity\Household\Household;
use Chill\PersonBundle\Form\AccompanyingCourseType;
use Chill\PersonBundle\Repository\AccompanyingPeriod\AccompanyingPeriodWorkRepository;
use Chill\PersonBundle\Repository\PersonRepository;
use Chill\PersonBundle\Security\Authorization\AccompanyingPeriodVoter;
use Chill\PersonBundle\Security\Authorization\PersonVoter;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\ParamConverter;
use Symfony\Component\Form\Extension\Core\Type\SubmitType;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;
use Symfony\Component\Routing\Annotation\Route;
use Symfony\Component\Security\Core\Security;
use Symfony\Component\Validator\ConstraintViolationInterface;
use Symfony\Component\Validator\ConstraintViolationListInterface;
use Symfony\Component\Validator\Validator\ValidatorInterface;
use Symfony\Component\Workflow\Registry;
use Symfony\Contracts\Translation\TranslatorInterface;

/**
 * Class AccompanyingCourseController.
 */
final class AccompanyingCourseController extends \Symfony\Bundle\FrameworkBundle\Controller\AbstractController
{
    public function __construct(
        private readonly ValidatorInterface $validator,
        private readonly AccompanyingPeriodWorkRepository $workRepository,
        private readonly Registry $registry,
        private readonly TranslatorInterface $translator,
        private readonly Security $security,
        private readonly PersonRepository $personRepository,
        private readonly \Doctrine\Persistence\ManagerRegistry $managerRegistry,
    ) {}

    /**
     * @ParamConverter("accompanyingCourse", options={"id": "accompanying_period_id"})
     */
    #[Route(path: '/{_locale}/parcours/{accompanying_period_id}/close', name: 'chill_person_accompanying_course_close')]
    public function closeAction(AccompanyingPeriod $accompanyingCourse, Request $request): Response
    {
        $this->denyAccessUnlessGranted(AccompanyingPeriodVoter::EDIT, $accompanyingCourse);

        $form = $this->createForm(AccompanyingCourseType::class, $accompanyingCourse, [
            'validation_groups' => [AccompanyingPeriod::STEP_CLOSED],
        ]);

        $form->handleRequest($request);

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

            $workflow = $this->registry->get($accompanyingCourse);

            if ($workflow->can($accompanyingCourse, 'close')) {
                $workflow->apply($accompanyingCourse, 'close', ['closing_motive' => $form['closingMotive']->getData()]);

                $em->flush();

                return $this->redirectToRoute('chill_person_accompanying_course_index', [
                    'accompanying_period_id' => $accompanyingCourse->getId(),
                ]);
            }
            /** @var ConstraintViolationListInterface $errors */
            $errors = $this->validator->validate($accompanyingCourse, null, [$accompanyingCourse::STEP_CLOSED]);
            $this->addFlash('error', $this->translator->trans('It is not possible to close this course'));

            foreach ($errors as $e) {
                /* @var ConstraintViolationInterface $e */
                $this->addFlash('error', $e->getMessage());
            }
        }

        return $this->render('@ChillPerson/AccompanyingCourse/close.html.twig', [
            'form' => $form->createView(),
            'accompanyingCourse' => $accompanyingCourse,
        ]);
    }

    /**
     * Delete page of Accompanying Course section.
     *
     * @ParamConverter("accompanyingCourse", options={"id": "accompanying_period_id"})
     */
    #[Route(path: '/{_locale}/parcours/{accompanying_period_id}/delete', name: 'chill_person_accompanying_course_delete')]
    public function deleteAction(Request $request, AccompanyingPeriod $accompanyingCourse)
    {
        $em = $this->managerRegistry->getManager();

        $person_id = $request->query->get('person_id');

        $form = $this->createFormBuilder()
            ->setAction($this->generateUrl('chill_person_accompanying_course_delete', [
                'accompanying_period_id' => $accompanyingCourse->getId(),
                'person_id' => $person_id,
            ]))
            ->add('submit', SubmitType::class, ['label' => 'Delete'])
            ->getForm();

        if (Request::METHOD_POST === $request->getMethod()) {
            $form->handleRequest($request);

            if ($form->isValid()) {
                $em->remove($accompanyingCourse);
                $em->flush();

                $this->addFlash('success', $this->translator
                    ->trans('The accompanying course has been successfully removed.'));

                if (null !== $person_id) {
                    return $this->redirectToRoute('chill_person_accompanying_period_list', [
                        'person_id' => $person_id,
                    ]);
                }

                return $this->redirectToRoute('chill_main_homepage');
            }
        }

        if (null !== $person_id) {
            $view = '@ChillPerson/AccompanyingCourse/confirm_delete_person.html.twig';
        } else {
            $view = '@ChillPerson/AccompanyingCourse/confirm_delete_accompanying_course.html.twig';
        }

        return $this->render($view, [
            'accompanyingCourse' => $accompanyingCourse,
            'person_id' => $person_id,
            'delete_form' => $form->createView(),
        ]);
    }

    /**
     * Edit page of Accompanying Course section.
     *
     * the page edit all blocks managed by vuejs component
     *
     * @ParamConverter("accompanyingCourse", options={"id": "accompanying_period_id"})
     */
    #[Route(path: '/{_locale}/parcours/{accompanying_period_id}/edit', name: 'chill_person_accompanying_course_edit')]
    public function editAction(AccompanyingPeriod $accompanyingCourse): Response
    {
        $this->denyAccessUnlessGranted(AccompanyingPeriodVoter::EDIT, $accompanyingCourse);

        return $this->render('@ChillPerson/AccompanyingCourse/edit.html.twig', [
            'accompanyingCourse' => $accompanyingCourse,
        ]);
    }

    /**
     * History page of Accompanying Course section.
     *
     * the page show anti chronologic history with all actions, title of page is 'Accompanying Course History'
     *
     * @ParamConverter("accompanyingCourse", options={"id": "accompanying_period_id"})
     */
    #[Route(path: '/{_locale}/parcours/{accompanying_period_id}/history', name: 'chill_person_accompanying_course_history')]
    public function historyAction(AccompanyingPeriod $accompanyingCourse): Response
    {
        $this->denyAccessUnlessGranted(AccompanyingPeriodVoter::SEE, $accompanyingCourse);

        return $this->render('@ChillPerson/AccompanyingCourse/history.html.twig', [
            'accompanyingCourse' => $accompanyingCourse,
        ]);
    }

    /**
     * Homepage of Accompanying Course section.
     *
     * @ParamConverter("accompanyingCourse", options={"id": "accompanying_period_id"})
     */
    #[Route(path: '/{_locale}/parcours/{accompanying_period_id}', name: 'chill_person_accompanying_course_index')]
    public function indexAction(AccompanyingPeriod $accompanyingCourse): Response
    {
        $this->denyAccessUnlessGranted(AccompanyingPeriodVoter::SEE, $accompanyingCourse);

        // compute some warnings
        // get persons without household
        $withoutHousehold = [];

        foreach ($accompanyingCourse->getCurrentParticipations() as $p) {
            if (false === $p->getPerson()->isSharingHousehold()) {
                $withoutHousehold[] = $p->getPerson();
            }
        }

        $activities = $this->managerRegistry->getManager()->getRepository(Activity::class)->findBy(
            ['accompanyingPeriod' => $accompanyingCourse],
            ['date' => 'DESC', 'id' => 'DESC'],
        );

        $works = $this->workRepository->findByAccompanyingPeriod(
            $accompanyingCourse,
            ['startDate' => 'DESC', 'endDate' => 'DESC'],
        );

        $counters = [
            'activities' => count($activities),
            'openWorks' => count($accompanyingCourse->getOpenWorks()),
            'works' => count($works),
        ];

        return $this->render('@ChillPerson/AccompanyingCourse/index.html.twig', [
            'accompanyingCourse' => $accompanyingCourse,
            'withoutHousehold' => $withoutHousehold,
            'participationsByHousehold' => $accompanyingCourse->actualParticipationsByHousehold(),
            'works' => \array_slice($works, 0, 3),
            'activities' => \array_slice($activities, 0, 3),
            'counters' => $counters,
        ]);
    }

    #[Route(path: '/{_locale}/person/parcours/new', name: 'chill_person_accompanying_course_new')]
    public function newAction(Request $request): Response
    {
        $user = $this->security->getUser();

        if (!$user instanceof User) {
            throw new AccessDeniedHttpException();
        }

        $period = new AccompanyingPeriod();
        $em = $this->managerRegistry->getManager();

        $personIds = $request->query->all('person_id');

        foreach ($personIds as $personId) {
            $person = $this->personRepository->find($personId);

            if (null !== $person) {
                if (!$this->isGranted(PersonVoter::SEE, $person)) {
                    throw new AccessDeniedHttpException(sprintf('person with id %d cannot be seen', $person->getId()));
                }
                $period->addPerson($person);
            }
        }

        $userLocation = $user->getCurrentLocation();
        $period->setAdministrativeLocation($userLocation);

        $this->denyAccessUnlessGranted(AccompanyingPeriodVoter::CREATE, $period);

        $em->persist($period);
        $em->flush();

        return $this->redirectToRoute('chill_person_accompanying_course_edit', [
            'accompanying_period_id' => $period->getId(),
        ]);
    }

    #[Route(path: '/{_locale}/person/household/parcours/new', name: 'chill_household_accompanying_course_new')]
    public function newHouseholdParcoursAction(Request $request): Response
    {
        $user = $this->getUser();

        if (!$user instanceof User || !$this->security->isGranted('ROLE_USER')) {
            throw new AccessDeniedHttpException();
        }

        $period = new AccompanyingPeriod();
        $em = $this->managerRegistry->getManager();

        if ($request->query->has('household_id')) {
            $householdId = $request->query->get('household_id');

            $household = $em->getRepository(Household::class)->find($householdId);
            $members = $household->getCurrentMembers();

            if (null !== $members) {
                foreach ($members as $m) {
                    $period->addPerson($m->getPerson());
                }
            }
        }

        $period->setAdministrativeLocation($user->getCurrentLocation());

        $this->denyAccessUnlessGranted(AccompanyingPeriodVoter::CREATE, $period);

        $em->persist($period);
        $em->flush();

        return $this->redirectToRoute('chill_person_accompanying_course_edit', [
            'accompanying_period_id' => $period->getId(),
        ]);
    }

    /**
     * @ParamConverter("accompanyingCourse", options={"id": "accompanying_period_id"})
     */
    #[Route(path: '/{_locale}/parcours/{accompanying_period_id}/open', name: 'chill_person_accompanying_course_reopen')]
    public function reOpenAction(AccompanyingPeriod $accompanyingCourse, Request $request): Response
    {
        $this->denyAccessUnlessGranted(AccompanyingPeriodVoter::SEE, $accompanyingCourse);

        if (null === $accompanyingCourse) {
            throw $this->createNotFoundException('period not found');
        }

        $form = $this->createFormBuilder([])->getForm();
        $form->handleRequest($request);

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

            return $this->redirectToRoute('chill_person_accompanying_course_index', [
                'accompanying_period_id' => $accompanyingCourse->getId(),
            ]);
        }

        return $this->render('@ChillPerson/AccompanyingCourse/re_open.html.twig', [
            'form' => $form->createView(),
            'accompanyingCourse' => $accompanyingCourse,
        ]);
    }
}
