<?php

namespace Ignite\Core\Models\Menu;

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

class Item extends Fluent implements CanDisplayInMenu
{
    /**
     * Create an instance of a menu item.
     *
     * @param string $url
     * @param string $label
     * @param int    $position
     * @param bool   $allow
     * @param array  $children
     * @param string $icon
     * @param mixed $badge
     * @param bool   $stickyLabel
     */
    public function __construct(
        $url,
        $label,
        $position,
        $allow = true,
        $children = null,
        $icon = '',
        $badge = null,
        $stickyLabel = false
    ) {
        parent::__construct([]);

        $this->setUrl($url);
        $this->setLabel($label);
        $this->setStickyLabel($stickyLabel);
        $this->setPosition($position);
        $this->setCanBeViewed($allow);
        $this->setChildren($children);
        $this->setIcon($icon);
        $this->setBadge($badge);
    }

    /**
     * Set the url of the view page.
     *
     * @param  string $url
     * @return $this
     */
    public function setUrl($url)
    {
        $this->url = '/' . ltrim(parse_url($url ?? '', PHP_URL_PATH) ?? '', '/');
        $parameters = parse_url($url, PHP_URL_QUERY);
        if ($parameters) {
            $this->url .= '?' . $parameters;
        }

        return $this;
    }

    /**
     * The url of the view page.
     *
     * @return string
     */
    public function getUrl()
    {
        return $this->url;
    }

    /**
     * Set the icon to display along with the label.
     *
     * @param  string $icon
     * @return CanDisplayInMenu
     */
    public function setIcon($icon)
    {
        $this->icon = $icon;

        return $this;
    }

    /**
     * The icon to display along with the label.
     *
     * @return CanDisplayInMenu
     */
    public function getIcon()
    {
        return $this->icon;
    }

    /**
     * Set the badge to display along with the label.
     *
     * @param  string|int $badge
     * @return $this
     */
    public function setBadge($badge)
    {
        $this->badge = $badge;

        return $this;
    }

    /**
     * The badge to display along with the label.
     *
     * @return string|int
     */
    public function getBadge()
    {
        return $this->badge;
    }

    /**
     * Set the page name label.
     *
     * @param  string $label
     * @return $this
     */
    public function setLabel($label)
    {
        $this->label = (string) $label;

        return $this;
    }

    /**
     * The page name label.
     *
     * @return string
     */
    public function getLabel()
    {
        return $this->label;
    }

    /**
     * Set whether the label should remain on a collapsed parent item.
     *
     * @param  bool $flag
     * @return $this
     */
    public function setStickyLabel($flag)
    {
        $this->sticky_label = (bool) $flag;

        return $this;
    }

    /**
     * Determine whether the label should remain on a collapsed parent item.
     *
     * @return bool
     */
    public function isStickyLabel()
    {
        return (bool) ($this->sticky_label ?? false);
    }

    /**
     * Set the position of the page in the menu.
     *
     * @param  int $position
     * @return $this
     */
    public function setPosition($position)
    {
        $this->position = (int) $position;

        return $this;
    }

    /**
     * The position of the page in the menu.
     *
     * @return int
     */
    public function getPosition()
    {
        return $this->position;
    }

    /**
     * Set whether the item be viewed in the menu.
     *
     * @param  bool $flag
     * @return $this
     */
    public function setCanBeViewed($flag)
    {
        $this->allow = (bool) $flag;

        return $this;
    }

    /**
     * Can the item be viewed in the menu.
     *
     * @return bool
     */
    public function canBeViewed()
    {
        return $this->allow;
    }

    /**
     * Set the children items.
     *
     * @param  array $children
     * @return $this
     */
    public function setChildren($children)
    {
        if (empty($children)) {
            $children = [];
        }

        if (is_array($children)) {
            $children = collect($children);
        }

        $this->children = $children->reject(function ($item) {
            return ! $item instanceof CanDisplayInMenu;
        });

        $this->updateChildPositions();

        return $this;
    }

    /**
     * Get the child items collection.
     *
     * @return \Illuminate\Support\Collection
     */
    public function getChildren()
    {
        return $this->children;
    }

    /**
     * Determine if the item has child items.
     *
     * @return bool
     */
    public function hasChildren()
    {
        return $this->children->isNotEmpty();
    }

    /**
     * Determine if an item with the given URL exists.
     *
     * @param $url
     * @return mixed
     */
    public function hasChild($url)
    {
        return $this->children->has($url);
    }

    /**
     * Retrieve a child item by URL.
     *
     * @param $url
     * @param null $default
     * @return mixed
     */
    public function getChild($url, $default = null)
    {
        return $this->children->get($url, $default);
    }

    /**
     * Add a child item.
     *
     * @param  CanDisplayInMenu $child
     * @return $this
     */
    public function addChild(CanDisplayInMenu $child)
    {
        $this->children->push($child);

        $this->updateChildPositions();

        return $this;
    }

    /**
     * Remove a child item by URL.
     *
     * @param $url
     * @return $this
     */
    public function removeChild($url)
    {
        $this->children->forget($url);

        $this->updateChildPositions();

        return $this;
    }

    /**
     * Key the collection by URL so we can search and re-order the positions.
     *
     * @return $this
     */
    protected function updateChildPositions()
    {
        $this->children = $this->children->keyBy(function ($child) {
            return $child->getMenuKey();
        })->sortBy(function ($child) {
            return $child->getPosition();
        });

        return $this;
    }

    /**
     * Get the value used to key the item in a collection.
     *
     * @return string
     */
    public function getMenuKey()
    {
        return str_replace(config('app.url'), '', $this->url);
    }
}
