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

use Doctrine\ORM\EntityManagerInterface;
use Ferienpass\CoreBundle\Entity\AccessCodeStrategy;
use Ferienpass\CoreBundle\Entity\Attendance;
use Ferienpass\CoreBundle\Entity\Consent;
use Ferienpass\CoreBundle\Entity\Debtor;
use Ferienpass\CoreBundle\Entity\Edition;
use Ferienpass\CoreBundle\Entity\Host;
use Ferienpass\CoreBundle\Entity\Offer\OfferInterface;
use Ferienpass\CoreBundle\Entity\Participant\ParticipantInterface;
use Ferienpass\CoreBundle\Entity\Payment;
use Ferienpass\CoreBundle\Entity\User;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\Attribute\MapQueryParameter;
use Symfony\Component\Routing\Attribute\Route;

/**
 * The body of one list row's actions dropdown, fetched when the menu opens.
 *
 * A list page renders a menu per row, and building one is not cheap: it walks
 * the workflow's enabled transitions and puts every entry through the voters.
 * Doing that for 250 rows to produce markup behind a click dominated the page.
 * The row now ships only the toggle plus a lazy Turbo frame pointing here.
 *
 * The route takes an arbitrary id of a listed type, which is deliberate and
 * safe: every entry is `display`-gated by the same voters the list itself uses,
 * so a caller only ever gets the actions they would have been shown anyway. An
 * id they may do nothing with yields an empty menu.
 */
final class ListActionsController extends AbstractController
{
    /**
     * The types {@see \Ferienpass\AdminBundle\Menu\ActionsBuilder::actions()}
     * knows, keyed by the slug that appears in the URL. ParticipantDuplicateDto
     * is absent on purpose — it is not an entity, so those rows keep the eager
     * menu.
     */
    private const array TYPES = [
        'offer' => OfferInterface::class,
        'participant' => ParticipantInterface::class,
        'host' => Host::class,
        'account' => User::class,
        'debtor' => Debtor::class,
        'consent' => Consent::class,
        'edition' => Edition::class,
        'attendance' => Attendance::class,
        'payment' => Payment::class,
        'accessCode' => AccessCodeStrategy::class,
    ];

    public function __construct(private readonly EntityManagerInterface $em)
    {
    }

    #[Route(
        '/_list-actions/{type}/{id}',
        name: 'admin_list_actions',
        requirements: ['type' => '[a-zA-Z]+', 'id' => '\d+'],
        methods: ['GET'],
    )]
    public function __invoke(
        string $type,
        int $id,
        #[MapQueryParameter]
        ?string $view = null,
        #[MapQueryParameter]
        ?string $edition = null,
    ): Response {
        $class = self::TYPES[$type] ?? null;
        if (null === $class) {
            throw $this->createNotFoundException();
        }

        $item = $this->em->find($class, $id);
        if (null === $item) {
            throw $this->createNotFoundException();
        }

        return $this->render('@FerienpassAdmin/fragment/list_actions.html.twig', [
            'item' => $item,
            'view' => $view,
            'edition' => $edition,
            'frame' => \sprintf('list-actions-%s-%d', $type, $id),
        ]);
    }

    public static function slugFor(object $item): ?string
    {
        foreach (self::TYPES as $slug => $class) {
            if ($item instanceof $class) {
                return $slug;
            }
        }

        return null;
    }
}
