<?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 Doctrine\ORM\EntityManagerInterface;
use Ferienpass\AdminBundle\Breadcrumb\Breadcrumb;
use Ferienpass\AdminBundle\Controller\TagsResponses;
use Ferienpass\AdminBundle\Export\ExportQueryBuilderInterface;
use Ferienpass\AdminBundle\Form\EditHostType;
use Ferienpass\AdminBundle\Form\Filter\HostsFilter;
use Ferienpass\AdminBundle\Form\FormScreenFactory;
use Ferienpass\AdminBundle\Form\HandleFormTrait;
use Ferienpass\AdminBundle\List\ListCriteriaApplier;
use Ferienpass\AdminBundle\Routing\Base58;
use Ferienpass\CoreBundle\Entity\Host;
use Ferienpass\CoreBundle\HttpCache\CacheTag;
use Ferienpass\CoreBundle\Pagination\Paginator;
use Ferienpass\CoreBundle\Repository\HostRepository;
use Ferienpass\CoreBundle\Repository\OfferRepositoryInterface;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\DependencyInjection\Attribute\TaggedLocator;
use Symfony\Component\DependencyInjection\ServiceLocator;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
use Symfony\Component\Form\Extension\Core\Type\FormType;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\Attribute\MapQueryParameter;
use Symfony\Component\Routing\Attribute\Route;
use Symfony\Component\Routing\Requirement\Requirement;
use Symfony\Component\Security\Http\Attribute\IsGranted;
use Symfony\Component\Uid\Uuid;
use Symfony\UX\Turbo\TurboBundle;

#[IsGranted('ROLE_ADMIN')]
#[Route('/veranstaltende')]
final class HostsController extends AbstractController
{
    use HandleFormTrait;
    use TagsResponses;

