<?php


namespace AutoAdmin\Bundle\Menu\Provider;


use AutoAdmin\Bundle\Content\ContentReference\ContentReferenceResolver;
use AutoAdmin\Bundle\Content\Routing\ContentRouter;
use AutoAdmin\Bundle\Menu\Entity\Menu;
use AutoAdmin\Bundle\Menu\Entity\MenuItem;
use AutoAdmin\Bundle\Menu\Entity\MenuItemTranslation;
use AutoAdmin\Bundle\Menu\Exception\MenuNotFoundException;
use Doctrine\ORM\EntityManagerInterface;
use Knp\Menu\FactoryInterface;
use Knp\Menu\ItemInterface;
use Knp\Menu\Provider\MenuProviderInterface;

class MenuProvider implements MenuProviderInterface
{
    /**
     * @param FactoryInterface $factory the menu factory used to create the menu item
     */
    public function __construct(
        protected ?FactoryInterface $factory,
        protected EntityManagerInterface $entityManager,
        protected ContentRouter $contentRouter,
        protected ContentReferenceResolver $contentReferenceResolver
    ) {
    }

    /**
     * Retrieves a menu by its name
     *
     * @throws MenuNotFoundException if the menu does not exists
     */
    public function get(string $name, array $options = []): ItemInterface
    {
        $menuRepo = $this->entityManager->getRepository(Menu::class);
        /** @var Menu $menu */
        $menu = $menuRepo->contentFindOneBySlug($name);

        if ($menu !== null && $root = $menu->getRoot()) {
            $item = $this->buildMenuItems($root);
        } else {
            throw new MenuNotFoundException(sprintf('The menu "%s" is not found.', $name));
        }

        return $item;
    }

    protected function buildMenuItems(MenuItem $item, $parent = null): ItemInterface
    {
        if ($parent === null) {
            $parent = $this->factory->createItem('root');
        }

        $currentItemNode = $this->convert($item);
        $parent->addChild($currentItemNode);
        $parent = $currentItemNode;

        if (!empty($item->getChildren())) {
            foreach ($item->getChildren() as $child) {
                $this->buildMenuItems($child, $parent);
            }
        }

        return $parent;
    }

    protected function convert(MenuItem $item): ItemInterface
    {
        /** @var MenuItemTranslation $itemTranslation */
        $itemTranslation = $item->getCurrentTranslation();
        $itemLabel = '<!-- aa:menu:item-not-translated -->';
        if ($itemTranslation && $label = $itemTranslation->getLabel()) {
            $itemLabel = $label;
        }
        $nodeItem = $this->factory->createItem($itemLabel);

        switch ($item->getType()) {
            case MenuItem::TYPE_TEXT:
                break;
            case MenuItem::TYPE_LINK_URL:
                $nodeItem->setUri($item->getUri());
                break;
            case MenuItem::TYPE_LINK_CONTENT:
                // resolve content urls using contentReference
                $content = $this->contentReferenceResolver->resolve($item->getContentReference());
                if ($content) {
                    $action = $item->getContentReference()->getReferencedAction();
                    $url = $this->contentRouter->generate($content, $action);
                    $nodeItem->setUri($url);
                    $routeName = $this->contentRouter->getRouteName($content, $action);
                    $routeParams = $this->contentRouter->buildRouteParams($content, $action);
                    if (!empty($routeName) && !empty($routeParams)) {
                        $nodeItem->setExtra('route', $routeName);
                        $nodeItem->setExtra('routeParameters', $routeParams);

                        // this index is used to flag element as active in menu item matcher
                        $nodeItem->setExtra('routes', [['route' => $routeName, 'parameters' => $routeParams,],]);
                    }
                }

                break;
        }

        $nodeItem->setDisplay($item->isDisplay());

        return $nodeItem;
    }


    /**
     * Only implement to stay compatible with KnpMenu
     *
     * We return true to handle all menu providing cases and silently handle absent menus
     * (needed for partially translated sites)
     *
     * @param string $name
     * @param array $options
     * @return bool
     */
    public function has(string $name, array $options = []): bool
    {
        return true;
    }

    public function doSlugExist(string $name): bool
    {
        $menuRepo = $this->entityManager->getRepository(Menu::class);
        /** @var Menu $menu */
        $menu = $menuRepo->contentFindOneBySlug($name);

        return $menu !== null;
    }
}
