<?php

namespace Ignite\Core\Models\Menu;

use Illuminate\Support\Collection;
use Ignite\Core\Contracts\CanDisplayInMenu;

class Manager
{
    /** @var Collection */
    protected $items;

    /**
     * Manager constructor.
     */
    public function __construct()
    {
        $this->items = new Collection();
    }

    /**
     * Add an item to the menu.
     *
     * @param  CanDisplayInMenu $item
     * @return $this
     */
    public function add(CanDisplayInMenu $item)
    {
        $this->items->push($item);

        $this->items = $this->items->keyBy(function (CanDisplayInMenu $item) {
            return $item->getMenuKey();
        });

        return $this;
    }

    /**
     * Remove an item from the menu by URL.
     *
     * @param  string $url
     * @return $this
     */
    public function remove($url)
    {
        $key = $this->items->search(function (CanDisplayInMenu $item) use ($url) {
            return $item->getMenuKey() == $url;
        });

        if (! $key) {
            return $this;
        }

        $this->items->pull($key);

        return $this;
    }

    /**
     * Check if the menu has a specific item.
     *
     * @param  string $url
     * @return bool
     */
    public function has($url)
    {
        return $this->items->has($url);
    }

    /**
     * Get a menu item by URL.
     *
     * @param  string $url
     * @param  mixed  $default
     * @return CanDisplayInMenu
     */
    public function get($url, $default = null)
    {
        return $this->items->get($url, $default);
    }

    /**
     * Merge a collection of items to the menu.
     *
     * @param  Collection $collection
     * @return $this
     */
    public function merge(Collection $collection)
    {
        $collection->each(function ($item) {
            $this->add($item);
        });

        $this->items = $this->items->keyBy(function (CanDisplayInMenu $item) {
            return $item->getMenuKey();
        });

        return $this;
    }


    /**
     * Hydrate the collection with Item objects.
     *
     * @param  \Illuminate\Support\Collection $items
     * @param  string $class
     * @return \Illuminate\Support\Collection
     */
    public function hydrateItems($items, $class = Item::class)
    {
        return $items->map(function ($item) use ($class) {
            if (isset($item['children']) && is_array($item['children'])) {
                $item['children'] = $this->hydrateItems(collect($item['children']), $class);
            }
            return $this->hydrateItem($item, $class);
        })->filter();
    }

    /**
     * Hydrate an item object from an array.
     *
     * @param  array $item
     * @param  string $class
     * @return Item
     */
    public function hydrateItem(array $item, $class = Item::class)
    {
        if (! $item['allow']) {
            return null;
        }

        return app($class, $item);
    }

    /**
     * Get the collection of menu items.
     *
     * @return Iterator
     */
    public function getItems()
    {
        return $this->items->sort(function (CanDisplayInMenu $a, CanDisplayInMenu $b) {
            return ($a->getPosition() <=> $b->getPosition());
        });
    }
}
