<?php

declare(strict_types=1);

namespace Apexl\Io\Menu\Tests\FeatureTests;

use Apexl\Io\Menu\Factories\MenuItemFactory;
use Apexl\Io\TestInfrastructure\FeatureTest;
use Apexl\Io\User\Enums\Permissions\Common;
use Apexl\Io\User\Exceptions\PermissionException;
use Apexl\Io\User\Middleware\RoutePermissionMiddleware;
use PHPUnit\Framework\Attributes\Test;
use Slim\App;

/**
 * Building a real menu item, because nothing else does.
 *
 * The menu is the one consumer that asks what a route requires without dispatching it, so it
 * is the thing that breaks when the answer moves. It has broken that way twice — an
 * uninitialised property that only fataled on a resolved route, and a rename that only
 * failed on a route that existed — and neither a grep nor the suite caught either, because
 * nothing built an item.
 */
final class MenuItemFactoryTest extends FeatureTest
{
    private function factory(): MenuItemFactory
    {
        return \Apexl\Io\container()->get(MenuItemFactory::class);
    }

    private function route(string $suffix, string $pattern = '/menu-test'): string
    {
        $name = 'test.menu.' . $suffix;

        \Apexl\Io\container()->get(App::class)->get($pattern . '/' . $suffix, fn() => null)->setName($name);

        return $name;
    }

    /** An open route becomes an item with somewhere to go. */
    #[Test]
    public function anUnrestrictedRouteBecomesALinkedItem(): void
    {
        $name = $this->route('open');

        $item = $this->factory()->make(['routeName' => $name, 'title' => 'Open']);

        self::assertSame('/menu-test/open', $item->href);
        self::assertFalse($item->isParent(), 'A resolved route is a link, not a heading.');
    }

    /** What the menu exists to do: not offer a page the caller would be refused. */
    #[Test]
    public function aRouteTheUserCannotReachIsRefused(): void
    {
        $name = $this->route('restricted');
        \Apexl\Io\container()->get(App::class)->getRouteCollector()->getNamedRoute($name)
            ->add(new RoutePermissionMiddleware(Common::CAN_DO_NOTHING));

        $this->expectException(PermissionException::class);

        $this->factory()->make(['routeName' => $name, 'title' => 'Restricted']);
    }

    /**
     * A name matching no route is how a heading is declared — it gets no href rather than
     * bringing the whole menu down, which on a signed-in page is every page.
     */
    #[Test]
    public function aNameMatchingNoRouteBecomesAHeading(): void
    {
        $item = $this->factory()->make(['routeName' => 'test.menu.no-such-route', 'title' => 'Heading']);

        self::assertTrue($item->isParent());
    }
}
