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

use Contao\CoreBundle\Routing\Page\PageRoute;
use Symfony\Cmf\Component\Routing\RouteProviderInterface;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\RequestStack;
use Symfony\Component\Routing\Route;
use Symfony\Component\Routing\RouteCollection;

final class LocaleRouteProviderDecorator implements RouteProviderInterface
{
    public const array MAP = [
        'de' => '',
        'nl' => 'nl',
        'en' => 'en',
        'ar' => 'ar',
        'ru' => 'ru',
        'uk' => 'uk',
        'tr' => 'tr',
        'pl' => 'pl',
        'sq' => 'sq',
        'ku' => 'ku',
        'es' => 'es',
        'fr' => 'fr',
        'pt' => 'pt',
        'sw' => 'sw',
        'prs' => 'prs',
        'zh' => 'zh',
        'sr' => 'sr',
        'hr' => 'hr',
        'bs' => 'bs',
    ];

    public function __construct(
        private readonly RouteProviderInterface $inner,
        private readonly RequestStack $requestStack,
    ) {
    }

    public function getRouteCollectionForRequest(Request $request): RouteCollection
    {
        [$locale, $prefix] = $this->detectLocale($request->getPathInfo());

        $innerRequest = '' !== $prefix
            ? $this->requestWithStrippedPrefix($request, $prefix)
            : $request;

        $collection = $this->inner->getRouteCollectionForRequest($innerRequest);

        foreach ($collection->all() as $name => $route) {
            $this->applyToRoute($route, $name, $locale, $prefix);
        }

        return $collection;
    }

    public function getRouteByName(string $name): Route
    {
        $route = $this->inner->getRouteByName($name);

        if ($route instanceof PageRoute) {
            $this->applyByLanguage($route);
        }

        return $route;
    }

    public function getRoutesByNames(?array $names = null): iterable
    {
        foreach ($this->inner->getRoutesByNames($names) as $name => $route) {
            if ($route instanceof PageRoute) {
                $this->applyByLanguage($route);
            }
            yield $name => $route;
        }
    }

    // -------------------------------------------------------------------------

    /**
     * Returns [locale, prefix] for a given path.
     * Mirrors AbstractCandidates which strips the leading slash first.
     *
     * @return array{string, string}
     */
    private function detectLocale(string $pathInfo): array
    {
        $url = substr($pathInfo, 1); // strip leading slash

        foreach (self::MAP as $locale => $prefix) {
            if ('' === $prefix) {
                continue;
            }
            if (str_starts_with($url, $prefix.'/') || $url === $prefix) {
                return [$locale, $prefix];
            }
        }

        return ['de', ''];
    }

    /**
     * Returns a new Request with the locale prefix removed from the path.
     * The inner RouteProvider (and its PageCandidates) will see the clean path.
     */
    private function requestWithStrippedPrefix(Request $request, string $prefix): Request
    {
        $strippedPath = substr($request->getPathInfo(), \strlen($prefix) + 1) ?: '/';
        $qs = $request->getQueryString();
        $server = $request->server->all();
        $server['REQUEST_URI'] = $strippedPath.($qs ? '?'.$qs : '');
        unset($server['PATH_INFO']); // force Symfony to recompute from REQUEST_URI

        return $request->duplicate(server: $server);
    }

    /**
     * Apply urlPrefix and _locale to a route returned from the collection (matching phase).
     * Handles PageRoute, .root and .fallback synthetic routes.
     */
    private function applyToRoute(Route $route, string $name, string $locale, string $prefix): void
    {
        if ($route instanceof PageRoute) {
            $route->setUrlPrefix($prefix);
            $route->setDefault('_locale', $locale);

            return;
        }

        // Synthetic .root route: e.g. tl_page.5.root
        if (str_ends_with($name, '.root')) {
            $route->setPath($prefix ? '/'.$prefix.'/' : '/');
            $route->setDefault('_locale', $locale);

            return;
        }

        // Synthetic .fallback route: redirects / → /{prefix}/
        if (str_ends_with($name, '.fallback') && $prefix) {
            $defaults = $route->getDefaults();
            $defaults['_locale'] = $locale;
            $defaults['path'] = '/'.$prefix.'/';
            $route->setDefaults($defaults);
        }
    }

    /**
     * Apply urlPrefix and _locale for URL generation, using rootLanguage from the page model.
     */
    private function applyByLanguage(PageRoute $route): void
    {
        $language = $route->getPageModel()->rootLanguage ?? 'de';

        if ($request = $this->requestStack->getCurrentRequest()) {
            $locale = $request->getLocale();
            if (isset(self::MAP[$locale])) {
                $language = $locale;
            }
        }

        $route->setUrlPrefix(self::MAP[$language] ?? '');
        $route->setDefault('_locale', $language);
    }
}
