<?php declare(strict_types=1);

namespace App\Engine\Navigation\Menu;

/**
 * @template TNavigationItem of \App\Engine\Navigation\Menu\NavigationItem
 */
class NavigationMenu
{

    /** @var array<int, TNavigationItem> */
    protected array $menuItemsById = [];

    public function __construct(
        protected readonly \App\Engine\Navigation\NavigationRepository $navigationRepository,
        protected readonly \App\Engine\Navigation\NavigationSection    $section,
    ) {
    }

    /**
     * @return array<int, TNavigationItem>
     */
    public function generateMenu(): array
    {
        $this->loadNavigationEntities();
        $this->processItemsRelations();
        $this->pruneMenu();

        return $this->menuItemsById;
    }

    protected function loadNavigationEntities(): void
    {
        $navigationRecords = $this->navigationRepository
            ->createQueryBuilder("n", 'n.id')
            ->select("n.id, n.priority, n.title, n.targetBlank, IDENTITY(n.parent) as parentId, COALESCE(p.url, n.url) as url")
            ->leftJoin("n.page", "p")
            ->where("n.enabled = 1 AND n.section = :section")
            ->orderBy("n.priority", "ASC")
            ->setParameter("section", $this->section->value)
            ->getQuery()
            ->getArrayResult();

        foreach ($navigationRecords as $navigationRecord) {
            $this->menuItemsById[$navigationRecord["id"]] = new NavigationItem(
                id: $navigationRecord["id"],
                title: $navigationRecord["title"],
                url: $navigationRecord["url"],
                parentId: $navigationRecord["parentId"],
                targetBlank: $navigationRecord["targetBlank"],
                priority: $navigationRecord["priority"],
            );
        }
    }

    protected function processItemsRelations(): void
    {
        foreach ($this->menuItemsById as $item) {
            if (($parentId = $item->getParentId()) === null) {
                continue;
            }

            $this->menuItemsById[$parentId]->addChild(
                child: $item,
                id: $item->getId(),
            );
        }
    }

    protected function pruneMenu(): void
    {
        $pruneKeys = [];
        foreach ($this->menuItemsById as $key => $value) {
            if ($value->getParentId() === null) {
                continue; // Root položky nemají parent, ty zachováme, zbavujeme se položek, které jsou již přiřazené.
            }

            $pruneKeys[] = $key;
        }

        foreach ($pruneKeys as $pruneKey) {
            unset($this->menuItemsById[$pruneKey]);
        }
    }

}