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

use Contao\CoreBundle\Filesystem\VirtualFilesystemInterface;
use Contao\CoreBundle\Framework\ContaoFramework;
use Doctrine\ORM\EntityManagerInterface;
use Ferienpass\AdminBundle\Breadcrumb\Breadcrumb;
use Ferienpass\AdminBundle\Form\EditNotificationType;
use Ferienpass\AdminBundle\Service\FileUploader;
use Ferienpass\CoreBundle\Entity\DbafsAttachment;
use Ferienpass\CoreBundle\Entity\Edition;
use Ferienpass\CoreBundle\Entity\Notification;
use Ferienpass\CoreBundle\Notification\AbstractNotification;
use Ferienpass\CoreBundle\Notification\EditionAwareNotificationInterface;
use Ferienpass\CoreBundle\Notification\EmailToAwareNotificationInterface;
use Ferienpass\CoreBundle\Notifier\Notifier;
use Ferienpass\CoreBundle\Repository\NotificationRepository;
use Symfony\Bridge\Doctrine\Attribute\MapEntity;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\Form\SubmitButton;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Notifier\Notification\EmailNotificationInterface;
use Symfony\Component\Notifier\Notification\SmsNotificationInterface;
use Symfony\Component\Notifier\Recipient\Recipient;
use Symfony\Component\Routing\Attribute\Route;
use Symfony\Component\Security\Http\Attribute\IsGranted;
use Symfony\Contracts\Translation\TranslatorInterface;
use Symfony\UX\Turbo\TurboBundle;

#[IsGranted('ROLE_ADMIN')]
final class NotificationsController extends AbstractController
{
    public function __construct(private readonly TranslatorInterface $translator, private readonly FileUploader $fileUploader, private readonly ContaoFramework $contaoFramework)
    {
    }

