<?php

namespace Ignite\Core\Models\Menu;

use Ignite\Core\Contracts\CanDisplayInMenu;
use Ignite\Core\Events\Menus\FooterNavigationMenuPreCompile;
use Spatie\Menu\Laravel\Link;
use Spatie\Menu\Laravel\Menu as SpatieMenu;

class FooterNavigationMenu extends NavigationMenu
{
    /**
     * Parent keys for grouped footer columns.
     */
    public const PROGRAM_MENU_KEY = '/menu-program';
    public const ACCOUNT_MENU_KEY = '/menu-account';
    public const SUPPORT_MENU_KEY = '/menu-support';
    public const LEGAL_MENU_KEY   = '/menu-legal';

    /**
     * Dispatch the footer menu compile event.
     */
    public function render(): string
    {
        event(new FooterNavigationMenuPreCompile($this));

        $this->moveUngroupedItemsToDefaultGroup();
        $this->removeEmptyGroups();

        $groups = $this->getGroupItems();

        if ($groups->isEmpty()) {
            return '';
        }

        $this->menu = $this->buildMenu($groups)->addClass('footer-nav');

        return $this->menu->render();
    }

    /**
     * Move top-level non-group items into the default footer group.
     *
     * @return void
     */
    protected function moveUngroupedItemsToDefaultGroup(): void
    {
        if (! $this->has(static::PROGRAM_MENU_KEY)) {
            return;
        }

        $ungroupedItems = $this->getItems()->filter(function ($item) {
            return $item instanceof CanDisplayInMenu
                && ! $this->isGroupItem($item);
        });

        foreach ($ungroupedItems as $item) {
            $this->addChildToParentMenu($item, static::PROGRAM_MENU_KEY);
            $this->remove($item->getMenuKey());
        }
    }

    /**
     * Get all footer group parent items in menu order.
     *
     * @return \Illuminate\Support\Collection
     */
    protected function getGroupItems()
    {
        return $this->getItems()->filter(function ($item) {
            return $item instanceof Item && $this->isGroupItem($item);
        });
    }

    /**
     * Determine whether an item is one of the footer group parents.
     *
     * @param  CanDisplayInMenu $item
     * @return bool
     */
    protected function isGroupItem(CanDisplayInMenu $item): bool
    {
        return in_array($item->getMenuKey(), [
            static::PROGRAM_MENU_KEY,
            static::ACCOUNT_MENU_KEY,
            static::SUPPORT_MENU_KEY,
            static::LEGAL_MENU_KEY,
        ], true);
    }

    /**
     * Render grouped footer markup.
     *
     * @param  \Illuminate\Support\Collection $groups
     * @return string
     */
    public function buildMenu($links): SpatieMenu
    {
        return MenuExtended::build($links->toArray(), function ($menu, $item) {
            $item = $this->normalizeMenuItem($item);

            if (! $item->hasChildren()) {
                $this->addFooterLink($item, $menu);

                return;
            }

            $children = $this->getRenderableChildren($item);

            if ($children->isEmpty()) {
                return;
            }

            $menu->submenu(
                $this->addGroupLink($item),
                $this->buildMenu($children)
                    ->addClass('footer-nav__submenu')
                    ->addParentClass('footer-nav__group')
            );
        });
    }

    /**
     * Render a footer leaf link.
     *
     * @param  CanDisplayInMenu $item
     * @param  SpatieMenu $menu
     * @return void
     */
    protected function addFooterLink(CanDisplayInMenu $item, SpatieMenu $menu): void
    {
        $menu->linkIf($item->canBeViewed(), $item->getUrl(), $this->buildLabel($item))
            ->addClass('footer-nav__link');
    }

    /**
     * Get visible children sorted by configured position.
     *
     * @param  Item $group
     * @return \Illuminate\Support\Collection
     */
    protected function getRenderableChildren(Item $group)
    {
        return $group->getChildren()
            ->filter(function ($child) {
                return $child->canBeViewed();
            })
            ->sortBy(function ($child) {
                return $child->getPosition();
            });
    }

    /**
     * Build the footer group heading link.
     *
     * @param  Item $item
     * @return Link
     */
    protected function addGroupLink(Item $item): Link
    {
        return Link::to('#', $this->buildLabel($item))
            ->addClass('footer-nav__heading')
            ->setAttribute('aria-disabled', 'true')
            ->setAttribute('tabindex', '-1');
    }

    /**
     * Remove footer groups that do not have any visible children.
     *
     * @return void
     */
    protected function removeEmptyGroups(): void
    {
        foreach ($this->getGroupItems() as $group) {
            if ($this->getRenderableChildren($group)->isNotEmpty()) {
                continue;
            }

            $this->remove($group->getMenuKey());
        }
    }
}
