<?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\CoreBundle\MessageHandler;

use Ferienpass\CoreBundle\ApplicationSystem\ApplicationSystemInterface;
use Ferienpass\CoreBundle\ApplicationSystem\ApplicationSystems;
use Ferienpass\CoreBundle\Entity\Offer\Base;
use Ferienpass\CoreBundle\Entity\Offer\OfferInterface;
use Ferienpass\CoreBundle\Entity\OfferDate;
use Ferienpass\CoreBundle\Message\ReorderParticipantList as ReorderParticipantListCommand;
use Ferienpass\CoreBundle\Repository\OfferRepositoryInterface;
use Symfony\Component\DependencyInjection\Attribute\AutowireLocator;
use Symfony\Component\DependencyInjection\ServiceLocator;
use Symfony\Component\Messenger\Attribute\AsMessageHandler;
use Symfony\Component\Workflow\Exception\NotEnabledTransitionException;
use Symfony\Component\Workflow\WorkflowInterface;

#[AsMessageHandler(bus: 'command.bus')]
class ReorderParticipantList
{
    public function __construct(private readonly OfferRepositoryInterface $offers, private readonly WorkflowInterface $attendanceStateMachine, private readonly ApplicationSystems $applicationSystems, #[AutowireLocator('ferienpass.application_system', defaultIndexMethod: 'getType')] private readonly ServiceLocator $applicationSystemsLocator)
    {
    }

    public function __invoke(ReorderParticipantListCommand $command): void
    {
        $now = new \DateTimeImmutable();
        $offerId = $command->getOfferId();

        /** @var Base $offer */
        $offer = $this->offers->findOneBy(['uuid' => $offerId]);
        /** @var OfferDate|false $date */
        $date = $offer->getDates()->first();

        // Do not update the attendances with the offer being in the past
        if (!$date || $now >= $date->getBegin()) {
            return;
        }

        // Only participants on waiting list can move up
        $attendances = $offer->getAttendancesWaitlisted();
        if ($attendances->isEmpty()) {
            return;
        }

        $applicationSystem = $this->applicationSystem($command, $offer);
        if (null === $applicationSystem) {
            return;
        }

        foreach ($attendances as $attendance) {
            $transition = $applicationSystem->suggestTransition($attendance);
            if (null === $transition) {
                continue;
            }

            try {
                $context = [
                    'notify' => $command->shouldNotify(),
                    'commit' => $command->shouldCommit(),
                ];

                if ($group = $command->getGroup()) {
                    $context['group'] = $group;
                }

                $this->attendanceStateMachine->apply($attendance, $transition, $context);
            } catch (NotEnabledTransitionException) {
                // Probably already in correct state
                continue;
            }
        }
    }

    private function applicationSystem(ReorderParticipantListCommand $command, OfferInterface $offer): ?ApplicationSystemInterface
    {
        if ($command->alwaysReorder() && $this->applicationSystemsLocator->has('firstcome')) {
            $applicationSystem = $this->applicationSystemsLocator->get('firstcome');
        }

        $applicationSystem ??= $this->applicationSystems->findApplicationSystem($offer);
        if ($applicationSystem instanceof ApplicationSystemInterface) {
            return $applicationSystem;
        }

        return null;
    }
}
