<?php

declare(strict_types=1);

/*
 * This file is part of the Ferienpass package.
 *
 * (c) Richard Henkenjohann <richard@ferienpass.online>
 *
 * For more information visit the project website <https://ferienpass.online>
 * or the documentation under <https://docs.ferienpass.online>.
 */

namespace Ferienpass\CmsBundle\Controller\Fragment;

use Ferienpass\CmsBundle\Exception\TurboStreamResponseException;
use Ferienpass\CmsBundle\Form\SimpleType\ContaoRequestTokenType;
use Ferienpass\CoreBundle\ApplicationSystem\ApplicationSystems;
use Ferienpass\CoreBundle\ApplicationSystem\LotApplicationSystem;
use Ferienpass\CoreBundle\Controller\AbstractPrivateFragmentController;
use Ferienpass\CoreBundle\Enum\AttendanceTransition;
use Ferienpass\CoreBundle\Message\DeleteAttendance;
use Ferienpass\CoreBundle\Repository\AttendanceRepository;
use Ferienpass\CoreBundle\Settings\SystemSettingsManager;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpFoundation\Session\Session;
use Symfony\Component\HttpKernel\Attribute\MapQueryParameter;
use Symfony\Component\Messenger\MessageBusInterface;
use Symfony\Component\Workflow\WorkflowInterface;
use Symfony\UX\Turbo\TurboBundle;

class AttendanceWithdraw extends AbstractPrivateFragmentController
{
    public function __construct(private readonly AttendanceRepository $attendances, private readonly ApplicationSystems $applicationSystems, private readonly WorkflowInterface $attendanceStateMachine, private readonly SystemSettingsManager $systemSettingsManager, private readonly MessageBusInterface $commandBus)
    {
    }

    public function __invoke(#[MapQueryParameter(name: 'id')] int $attendanceId, Request $request, Session $session): Response
    {
        if (
            (null === $attendance = $this->attendances->find($attendanceId))
            || (!$this->attendanceStateMachine->can($attendance, AttendanceTransition::WITHDRAW))
            || (!($applicationSystem = $this->applicationSystems->findApplicationSystem($attendance->getOffer())) instanceof \Ferienpass\CoreBundle\ApplicationSystem\ApplicationSystemInterface)
        ) {
            throw $this->createNotFoundException();
        }

        $this->denyAccessUnlessGranted(AttendanceTransition::WITHDRAW, $attendance);

        $form = $this->createFormBuilder(options: [
            'action' => $this->generateUrl('applications', ['action' => 'abmelden', 'id' => $attendanceId]),
        ])
            ->add('requestToken', ContaoRequestTokenType::class)
            ->getForm()
        ;

        $shallDelete = $applicationSystem instanceof LotApplicationSystem;

        $form->handleRequest($request);
        if ($form->isSubmitted() && $form->isValid()) {
            if ($shallDelete) {
                $this->commandBus->dispatch(new DeleteAttendance((string) $attendance->getUuid()));
            } else {
                $this->attendanceStateMachine->apply($attendance, AttendanceTransition::WITHDRAW, ['notify' => false]);
            }

            // If the request comes from Turbo, set the content type as text/vnd.turbo-stream.html and only send the HTML to update
            if (\in_array(TurboBundle::STREAM_MEDIA_TYPE, $request->getAcceptableContentTypes(), true)) {
                if ($shallDelete) {
                    throw new TurboStreamResponseException($this->markResponseAsPrivate($this->renderBlock('@Contao/fragment/application_list.html.twig', 'deleted_success_stream', ['attendance' => $attendance])));
                }

                throw new TurboStreamResponseException($this->markResponseAsPrivate($this->renderBlock('@Contao/fragment/application_list.html.twig', 'withdrawn_success_stream', ['attendance' => $attendance, 'applicationSystem' => $applicationSystem])));
            }

            return $this->redirectToRoute('applications', status: Response::HTTP_SEE_OTHER);
        }

        $gracePeriodStr = $this->systemSettingsManager->getWithdrawGracePeriod();
        $gracePeriod = $gracePeriodStr ? new \DateTimeImmutable($gracePeriodStr) : null;

        return $this->render('@Contao/fragment/attendance_withdraw.html.twig', [
            'attendance' => $attendance,
            'cancel' => $form,
            // A withdrawal right after registering is free of charge as well, so it
            // must not be announced as the fee-incurring kind.
            'inGracePeriod' => $attendance->isInstantWithdrawal()
                || !$gracePeriod instanceof \DateTimeImmutable
                || ($attendance->getOffer()->getDates()->isEmpty() || $attendance->getOffer()->getDates()->first()->getBegin() > $gracePeriod),
        ]);
    }
}
