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

use Ferienpass\CmsBundle\Routing\LocaleRouteProviderDecorator;
use Ferienpass\CoreBundle\Routing\AdminPath;
use Ferienpass\CoreBundle\Routing\Locales;
use PHPUnit\Framework\Attributes\DataProvider;
use PHPUnit\Framework\TestCase;

class AdminPathTest extends TestCase
{
    #[DataProvider('adminPaths')]
    public function testRecognisesAdminPaths(string $path, bool $expected): void
    {
        self::assertSame($expected, AdminPath::isAdmin($path));
    }

    public static function adminPaths(): iterable
    {
        yield ['/admin', true];
        yield ['/admin/angebote', true];
        yield ['/nl/admin/angebote', true];
        yield ['/prs/admin', true];
        yield ['/', false];
        yield ['/angebote', false];
        yield ['/administration', false];
        yield ['/nl/angebote', false];
        // `de` is served without a prefix, so this is a path segment.
        yield ['/de/admin', false];
        // Not a locale we serve.
        yield ['/api/admin', false];
    }

    #[DataProvider('roundTrips')]
    public function testMovesPathsInAndOutOfTheAdminSpace(string $public, string $internal): void
    {
        self::assertSame($internal, AdminPath::prepend($public));
        self::assertSame($public, AdminPath::strip($internal));
    }

    public static function roundTrips(): iterable
    {
        // The trailing slash survives: the admin index is routed as `/admin/`,
        // and handing the router `/admin` earns a 301 to put the slash back —
        // which the way out then strips to `/`, looping the root of an admin
        // domain forever.
        yield ['/', '/admin/'];
        yield ['/angebote', '/admin/angebote'];
        yield ['/angebote/4Z9k/bearbeiten', '/admin/angebote/4Z9k/bearbeiten'];
        yield ['/nl/', '/nl/admin/'];
        yield ['/nl/angebote', '/nl/admin/angebote'];
        yield ['/api/tokens', '/admin/api/tokens'];
    }

    public function testLeavesANonAdminPathAlone(): void
    {
        self::assertSame('/angebote', AdminPath::strip('/angebote'));
        self::assertSame('/administration', AdminPath::strip('/administration'));
    }

    #[DataProvider('urls')]
    public function testSplitsUrls(string $url, array $expected): void
    {
        self::assertSame($expected, AdminPath::splitUrl($url));
    }

    public static function urls(): iterable
    {
        yield ['/admin/angebote', ['', '/admin/angebote', '']];
        yield ['/admin/angebote?page=2', ['', '/admin/angebote', '?page=2']];
        yield ['/admin/angebote#top', ['', '/admin/angebote', '#top']];
        yield ['https://example.com/admin/angebote', ['https://example.com', '/admin/angebote', '']];
        yield ['https://example.com:8443/admin?a=1', ['https://example.com:8443', '/admin', '?a=1']];
        yield ['//example.com/admin', ['//example.com', '/admin', '']];
        yield ['https://example.com', ['https://example.com', '/', '']];
    }

    /**
     * The prefix map the CMS routes are mounted with is the same list, and the
     * admin URL space is split with it — a locale added to one and not the
     * other would silently mistake a path segment for a language.
     */
    public function testTheLocaleListMatchesTheCmsRoutePrefixes(): void
    {
        self::assertSame(Locales::ALL, array_keys(LocaleRouteProviderDecorator::MAP));

        foreach (LocaleRouteProviderDecorator::MAP as $locale => $prefix) {
            self::assertSame(Locales::DEFAULT === $locale ? '' : $locale, $prefix);
        }
    }
}