    public function __construct(#[TaggedLocator(ExportQueryBuilderInterface::class, defaultIndexMethod: 'getFormat')] private readonly ServiceLocator $exporters)
    {
    }

    #[Route('', name: 'admin_hosts_index')]
    public function index(HostRepository $repository, Request $request, Breadcrumb $breadcrumb): Response
    {
        $qb = $repository->createQueryBuilder('i');

        $qb
            // Solve N+1 for memberAssociations
            ->leftJoin('i.memberAssociations', 'ma')
            ->addSelect('ma')
            ->leftJoin('ma.user', 'u')
            ->addSelect('u')
            // Solve N+1 for offer
            ->leftJoin('i.offers', 'o')
            ->addSelect('o')
            // Solve N+1 for log
            ->leftJoin('i.activity', 'l')
            ->addSelect('l')
            // Solve N+1 for postalAddresses
            ->leftJoin('i.postalAddresses', 'pa')
            ->addSelect('pa')
        ;

        $paginator = new Paginator($qb)->paginate($request->query->getInt('page', 1));

        return $this->render('@FerienpassAdmin/page/hosts/index.html.twig', [
            'qb' => $qb,
            'filterType' => HostsFilter::class,
            'exports' => array_keys($this->exporters->getProvidedServices()),
            'searchable' => ['name'],
            'createUrl' => $this->generateUrl('admin_hosts_create'),
            'pagination' => $paginator,
            'breadcrumb' => $breadcrumb->generate('hosts.title'),
        ]);
    }

    #[Route('/{uuid}', name: 'admin_hosts_show', requirements: ['uuid' => Base58::UUID])]
    #[Route('/{uuid}', name: 'admin_hosts_show_legacy', requirements: ['uuid' => Requirement::UUID])]
    public function show(Uuid $uuid, #[MapQueryParameter(name: 'kommentare')] ?bool $commentsOnly, HostRepository $hostRepository, OfferRepositoryInterface $repository, Breadcrumb $breadcrumb): Response
    {
        $host = $hostRepository->findOneBy(['uuid' => $uuid]);
        if (null === $host) {
            throw $this->createNotFoundException();
        }

        $this->denyAccessUnlessGranted('view', $host);

        $this->tagResponse(CacheTag::host($host));

        return $this->render('@FerienpassAdmin/page/hosts/show.html.twig', [
            'item' => $host,
            'commentsOnly' => (bool) $commentsOnly,
            'breadcrumb' => $breadcrumb->generate(['hosts.title', ['route' => 'admin_hosts_index']], $host->getName()),
        ]);
    }

    #[Route('/export.{format}', name: 'admin_hosts_export', requirements: ['format' => '\w+'])]
    public function export(HostRepository $repository, string $format, Request $request, ListCriteriaApplier $filterer)
    {
        $qb = $repository->createQueryBuilder('i');

        $filterer->apply(
            $qb,
            HostsFilter::class,
            ['name'],
            $request->query->get('q'),
            $request->query->get('sort'),
            (array) $request->query->all('filter')
        );

        if (!$this->exporters->has($format)) {
            throw $this->createNotFoundException();
        }

        $exporter = $this->exporters->get($format);

        return $this->file($exporter->generate($qb), "veranstaltende.$format");
    }

    #[Route('/neu/{section?default}', name: 'admin_hosts_create')]
    #[Route('/{uuid}/bearbeiten/{section?default}', name: 'admin_hosts_edit', requirements: ['uuid' => Base58::UUID], priority: -1)]
    #[Route('/{uuid}/bearbeiten/{section?default}', name: 'admin_hosts_edit_legacy', requirements: ['uuid' => Requirement::UUID], priority: -1)]
    public function edit(?Uuid $uuid, string $section, Request $request, HostRepository $hostRepository, EventDispatcherInterface $dispatcher, EntityManagerInterface $entityManager, Breadcrumb $breadcrumb, FormScreenFactory $forms): Response
    {
        $item = null !== $uuid ? $hostRepository->findOneBy(['uuid' => $uuid]) : null;
        $item ??= new Host();

        $form = $this->createForm(EditHostType::class, $item, $formOptions = [
            'display_section' => $section,
        ]);

        $form->handleRequest($request);
        if ($form->isSubmitted() && $form->isValid()) {
            $this->handleSubmit($form, $dispatcher, $request);

            $entityManager->flush();

            $this->addFlash('success', 'Die Änderungen wurden erfolgreich übernommen.');

            if ($nextSection = $request->request->get('nextSection')) {
                return $this->redirectToRoute('admin_hosts_edit', ['uuid' => $item->getUuid()->toBase58(), 'section' => $nextSection]);
            }

            if ($redirect = $request->request->get('redirect')) {
                return $this->redirectToRoute($redirect, ['uuid' => $item->getUuid()->toBase58()]);
            }

            return $this->redirectToRoute('admin_hosts_show', ['uuid' => $item->getUuid()->toBase58()]);
        }

        $breadcrumbTitle = $item->getId() ? $item->getName() : 'hosts.new';

        $this->tagResponse(CacheTag::host($item));

        return $this->render('@FerienpassAdmin/page/hosts/edit.html.twig', [
            'item' => $item,
            'form' => $form,
            'formOptions' => $formOptions,
            'formScreen' => $forms->screen(EditHostType::class, $item),
            'section' => $section,
            'breadcrumb' => $breadcrumb->generate(['hosts.title', ['route' => 'admin_hosts_index']], $breadcrumbTitle),
        ]);
    }

    #[Route('/{uuid}/löschen', name: 'admin_hosts_delete', requirements: ['uuid' => Base58::UUID])]
    #[Route('/{uuid}/löschen', name: 'admin_hosts_delete_legacy', requirements: ['uuid' => Requirement::UUID])]
    public function delete(Uuid $uuid, Request $request, HostRepository $hostRepository, EntityManagerInterface $entityManager): Response
    {
        $item = $hostRepository->findOneBy(['uuid' => $uuid]);
        if (null === $item) {
            throw $this->createNotFoundException();
        }

        $this->denyAccessUnlessGranted('delete', $item);

        $form = $this->createForm(FormType::class, options: [
            'action' => $this->generateUrl('admin_hosts_delete', ['uuid' => $item->getUuid()->toBase58()]),
        ]);

        $form->handleRequest($request);
        if ($form->isSubmitted() && $form->isValid()) {
            $deletedId = $item->getId();

            $entityManager->remove($item);
            $entityManager->flush();

            if (TurboBundle::STREAM_FORMAT === $request->getPreferredFormat()) {
                // If the request comes from Turbo, set the content type as text/vnd.turbo-stream.html and only send the HTML to update
                $request->setRequestFormat(TurboBundle::STREAM_FORMAT);

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

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

        $this->tagResponse(CacheTag::host($item));

        return $this->render('@FerienpassAdmin/page/hosts/delete.html.twig', [
            'item' => $item,
            'form' => $form,
        ]);
    }
}
