<?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\ApplicationSystem\ParticipantListDto;
use Ferienpass\AdminBundle\Breadcrumb\Breadcrumb;
use Ferienpass\AdminBundle\Controller\TagsResponses;
use Ferienpass\AdminBundle\LiveComponent\MultiSelect;
use Ferienpass\AdminBundle\LiveComponent\MultiSelectAction;
use Ferienpass\AdminBundle\LiveComponent\MultiSelectHandlerInterface;
use Ferienpass\AdminBundle\Routing\Base58;
use Ferienpass\CoreBundle\ConsentManager\ConsentManager;
use Ferienpass\CoreBundle\Entity\Attendance;
use Ferienpass\CoreBundle\Entity\HostConsent;
use Ferienpass\CoreBundle\Entity\Offer\OfferInterface;
use Ferienpass\CoreBundle\Entity\User;
use Ferienpass\CoreBundle\Enum\AttendanceStatus;
use Ferienpass\CoreBundle\Export\Offer\ConsentsOverviewExport;
use Ferienpass\CoreBundle\Export\ParticipantList\PdfExport;
use Ferienpass\CoreBundle\Export\ParticipantList\WordExport;
use Ferienpass\CoreBundle\Export\ParticipantList\XlsxExport;
use Ferienpass\CoreBundle\Facade\AttendanceFacade;
use Ferienpass\CoreBundle\HttpCache\CacheTag;
use Ferienpass\CoreBundle\Repository\AttendanceRepository;
use Ferienpass\CoreBundle\Repository\HostConsentRepository;
use Ferienpass\CoreBundle\Repository\OfferRepositoryInterface;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\DependencyInjection\Attribute\Autowire;
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\Uid\Uuid;

#[Route('/angebote/{uuid}')]
final class OfferParticipantsController extends AbstractController implements MultiSelectHandlerInterface
{
    use TagsResponses;

    public function __construct(
        private readonly HostConsentRepository $consents,
        private readonly AttendanceRepository $attendances,
        #[Autowire(param: 'ferienpass_admin.privacy_consent_text')]
        private readonly string $consentText,
        #[Autowire(param: 'ferienpass_admin.participant_list.hosts_can_edit')]
        private readonly bool $hostsCanEdit,
        private readonly PdfExport $pdfExport,
        private readonly WordExport $wordExport,
        private readonly XlsxExport $xlsxExport,
        private readonly ConsentManager $consentManager
    ) {
    }

    #[Route('/teilnahmeliste', name: 'admin_offers_participants', requirements: ['uuid' => Base58::UUID])]
    #[Route('/teilnahmeliste', name: 'admin_offers_participants_legacy', requirements: ['uuid' => Requirement::UUID])]
    public function __invoke(Uuid $uuid, OfferRepositoryInterface $offers, AttendanceRepository $attendanceRepository, Request $request, EntityManagerInterface $em, AttendanceFacade $attendanceFacade, Breadcrumb $breadcrumb, ParticipantListDto $participantList): Response
    {
        /** @var OfferInterface $offer */
        if (!($offer = $offers->findByUuid($uuid)) instanceof OfferInterface) {
            throw $this->createNotFoundException();
        }

        $this->denyAccessUnlessGranted('participants.view', $offer);

        $qb = $attendanceRepository->createQueryBuilder('i');
        $qb
            ->innerJoin('i.participant', 'p')
            ->innerJoin('i.offer', 'o')
            ->leftJoin('i.activity', 'l')
            ->addSelect('l')
            ->where('o.id = :offer')
            ->setParameter('offer', $offer->getId())
            ->addOrderBy(\sprintf("CASE WHEN i.status = '%s' THEN 1 WHEN i.status = '%s' THEN 2 ELSE 3 END", AttendanceStatus::CONFIRMED, AttendanceStatus::WAITLISTED), 'ASC')
            ->addOrderBy('i.sorting', 'ASC')
            ->addOrderBy('p.lastname', 'ASC')
            ->addOrderBy('p.firstname', 'ASC')
        ;

        if (!$this->isGranted('ROLE_ADMIN')) {
            $qb->andWhere('i.status <> :status_withdrawn')->setParameter('status_withdrawn', AttendanceStatus::WITHDRAWN);

            if (!$this->hostsCanEdit) {
                $qb->andWhere('i.status = :status_confirmed')->setParameter('status_confirmed', AttendanceStatus::CONFIRMED);
            }
        }

        $user = $this->getUser();
        if (!$user instanceof User) {
            return new Response('', Response::HTTP_NO_CONTENT);
        }

        if ('' !== $this->consentText && !$this->consents->findValid($user) instanceof HostConsent) {
            $this->tagResponse(CacheTag::offer($offer));

            return $this->render('@FerienpassAdmin/page/missing_privacy_statement.html.twig', [
                'breadcrumb' => $breadcrumb->generate(
                    ['offers.title', ['route' => 'admin_offers_index', 'routeParameters' => ['edition' => $offer->getEdition()?->getAlias()]]],
                    [$offer->getEdition()?->getName(), ['route' => 'admin_offers_index', 'routeParameters' => ['edition' => $offer->getEdition()?->getAlias()]]],
                    $offer->getName()
                ),
            ]);
        }

        $ms = null;
        if ($this->isGranted('participants.edit', $offer)) {
            $ms = new MultiSelect([
                new MultiSelectAction('changeStatus', 'admin_offers_participants_status', ['uuid' => $offer->getUuid()->toBase58()], 'GET', 'slideover'),
            ], self::class);
        }

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

        return $this->render('@FerienpassAdmin/page/offers/participant_list.html.twig', [
            'qb' => $qb,
            'searchable' => ['p.firstname', 'p.lastname'],
            'ms' => $ms,
            'offer' => $offer,
            'hasConsents' => [] !== $this->consentManager->getConsentsForOffer($offer),
            'hasWordExport' => $this->wordExport->hasTemplate(),
            'columns' => $participantList->adminViewColumns($offer, $request),
            'breadcrumb' => $breadcrumb->generate(
                ['offers.title', ['route' => 'admin_offers_index', 'routeParameters' => array_filter(['edition' => $offer->getEdition()?->getAlias()])]],
                [$offer->getEdition()?->getName(), ['route' => 'admin_offers_index', 'routeParameters' => ['edition' => $offer->getEdition()?->getAlias()]]],
                $offer->getName()
            ),
        ]);
    }

