<?php
/**
* @package   Gridbox
* @author    Balbooa http://www.balbooa.com/
* @copyright Copyright @ Balbooa
* @license   http://www.gnu.org/licenses/gpl.html GNU/GPL
*/

namespace Balbooa\Component\Gridbox\Site\Helper;

use Joomla\CMS\Factory;
use Joomla\CMS\Filter\OutputFilter;

defined('_JEXEC') or die;

class MenuHelper
{
    public $root;
    public $db;
    public $cache;

    public function __construct()
    {
        $this->db = Factory::getDbo();
        $this->cache = [];
    }

    public function getMenu($id, $items = false)
    {
        $query = $this->db->getQuery(true)
            ->select('params')
            ->from('#__modules')
            ->where('`id` = '.$id);
        $this->db->setQuery($query);
        $menu = $this->db->loadResult();
        $menu = json_decode($menu);
        $obj = new \stdClass();
        $obj->menutype = $menu->menutype;
        if ($items) {
            $obj->items = $this->getMenuItems($menu->menutype);
        }
        
        return $obj;
    }

    public function getMenuItems($menutype)
    {
        $query = $this->db->getQuery(true)
            ->select('id, title')
            ->from('#__menu')
            ->where('`menutype` = '.$this->db->quote($menutype));
        $this->db->setQuery($query);
        $items = $this->db->loadObjectList();
        
        return $items;
    }

    public function getNewMenuAlias($type, $orig)
    {
        $alias = GridboxHelper::stringURLSafe(trim($type));
        $query = $this->db->getQuery(true);
        $query->select('COUNT(id)')
            ->from('#__menu')
            ->where('`alias` = '.$this->db->quote($alias));
        $this->db->setQuery($query);
        $n = $this->db->loadResult();
        if (!empty($n)) {
            $alias = empty($orig) ? GridboxHelper::increment($alias) : GridboxHelper::increment($orig);
            $orig = $alias;
            $alias = GridboxHelper::stringURLSafe($alias);
            if (empty($alias)) {
                $alias = $orig;
                $alias = GridboxHelper::replace($alias);
                $alias = OutputFilter::stringURLSafe($alias);
            }
            if (empty($alias)) {
                $alias = date('Y-m-d-H-i-s');
            }
            $alias = $this->getNewMenuAlias($alias, $orig);
        }

        return $alias;
    }

    public function getComponentId(string $type): int
    {
        if ($type != 'component') {
            return 0;
        }
        $query = $this->db->getQuery(true)
            ->select('extension_id')
            ->from('`#__extensions`')
            ->where('`element` = '.$this->db->quote('com_gridbox'))
            ->where('`type` = '.$this->db->quote('component'));
        $this->db->setQuery($query);
        $component = $this->db->loadResult();

        return $component;
    }

    public function setMenuItem(object $validated, string $menutype): void
    {
        $component = $this->getComponentId($validated->type);
        $query = $this->db->getQuery(true)
            ->select('MAX(rgt)')
            ->from('#__menu');
        $this->db->setQuery($query);
        $rgt = $this->db->loadResult();
        $path = JPATH_SITE . 'components/com_gridbox/assets/json/params/menu-item.json';
        $obj = (object) [
            'lft' => ++$rgt,
            'rgt' => ++$rgt,
            'title' => $validated->title,
            'menutype' => $menutype,
            'alias' => $this->getNewMenuAlias($validated->title, ''),
            'path' => '',
            'level' => 1,
            'link' => $validated->link,
            'type' => $validated->type,
            'published' => 1,
            'parent_id' => $validated->parent,
            'component_id' => $component,
            'access' => 1,
            'language' => '*',
            'img' => '',
            'params' => FileHelper::readFile($path)
        ];
        $this->db->insertObject('#__menu', $obj);
        $this->rebuild();
    }

    public function getRootId()
    {
        if ((int)$this->root > 0) {
            return $this->root;
        }
        $query = $this->db->getQuery(true)
            ->select('id')
            ->from('#__menu')
            ->where('parent_id = 0');
        $id = $this->db->setQuery($query)
            ->loadResult();
        $this->root = $id;

        return $this->root;
    }

    public function updateAnchorLink(&$obj, $anchor): void
    {
        if (empty($anchor)) {
            return;
        }
        $db = Factory::getDbo();
        $query = $db->getQuery(true)
            ->select('link')
            ->from('#__menu')
            ->where('id = ' . $db->quote($obj->id));
        $db->setQuery($query);
        $link = $db->loadResult();
        list($href, $hash) = explode('#', $link, 2);
        $link = $href . '#' . $anchor;
        $obj->link = $link;
    }

    public function saveMenuItemTitle(object $obj, string $anchor): void
    {
        $db = Factory::getDbo();
        $this->updateAnchorLink($obj, $anchor);
        $db->updateObject('#__menu', $obj, 'id');
    }

    public function deleteMenuItem($ids, $parents)
    {
        $db = Factory::getDbo();
        foreach ($ids as $key => $id) {
            $query = $db->getQuery(true)
                ->delete('#__menu')
                ->where("id = " . $id);
            $db->setQuery($query)
                ->execute();
            $query->clear()
                ->update('#__menu')
                ->where('parent_id = '.$id)
                ->set('parent_id = '.$parents[$key]);
            $db->setQuery($query)
                ->execute();
        }
        $this->rebuild();
    }

    public function sortMenuItems(array $idArray): void
    {
        $pks = [];
        foreach ($idArray as $value) {
            $pks[] = $value['id'];
        }
        $idStr = implode(',', $pks);
        $db = Factory::getDbo();
        $query = $db->getQuery(true)
            ->select('lft, rgt')
            ->from('#__menu')
            ->where('id in (' . $idStr . ')')
            ->order('lft ASC');
        $db->setQuery($query);
        $items = $db->loadObjectList();
        for ($i = 0; $i < count($idArray); $i++) {
            $query->clear()
                ->update('#__menu')
                ->where('id = ' . $idArray[$i]['id'])
                ->set('lft = ' . $items[$i]->lft)
                ->set('parent_id = ' . $idArray[$i]['parent_id'])
                ->set('rgt = ' . $items[$i]->rgt);
            $db->setQuery($query)
                ->execute();
        }
        $this->rebuild();
    }

    public function rebuild($parentId = null, $lft = 0, $level = 0, $path = '')
    {
        if ($parentId === null) {
            $parentId = $this->getRootId();
            if ($parentId === false) {
                return false;
            }
        }
        $query = $this->db->getQuery(true);
        if (!isset($this->cache['rebuild'])) {
            $query->clear()
                ->select('id, alias')
                ->from('#__menu')
                ->where('parent_id = %d')
                ->order('parent_id, lft');
            $this->cache['rebuild'] = (string)$query;
        }
        $this->db->setQuery(sprintf($this->cache['rebuild'], (int)$parentId));
        $children = $this->db->loadObjectList();
        $rgt = $lft + 1;
        foreach ($children as $node) {
            $rgt = $this->rebuild($node->id, $rgt, $level + 1, $path.(empty($path) ? '' : '/').$node->alias);
            if ($rgt === false) {
                return false;
            }
        }
        $query->clear()
            ->update('#__menu')
            ->set('lft = '.(int)$lft)
            ->set('rgt = '.(int)$rgt)
            ->set('level = '.(int)$level)
            ->set('path = '.$this->db->quote($path))
            ->where('id = '.(int)$parentId);
        $this->db->setQuery($query)->execute();

        return $rgt + 1;
    }
}