    #[Route('/benachrichtigungen/{type?}/{edition?}', name: 'admin_notifications', defaults: ['view' => 'preview'])]
    #[Route('/benachrichtigungen/{type}/bearbeiten', name: 'admin_notifications_edit', priority: 2, defaults: ['view' => 'edit'])]
    #[Route('/benachrichtigungen/{type}/{edition}/bearbeiten', name: 'admin_notifications_edit_in_edition', priority: 2, defaults: ['view' => 'edit'])]
    #[Route('/benachrichtigungen/{type?}/neu', name: 'admin_notifications_new', priority: 2, defaults: ['view' => 'edit'])]
    public function index(?string $type, #[MapEntity(mapping: ['edition' => 'alias'])] ?Edition $edition, Request $request, Notifier $notifier, NotificationRepository $repository, Breadcrumb $breadcrumb, EntityManagerInterface $em, VirtualFilesystemInterface $attachmentsStorage, string $view = 'preview'): Response
    {
        if (null === $type) {
            return $this->render('@FerienpassAdmin/page/notifications/index.html.twig', [
                'types' => $notifier->types(),
                'notifier' => $notifier,
                'breadcrumb' => $breadcrumb->generate('tools.title', 'notifications.title'),
            ]);
        }

        if (!\in_array($type, $notifier->types(), true)) {
            throw $this->createNotFoundException();
        }

        if ($request->get('edition') && !$edition instanceof Edition) {
            throw $this->createNotFoundException();
        }

        $editions = $em->createQuery('SELECT e FROM '.Edition::class.' e WHERE e IN (SELECT IDENTITY(n.edition) FROM '.Notification::class.' n WHERE n.type = :type)')->setParameter('type', $type)->getResult();
        $entity = $repository->findOneBy(['type' => $type, 'edition' => $edition]) ?? $this->newNotificationForType($type);
        if ($edition instanceof Edition && null === $entity->getEdition()) {
            $entity->setEdition($edition);
        }

        // Hardcoded sample values shown in the "Verfügbare Tokens" modal.
        // Mirrors the sample entities used by AbstractNotification::createMock(),
        // so the modal preview and the rendered email preview line up.
        $tokenSamples = [
            'baseUrl' => $request->getSchemeAndHttpHost().$request->getBaseUrl(),
            'user' => [
                'firstname' => 'Erika',
                'lastname' => 'Mustermann',
                'name' => 'Erika Mustermann',
                'email' => 'erika.mustermann@example.com',
                'phone' => '+49 30 1234567',
            ],
            'offer' => [
                'name' => 'Schwimmkurs für Anfänger',
                'description' => 'Ein Beispielangebot zur Veranschaulichung.',
            ],
            'participant' => [
                'firstname' => 'Lena',
                'lastname' => 'Mustermann',
                'name' => 'Lena Mustermann',
            ],
            'attendance' => [
                'status' => 'confirmed',
            ],
            'token' => 'example-token',
            'inviteeEmail' => 'example@example.org',
        ];

        $availableTokens = [];
        $notificationClass = $notifier->getClass($type);
        if (is_subclass_of($notificationClass, AbstractNotification::class)) {
            foreach ($notificationClass::getAvailableTokens() as $tokenKey) {
                $sample = $tokenSamples[$tokenKey] ?? null;
                if (\is_array($sample)) {
                    foreach ($sample as $prop => $value) {
                        $availableTokens["$tokenKey.$prop"] = (string) $value;
                    }
                } elseif (null !== $sample) {
                    $availableTokens[$tokenKey] = (string) $sample;
                }
            }
        }

        $form = $this->createForm(EditNotificationType::class, $entity, [
            'notification_type' => $type,
            'supports_sms' => is_subclass_of($notifier->getClass($type), SmsNotificationInterface::class),
            'new_edition' => 'admin_notifications_new' === $request->attributes->get('_canonical_route', $request->get('_route')),
            'can_delete' => $edition instanceof Edition,
            'supports_email_to' => is_subclass_of($notifier->getClass($type), EmailToAwareNotificationInterface::class),
            'available_tokens' => $availableTokens,
        ]);

        $form->handleRequest($request);
        if ($form->isSubmitted() && $form->isValid()) {
            if (!$em->contains($entity)) {
                $em->persist($entity);
            }

            if ($form->has('delete') && ($button = $form->get('delete')) && $button instanceof SubmitButton && $button->isClicked()) {
                $em->remove($entity);
                $em->flush();

                if (TurboBundle::STREAM_FORMAT === $request->getPreferredFormat()) {
                    $request->setRequestFormat(TurboBundle::STREAM_FORMAT);

                    return $this->renderBlock('@FerienpassAdmin/page/notifications/index.html.twig', 'deleted_stream', ['type' => $type]);
                }

                return $this->redirectToRoute('admin_notifications', ['type' => $type]);
            }

            if ($pdfFile = $form->get('emailAttachment')->getData()) {
                $this->contaoFramework->initialize();

                $uuid = $this->fileUploader->upload($attachmentsStorage, $pdfFile);

                $entity->setEmailAttachment($em->getReference(DbafsAttachment::class, $uuid));
            }

            $em->flush();

            if (TurboBundle::STREAM_FORMAT === $request->getPreferredFormat()) {
                $request->setRequestFormat(TurboBundle::STREAM_FORMAT);

                return $this->renderBlock('@FerienpassAdmin/page/notifications/index.html.twig', 'success_stream', ['type' => $type, 'edition' => $edition]);
            }

            return $edition instanceof Edition
                ? $this->redirectToRoute('admin_notifications_edit_in_edition', ['type' => $type, 'edition' => $edition->getAlias()])
                : $this->redirectToRoute('admin_notifications_edit', ['type' => $type]);
        }

        $mockNotification = $notifier->createMock($type, $entity->getEmailSubject() ?? '', $entity->getEmailText() ?? '');
        $mockEmail = $mockNotification instanceof EmailNotificationInterface ? $mockNotification->asEmailMessage(new Recipient('example@example.org')) : null;

        /** @noinspection FormViewTemplate `createView()` messes ups error handling/redirect */
        return $this->render('@FerienpassAdmin/page/notifications/index.html.twig', [
            'form' => $form,
            'edition' => $edition,
            'editions' => $editions,
            'canEditEditions' => is_subclass_of($notificationClass, EditionAwareNotificationInterface::class),
            'mockEmail' => $mockEmail,
            'types' => $notifier->types(),
            'notifier' => $notifier,
            'view' => $view,
            'breadcrumb' => $breadcrumb->generate('tools.title', ['notifications.title', ['route' => 'admin_notifications']], 'notifications.'.$type.'.0', $edition?->getName()),
        ]);
    }

    private function newNotificationForType(string $type): Notification
    {
        $notification = new Notification($type);

        $getTransKey = static fn (string $transKey): string => \sprintf('defaults.%s.%s', $type, $transKey);

        $transKey = $getTransKey('emailText');
        if ($transKey !== ($emailText = $this->translator->trans($transKey, domain: 'notifications'))) {
            $notification->setEmailText($emailText);
        }

        $transKey = $getTransKey('emailSubject');
        if ($transKey !== ($emailSubject = $this->translator->trans($transKey, domain: 'notifications'))) {
            $notification->setEmailSubject($emailSubject);
        }

        $transKey = $getTransKey('smsText');
        if ($transKey !== ($smsText = $this->translator->trans($transKey, domain: 'notifications'))) {
            $notification->setSmsText($smsText);
        }

        return $notification;
    }
}
