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

use Ferienpass\CoreBundle\Entity\User;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
use Symfony\Component\Security\Http\Authentication\AuthenticationSuccessHandlerInterface;

final class AuthenticationSuccessHandler implements AuthenticationSuccessHandlerInterface
{
    public function __construct(private readonly UrlGeneratorInterface $urlGenerator)
    {
    }

    public function onAuthenticationSuccess(Request $request, TokenInterface $token): ?Response
    {
        $user = $token->getUser();
        if (!$user instanceof User) {
            return null;
        }

        $locale = $user->getLocale();

        // Honour the `_target_path` field that LoginController stamps onto the
        // form (from a signed `?redirect=` query arg or the user's saved
        // target_path). Without this every successful login bounced users to
        // the dashboard even when they deep-linked to /admin/angebote/….
        if (null !== $submitted = $request->request->get('_target_path')) {
            $target = base64_decode((string) $submitted, true);
            if (\is_string($target) && '' !== $target && $this->isSafeRedirect($target)) {
                return new RedirectResponse($target);
            }
        }

        return new RedirectResponse($this->urlGenerator->generate('admin_index', ['_locale' => $locale]));
    }

    /**
     * Only allow relative redirects that stay within the admin area. This
     * prevents an attacker crafting a phishing URL like
     * /admin/login?redirect=https://evil.com/ (even though the signed-URL
     * check in LoginController already mitigates that — belt and braces).
     */
    private function isSafeRedirect(string $target): bool
    {
        return str_starts_with($target, '/admin')
            && !str_starts_with($target, '//')
            && !preg_match('#^/admin/[a-z]{2,3}/login$#i', $target);
    }
}
