<?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\Components;

use Contao\StringUtil;
use Doctrine\ORM\EntityManagerInterface;
use Doctrine\ORM\QueryBuilder;
use Ferienpass\AdminBundle\Menu\ActionsBuilder;
use Ferienpass\CoreBundle\Entity\Edition;
use Ferienpass\CoreBundle\Entity\Offer\OfferInterface;
use Ferienpass\CoreBundle\Enum\OfferStatus;
use Ferienpass\CoreBundle\Notification\MailingNotification;
use Ferienpass\CoreBundle\Notifier\Notifier;
use Ferienpass\CoreBundle\Repository\EditionRepository;
use Ferienpass\CoreBundle\Repository\HostRepository;
use Ferienpass\CoreBundle\Repository\OfferRepositoryInterface;
use Ferienpass\CoreBundle\Repository\ParticipantRepositoryInterface;
use Ferienpass\CoreBundle\Repository\UserRepository;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\RequestStack;
use Symfony\Component\Serializer\Normalizer\NormalizerInterface;
use Symfony\Component\Workflow\Exception\NotEnabledTransitionException;
use Symfony\Component\Workflow\WorkflowInterface;
use Symfony\UX\LiveComponent\Attribute\AsLiveComponent;
use Symfony\UX\LiveComponent\Attribute\LiveArg;
use Symfony\UX\LiveComponent\Attribute\LiveListener;
use Symfony\UX\LiveComponent\Attribute\LiveProp;
use Symfony\UX\LiveComponent\ComponentToolsTrait;
use Symfony\UX\LiveComponent\DefaultActionTrait;
use Symfony\UX\LiveComponent\Metadata\UrlMapping;
use Symfony\UX\LiveComponent\ValidatableComponentTrait;
use Symfony\UX\TwigComponent\Attribute\ExposeInTemplate;
use Symfony\UX\TwigComponent\Attribute\PostMount;
use Twig\Environment;

#[AsLiveComponent(route: 'live_component_admin')]
class OffersReview extends AbstractController
{
    use ComponentToolsTrait;
    use DefaultActionTrait;
    use ValidatableComponentTrait;

    #[LiveProp]
    public ?Edition $edition = null;

    #[LiveProp(onUpdated: 'onOfferUpdated', url: new UrlMapping(as: 'angebot'))]
    public ?OfferInterface $offer = null;

    #[LiveProp(url: new UrlMapping(as: 'format'))]
    public string $format = 'web-details';

    #[LiveProp(writable: true)]
    public string $query = '';

    public function __construct(private readonly EditionRepository $editions, private readonly OfferRepositoryInterface $offers, private readonly ParticipantRepositoryInterface $participants, private readonly UserRepository $users, private readonly HostRepository $hosts, private readonly Environment $twig, private readonly RequestStack $requestStack, private readonly NormalizerInterface $normalizer, private readonly Notifier $notifier, private readonly MailingNotification $mailingNotification, private readonly ActionsBuilder $actionsBuilder)
    {
    }

    public function getMenu(OfferInterface $offer): \Knp\Menu\ItemInterface
    {
        return $this->actionsBuilder->actions([
            'item' => $offer,
            'view' => 'admin_offers_review',
        ]);
    }

    #[ExposeInTemplate]
    public function offers(): iterable
    {
        $finalized = OfferStatus::FINALIZED;
        $draft = OfferStatus::DRAFT;
        $reviewed = OfferStatus::REVIEWED;
        $published = OfferStatus::PUBLISHED;
        $completed = OfferStatus::COMPLETED;

        /** @var QueryBuilder $qb */
        $qb = $this->offers->createQueryBuilder('o');
        $qb
            ->leftJoin('o.hosts', 'h')
            ->leftJoin('o.dates', 'd')
            ->leftJoin('o.attendances', 'a')
            ->leftJoin('o.activity', 'l')
            ->addSelect('l')
            ->leftJoin('o.variants', 'variants')
            ->addSelect('variants')
            ->andWhere("JSON_CONTAINS_PATH(o.status, 'one', '$.$completed') = 0")
            ->addOrderBy("CASE WHEN JSON_CONTAINS_PATH(o.status, 'one', '$.$finalized') = 1 THEN 1 ELSE 0 END", 'DESC')
            ->addOrderBy("CASE WHEN JSON_CONTAINS_PATH(o.status, 'one', '$.$draft') = 1 THEN 1 ELSE 0 END", 'DESC')
            ->addOrderBy("CASE WHEN JSON_CONTAINS_PATH(o.status, 'one', '$.$reviewed') = 1 THEN 1 ELSE 0 END", 'DESC')
            ->addOrderBy("CASE WHEN JSON_CONTAINS_PATH(o.status, 'one', '$.$published') = 1 THEN 1 ELSE 0 END", 'DESC')
            ->addOrderBy('d.begin')
            ->addGroupBy('o.id')
            ->addGroupBy('d.begin')
        ;

        $this->addQueryBuilderSearch($qb);

        if ($this->edition instanceof Edition) {
            $qb->andWhere('o.edition = :edition')->setParameter('edition', $this->edition);
        }

        return $qb->getQuery()->getResult();
    }

    #[LiveListener('open')]
    public function openOffer(#[LiveArg('offer')] int $offerId): void
    {
        $this->offer = $this->offers->find($offerId);
    }

    #[LiveListener('finalize')]
    public function finalize(WorkflowInterface $offerWorkflow, EntityManagerInterface $entityManager): void
    {
        try {
            $offerWorkflow->apply($this->offer, 'finalize');
            $entityManager->flush();
        } catch (NotEnabledTransitionException) {
        }
    }

    #[LiveListener('approve')]
    public function approve(WorkflowInterface $offerWorkflow, EntityManagerInterface $entityManager): void
    {
        try {
            $offerWorkflow->apply($this->offer, 'approve');
            $entityManager->flush();
        } catch (NotEnabledTransitionException) {
        }
    }

    #[LiveListener('publish')]
    public function publish(WorkflowInterface $offerWorkflow, EntityManagerInterface $entityManager): void
    {
        try {
            $offerWorkflow->apply($this->offer, 'publish');
            $entityManager->flush();
        } catch (NotEnabledTransitionException) {
        }
    }

    public function onOfferUpdated($previous): void
    {
    }

    #[PostMount]
    public function postMount(): void
    {
        // Fix invalid entity when ?angebot=
        if (null === $this->offer?->getId()) {
            $this->offer = null;
        }
    }

    private function addQueryBuilderSearch(QueryBuilder $qb): void
    {
        $where = $qb->expr()->andX();
        foreach (array_filter(StringUtil::trimsplit(' ', $this->query)) as $j => $token) {
            foreach (['o.name', 'h.name'] as $field) {
                $where->add("$field LIKE :query_$j");
            }

            $qb->setParameter("query_$j", "%{$token}%");
        }

        if ($where->count()) {
            $qb->andWhere($where);
        }
    }
}
