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

use Ferienpass\CoreBundle\Routing\AdminPath;
use Ferienpass\CoreBundle\Tenant\SurfaceContext;
use Symfony\Component\EventDispatcher\Attribute\AsEventListener;
use Symfony\Component\HttpKernel\Event\ResponseEvent;
use Symfony\Component\HttpKernel\KernelEvents;

/**
 * Takes `/admin` back out of `Location` headers on a domain that serves the
 * admin at its root.
 *
 * Most redirects are built from the router and are already right
 * ({@see \Ferienpass\CoreBundle\Routing\SurfaceAwareUrlGenerator}). The ones
 * that are not come from inside Symfony: the firewall stores the URI of the
 * request it interrupted and sends the visitor back to it after signing in,
 * and that URI is the rewritten, internal one. Redirecting a visitor to a path
 * that does not exist on the domain they are on would send them into a 404 at
 * exactly the wrong moment.
 *
 * Only same-host locations are touched — a link that deliberately leaves for
 * the public site keeps its own path.
 */
#[AsEventListener(event: KernelEvents::RESPONSE, priority: -100)]
class AdminRedirectListener
{
    public function __construct(private readonly SurfaceContext $surfaces)
    {
    }

    public function __invoke(ResponseEvent $event): void
    {
        if (!$this->surfaces->adminAtRoot()) {
            return;
        }

        $response = $event->getResponse();
        if (!$response->isRedirection() || null === $location = $response->headers->get('Location')) {
            return;
        }

        [$origin, $path, $suffix] = AdminPath::splitUrl($location);

        if ('' !== $origin && $origin !== $event->getRequest()->getSchemeAndHttpHost()) {
            return;
        }

        if (!AdminPath::isAdmin($path)) {
            return;
        }

        $response->headers->set('Location', $origin.AdminPath::strip($path).$suffix);
    }
}