    public function handleMultiSelect(string $action, array $selected, Request $request): Response
    {
        /** @var Attendance[] $selectedParticipants */
        $selectedParticipants = $this->attendances->findBy(['id' => $selected]);
        if (empty($selectedParticipants)) {
            return new Response('', Response::HTTP_BAD_REQUEST);
        }

        $offer = $selectedParticipants[0]->getOffer();

        $this->denyAccessUnlessGranted("participants.$action", $offer);

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

    #[Route('/teilnahmeliste/status', name: 'admin_offers_participants_status', requirements: ['uuid' => Base58::UUID])]
    #[Route('/teilnahmeliste/status', name: 'admin_offers_participants_status_legacy', requirements: ['uuid' => Requirement::UUID])]
    public function status(Uuid $uuid, #[MapQueryParameter('ids')] array $ids, OfferRepositoryInterface $offers, Request $request): Response
    {
        /** @var OfferInterface $offer */
        if (!($offer = $offers->findByUuid($uuid)) instanceof OfferInterface) {
            throw $this->createNotFoundException();
        }

        $this->denyAccessUnlessGranted('participants.edit', $offer);

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

        return $this->render('@FerienpassAdmin/page/offers/participant_status.html.twig', [
            'selected' => $ids,
            'offer' => $offer,
        ]);
    }

    #[Route('/teilnahmeliste.pdf', name: 'admin_offer_participants_pdf', requirements: ['uuid' => Base58::UUID])]
    #[Route('/teilnahmeliste.pdf', name: 'admin_offer_participants_pdf_legacy', requirements: ['uuid' => Requirement::UUID])]
    public function pdf(Uuid $uuid, OfferRepositoryInterface $offerRepository, #[MapQueryParameter] bool $simple = false): Response
    {
        if (!($offer = $offerRepository->findByUuid($uuid)) instanceof OfferInterface) {
            throw $this->createNotFoundException();
        }

        $this->denyAccessUnlessGranted('participants.view', $offer);

        $path = $this->pdfExport->generate($offer, expanded: !$simple);

        return $this->file($path, 'teilnahmeliste.pdf');
    }

    #[Route('/teilnahmeliste.docx', name: 'admin_offer_participants_docx', requirements: ['uuid' => Base58::UUID])]
    #[Route('/teilnahmeliste.docx', name: 'admin_offer_participants_docx_legacy', requirements: ['uuid' => Requirement::UUID])]
    public function docx(Uuid $uuid, OfferRepositoryInterface $offerRepository): Response
    {
        if (!($offer = $offerRepository->findByUuid($uuid)) instanceof OfferInterface) {
            throw $this->createNotFoundException();
        }

        $this->denyAccessUnlessGranted('participants.view', $offer);

        $path = $this->wordExport->generate($offer);

        return $this->file($path, 'teilnahmeliste.docx');
    }

    #[Route('/teilnahmeliste.xlsx', name: 'admin_offer_participants_xlsx', requirements: ['uuid' => Base58::UUID])]
    #[Route('/teilnahmeliste.xlsx', name: 'admin_offer_participants_xlsx_legacy', requirements: ['uuid' => Requirement::UUID])]
    public function xlsx(Uuid $uuid, OfferRepositoryInterface $offerRepository): Response
    {
        if (!($offer = $offerRepository->findByUuid($uuid)) instanceof OfferInterface) {
            throw $this->createNotFoundException();
        }

        $this->denyAccessUnlessGranted('participants.view', $offer);

        $path = $this->xlsxExport->generate($offer);

        return $this->file($path, 'teilnahmeliste.xlsx');
    }

    #[Route('/einwilligungen.pdf', name: 'admin_offer_consents_pdf', requirements: ['uuid' => Base58::UUID])]
    #[Route('/einwilligungen.pdf', name: 'admin_offer_consents_pdf_legacy', requirements: ['uuid' => Requirement::UUID])]
    public function consents(Uuid $uuid, OfferRepositoryInterface $offers, ConsentsOverviewExport $export, #[MapQueryParameter('detailed')] bool $appendSheets = false): Response
    {
        /** @var OfferInterface $offer */
        if (!($offer = $offers->findByUuid($uuid)) instanceof OfferInterface) {
            throw $this->createNotFoundException();
        }

        $this->denyAccessUnlessGranted('participants.view', $offer);

        $path = $export->generate($offer, $appendSheets);

        return $this->file($path, \sprintf('einwilligungen-%s.pdf', $offer->getAlias() ?: $offer->getUuid()->toRfc4122()));
    }
}
