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

use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Criteria;
use Doctrine\Common\Collections\Order;
use Doctrine\ORM\EntityManagerInterface;
use Ferienpass\CoreBundle\ApplicationSystem\ApplicationSystemInterface;
use Ferienpass\CoreBundle\Entity\Attendance;
use Ferienpass\CoreBundle\Entity\Edition;
use Ferienpass\CoreBundle\Entity\Participant\ParticipantInterface;
use Ferienpass\CoreBundle\Enum\AttendanceStatus;
use Ferienpass\CoreBundle\Repository\EditionTaskRepository;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\DependencyInjection\Attribute\AutowireLocator;
use Symfony\Component\DependencyInjection\ServiceLocator;
use Symfony\UX\LiveComponent\Attribute\AsLiveComponent;
use Symfony\UX\LiveComponent\Attribute\LiveAction;
use Symfony\UX\LiveComponent\Attribute\LiveArg;
use Symfony\UX\LiveComponent\Attribute\LiveProp;
use Symfony\UX\LiveComponent\DefaultActionTrait;
use Symfony\UX\TwigComponent\Attribute\ExposeInTemplate;

#[AsLiveComponent(template: '@Contao/components/applications_prioritize.html.twig', route: 'live_component_cms')]
class ApplicationsPrioritize extends AbstractController
{
    use DefaultActionTrait;

    #[LiveProp]
    public Edition $edition;

    #[LiveProp]
    public ParticipantInterface $participant;

    #[LiveProp(hydrateWith: 'hydrateApplicationSystem', dehydrateWith: 'dehydrateApplicationSystem')]
    public ApplicationSystemInterface $applicationSystem;

    public function __construct(private readonly EditionTaskRepository $tasks, #[AutowireLocator('ferienpass.application_system', defaultIndexMethod: 'getType')] private readonly ServiceLocator $applicationSystems)
    {
    }

    #[LiveAction]
    public function upvote(#[LiveArg] Attendance $attendance, EntityManagerInterface $entityManager): void
    {
        $this->reorder($attendance);

        $entityManager->flush();
    }

    #[LiveAction]
    public function downvote(#[LiveArg] Attendance $attendance, EntityManagerInterface $entityManager): void
    {
        $this->reorder($attendance, 'down');

        $entityManager->flush();
    }

    #[ExposeInTemplate]
    public function attendances(): ArrayCollection
    {
        return $this->participant->getAttendances()
            ->matching(
                Criteria::create()
                    ->where(Criteria::expr()->neq('status', AttendanceStatus::WITHDRAWN))
                    ->where(Criteria::expr()->neq('status', AttendanceStatus::UNFULFILLED))
                    ->orderBy(['userPriority' => Order::Ascending])
            )
            ->filter(fn (Attendance $a) => false === $a->getOffer()->isCancelled() && $a->getOffer()->getEdition() === $this->edition);
    }

    public function dehydrateApplicationSystem(ApplicationSystemInterface $applicationSystem)
    {
        return [
            'type' => $applicationSystem->getType(),
            'task' => $applicationSystem->getTask()->getId(),
        ];
    }

    public function hydrateApplicationSystem($data): ?ApplicationSystemInterface
    {
        $applicationSystem = $this->applicationSystems->get($data['type']);
        if (!$applicationSystem instanceof ApplicationSystemInterface) {
            return null;
        }

        $task = $this->tasks->find($data['task']);

        if ($task) {
            return $applicationSystem->withTask($task);
        }

        return $applicationSystem;
    }

    private function reorder(Attendance $attendance, string $direction = 'up'): void
    {
        if (!$attendance->isWaiting()) {
            return;
        }

        $attendances = $this->attendances();

        if ($attendances->isEmpty()) {
            $attendance->setUserPriority(1);

            return;
        }

        $movingId = $attendance->getId();

        $iterator = $attendances->getIterator();
        $moved = false;
        $iterator->uasort(static function (Attendance $a, Attendance $b) use ($movingId, $direction, &$moved) {
            if ($moved) {
                return 0;
            }

            if (($a->getId() === $movingId && 'down' === $direction) || $b->getId() === $movingId && 'up' === $direction) {
                $moved = true;

                return 1;
            }

            return 0;
        });

        $items = array_values(iterator_to_array($iterator));
        $counter = \count($items);

        for ($i = 0; $i < $counter; ++$i) {
            $items[$i]->setUserPriority($i + 1);
        }
    }
}
