<?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\User;
use Chill\MainBundle\Pagination\PaginatorFactory;
use Chill\MainBundle\Templating\Listing\FilterOrderHelper;
use Chill\MainBundle\Templating\Listing\FilterOrderHelperFactoryInterface;
use Chill\MainBundle\Templating\TranslatableStringHelperInterface;
use Chill\PersonBundle\Entity\AccompanyingPeriod;
use Chill\PersonBundle\Entity\AccompanyingPeriod\AccompanyingPeriodWork;
use Chill\PersonBundle\Entity\SocialWork\SocialAction;
use Chill\PersonBundle\Repository\AccompanyingPeriod\AccompanyingPeriodWorkRepository;
use Chill\PersonBundle\Security\Authorization\AccompanyingPeriodWorkVoter;
use Psr\Log\LoggerInterface;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\Form\Extension\Core\Type\SubmitType;
use Symfony\Component\Form\FormInterface;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;
use Symfony\Component\Serializer\SerializerInterface;
use Symfony\Contracts\Translation\TranslatorInterface;

final class AccompanyingCourseWorkController extends AbstractController
{
    public function __construct(
        private readonly TranslatorInterface $trans,
        private readonly SerializerInterface $serializer,
        private readonly AccompanyingPeriodWorkRepository $workRepository,
        private readonly PaginatorFactory $paginator,
        private readonly LoggerInterface $chillLogger,
        private readonly TranslatableStringHelperInterface $translatableStringHelper,
        private readonly FilterOrderHelperFactoryInterface $filterOrderHelperFactory,
        private readonly \Doctrine\Persistence\ManagerRegistry $managerRegistry,
    ) {}

    #[Route(path: '{_locale}/person/accompanying-period/{id}/work/new', name: 'chill_person_accompanying_period_work_new', methods: ['GET'])]
    public function createWork(AccompanyingPeriod $period): Response
    {
        $this->denyAccessUnlessGranted(AccompanyingPeriodWorkVoter::CREATE, $period);

        if (0 === $period->getSocialIssues()->count()) {
            $this->addFlash(
                'error',
                $this->trans->trans(
                    'accompanying_work.You must add at least '.
                    'one social issue on accompanying period'
                )
            );

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

        $json = $this->serializer->normalize($period, 'json', ['groups' => ['read']]);

        return $this->render('@ChillPerson/AccompanyingCourseWork/create.html.twig', [
            'accompanyingCourse' => $period,
            'json' => $json,
        ]);
    }

    #[Route(path: '{_locale}/person/accompanying-period/work/{id}/delete', name: 'chill_person_accompanying_period_work_delete', methods: ['GET', 'POST', 'DELETE'])]
    public function deleteWork(AccompanyingPeriodWork $work, Request $request): Response
    {
        $this->denyAccessUnlessGranted(AccompanyingPeriodWorkVoter::UPDATE, $work);

        $em = $this->managerRegistry->getManager();

        $form = $this->createDeleteForm($work->getId());

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

            if ($form->isSubmitted() && $form->isValid()) {
                $this->chillLogger->notice('An accompanying period work has been removed', [
                    'by_user' => $this->getUser()->getUsername(),
                    'work_id' => $work->getId(),
                    'accompanying_period_id' => $work->getAccompanyingPeriod()->getId(),
                ]);

                $em->remove($work);
                $em->flush();

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

                return $this->redirectToRoute('chill_person_accompanying_period_work_list', [
                    'id' => $work->getAccompanyingPeriod()->getId(),
                ]);
            }
        }

        return $this->render('@ChillPerson/AccompanyingCourseWork/delete.html.twig', [
            'accompanyingCourse' => $work->getAccompanyingPeriod(),
            'work' => $work,
            'delete_form' => $form->createView(),
        ]);
    }

    #[Route(path: '{_locale}/person/accompanying-period/work/{id}/edit', name: 'chill_person_accompanying_period_work_edit', methods: ['GET'])]
    public function editWork(AccompanyingPeriodWork $work): Response
    {
        $this->denyAccessUnlessGranted(AccompanyingPeriodWorkVoter::UPDATE, $work);

        $json = $this->serializer->normalize($work, 'json', ['groups' => ['read']]);

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

    #[Route(path: '{_locale}/person/accompanying-period/{id}/work', name: 'chill_person_accompanying_period_work_list', methods: ['GET'])]
    public function listWorkByAccompanyingPeriod(AccompanyingPeriod $period): Response
    {
        $this->denyAccessUnlessGranted(AccompanyingPeriodWorkVoter::SEE, $period);

        $filter = $this->buildFilterOrder($period);
        $currentUser = $this->getUser();

        $filterData = [
            'types' => $filter->hasEntityChoice('typesFilter') ? $filter->getEntityChoiceData('typesFilter') : [],
            'before' => $filter->getDateRangeData('dateFilter')['to'],
            'after' => $filter->getDateRangeData('dateFilter')['from'],
            'user' => $filter->getUserPickerData('userFilter'),
        ];

        if ($filter->getSingleCheckboxData('currentUserFilter') && $currentUser instanceof User) {
            $filterData['currentUser'] = $currentUser;
        }

        $totalItems = $this->workRepository->countByAccompanyingPeriod($period);
        $paginator = $this->paginator->create($totalItems);

        $works = $this->workRepository->findByAccompanyingPeriodOpenFirst(
            $period,
            $filterData,
            $paginator->getItemsPerPage(),
            $paginator->getCurrentPageFirstItemNumber()
        );

        return $this->render('@ChillPerson/AccompanyingCourseWork/index.html.twig', [
            'accompanyingCourse' => $period,
            'works' => $works,
            'paginator' => $paginator,
            'filter' => $filter,
        ]);
    }

    #[Route(path: '{_locale}/person/accompanying-period/work/{id}/show', name: 'chill_person_accompanying_period_work_show', methods: ['GET'])]
    public function showWork(AccompanyingPeriodWork $work): Response
    {
        if (null === $work) {
            throw $this->createNotFoundException('Unable to find Work entity.');
        }

        $this->denyAccessUnlessGranted(AccompanyingPeriodWorkVoter::SEE, $work);

        return $this->render('@ChillPerson/AccompanyingCourseWork/show.html.twig', [
            'accompanyingCourse' => $work->getAccompanyingPeriod(),
            'work' => $work,
        ]);
    }

    private function createDeleteForm(int $id): FormInterface
    {
        $params = [];
        $params['id'] = $id;

        return $this->createFormBuilder()
            ->setAction($this->generateUrl('chill_person_accompanying_period_work_delete', $params))
            ->add('submit', SubmitType::class, ['label' => 'Delete'])
            ->getForm();
    }

    private function buildFilterOrder($associatedPeriod): FilterOrderHelper
    {
        $filterBuilder = $this->filterOrderHelperFactory->create(self::class);
        $types = $this->workRepository->findActionTypeByPeriod($associatedPeriod);

        $filterBuilder
            ->addDateRange('dateFilter', 'accompanying_course_work.date_filter');

        if (1 < count($types)) {
            $filterBuilder
                ->addEntityChoice('typesFilter', 'accompanying_course_work.types_filter', SocialAction::class, $types, [
                    'choice_label' => fn (SocialAction $sa) => $this->translatableStringHelper->localize($sa->getTitle()),
                ]);
        }

        $filterBuilder
            ->addUserPicker('userFilter', 'accompanying_course_work.user_filter', ['required' => false])
        ;

        $filterBuilder->addSingleCheckbox('currentUserFilter', 'accompanying_course_work.my_actions_filter');

        return $filterBuilder->build();
    }
}
