<?php

/**
 * @package     VP Prime Framework
 *
 * @author      Abhishek Das <info@virtueplanet.com>
 * @link        https://www.virtueplanet.com
 * @copyright   Copyright (C) 2023-2025 Virtueplanet Services LLP. All Rights Reserved.
 * @license     GNU General Public License version 2 or later; see LICENSE.txt
 */

namespace Virtueplanet\Module\Megamenu\Site\Helper;

// phpcs:disable PSR1.Files.SideEffects
\defined('_JEXEC') or die;
// phpcs:enable PSR1.Files.SideEffects

use Joomla\CMS\Cache\CacheControllerFactoryInterface;
use Joomla\CMS\Cache\Controller\OutputController;
use Joomla\CMS\Factory;
use Joomla\CMS\Language\Multilanguage;
use Joomla\CMS\Router\Route;
use Virtueplanet\Plugin\System\Prime\Helper\ModuleHelper;
use Virtueplanet\Plugin\System\Prime\Helper\VirtuemartHelper;

/**
 * Helper for mod_vpprime_mega_menu
 */
class MenuHelper
{
    /**
     * Active Virtuemart category ID
     *
     * @param   string
     */
    protected static $activeVirtuemartCategory;

    /**
     * Get a list of the menu items.
     *
     * @param   \Joomla\Registry\Registry  &$params  The module options.
     *
     * @return  array
     */
    public static function getList(&$params)
    {
        /** @var \Joomla\CMS\Application\SiteApplication $app */
        $app  = Factory::getApplication();
        $menu = $app->getMenu();
        $lang = $app->getLanguage();

        // Get active menu item
        $base   = self::getBase($params, true);
        $levels = $app->getIdentity()->getAuthorisedViewLevels();

        asort($levels);

        // Virtuemart Category Id
        $virtuemartCategoryId = $app->input->getInt('virtuemart_category_id', 0);

        if (is_array($virtuemartCategoryId)) {
            $virtuemartCategoryId = array_map('intval', array_values($virtuemartCategoryId));
        } else {
            $virtuemartCategoryId = [(int) $virtuemartCategoryId];
        }

        $virtuemartCategoryId = isset($virtuemartCategoryId[0]) ? $virtuemartCategoryId[0] : 0;
        $virtuemartCategoryId = $virtuemartCategoryId > 0 ? 'vmc-' . $virtuemartCategoryId : 0;

        // Reset active Virtuemart category Id
        self::$activeVirtuemartCategory = null;

        // Virtuemart Category end level
        $virtuemartCategoryEndLevel = 0;

        // Compose cache key
        $cacheKey = 'menu_items:' . $params . implode(',', $levels) . '.' . $base->id . '.lang:' . $lang->getTag();

        // Compose cache key for active Virtuemart category
        $cacheKeyForActiveCategory = 'category_active:' . $params . implode(',', $levels) . '.' . $base->id . '.lang:' . $lang->getTag();

        if ($params->get('cachemode', 'itemid') === 'safeuri') {
            $cacheKey                  .= '.category:' . $virtuemartCategoryId;
            $cacheKeyForActiveCategory .= '.category:' . $virtuemartCategoryId;
        }

        /** @var OutputController $cache */
        $cache = Factory::getContainer()->get(CacheControllerFactoryInterface::class)
            ->createCacheController('output', ['defaultgroup' => 'mod_vpprime_mega_menu']);

        // Disable caching if disabled in module options
        if ($params->get('caching', 1) == 0) {
            $cache->setCaching(0);
        }

        // Check if we have a cache hit
        if ($cache->contains($cacheKey)) {
            $items = $cache->get($cacheKey);

            if ($cache->contains($cacheKeyForActiveCategory)) {
                self::$activeVirtuemartCategory = $cache->get($cacheKeyForActiveCategory);
            }
        } else {
            $path           = $base->tree;
            $start          = (int) $params->get('startLevel', 1);
            $end            = (int) $params->get('endLevel', 0);
            $showAll        = $params->get('showAllChildren', 1);
            $rawMenuItems   = $menu->getItems('menutype', $params->get('menutype'));
            $inputVars      = $app->getInput()->getArray();
            $items          = [];
            $appendItems    = [];
            $hidden_parents = [];
            $module_parents = [];
            $lastitem       = 0;

            // Process the raw menu items and merge VirtueMart categories and modules with it.
            if ($rawMenuItems) {
                foreach ($rawMenuItems as $item) {
                    if (in_array($item->parent_id, $module_parents)) {
                        continue;
                    }

                    $itemParams        = $item->getParams();
                    $mergeVMCategories = $itemParams->get('megamenu_merge_vm_categories', 0);
                    $moduleSubContent  = $itemParams->get('megamenu_subcontent', '');

                    if ($mergeVMCategories) {
                        $parentId = $itemParams->get('megamenu_vm_category_parent', 0);
                        $endLevel = $itemParams->get('megamenu_vm_category_end_level', 0);

                        $vmCategories = VirtuemartHelper::getCategoriesForMenu($parentId, $endLevel, 1, false, $item);

                        if (!empty($vmCategories)) {
                            if ($mergeVMCategories === 'prepend') {
                                $items   = array_merge($items, $vmCategories);
                                $items[] = $item;
                            } else {
                                $items[]                = $item;
                                $appendItems[$item->id] = ['level' => $item->level, 'newItems' => $vmCategories];
                            }
                        } else {
                            $items[] = $item;
                        }
                    } else {
                        $items[] = $item;
                    }

                    if ($moduleSubContent) {
                        $module_parents[] = $item->id;

                        // if sub-content is a module position.
                        if ($moduleSubContent === 'pos') {
                            $selectedModulePosition = $itemParams->get('megamenu_module_position', '');

                            if (!empty($selectedModulePosition)) {
                                $modules = ModuleHelper::getByPositionForMenu($selectedModulePosition, $item);

                                foreach ($modules as $module) {
                                    if (!empty($module->html)) {
                                        $items[] = $module;
                                    }
                                }
                            }
                        } else {
                            $selectedModuleIds = $itemParams->get('megamenu_modules', []);

                            foreach ($selectedModuleIds as $selectedModuleId) {
                                $module = ModuleHelper::getForMenu($selectedModuleId, $item);

                                if ($module && !empty($module->html)) {
                                    $items[] = $module;
                                }
                            }
                        }
                    }
                }

                /**
                 * Appending items need spcial treatment.
                 */
                foreach ($appendItems as $targetId => $array) {
                    $appended    = false;
                    $targetFound = false;

                    foreach ($items as $i => $item) {
                        if ($item->id == $targetId) {
                            $targetFound = true;
                        }

                        if ($targetFound && $item->id != $targetId && $item->id == $array['newItems'][0]->called_menu_id) {
                            if ($array['level'] == $item->level) {
                                array_splice($items, $i, 0, $array['newItems']);

                                $appended = true;

                                break;
                            } elseif ($item->level < $array['level']) {
                                array_splice($items, $i, 0, $array['newItems']);

                                $appended = true;

                                break;
                            }
                        }
                    }

                    if (!$appended && $targetFound) {
                        $items = array_merge($items, $array['newItems']);
                    }

                    // Reset array keys. (May not be required)
                    $items = array_values($items);
                }

                // Process the final menu items.
                foreach ($items as $i => $item) {
                    $item->parent     = false;
                    $item->menu_level = $item->level;
                    $itemParams       = $item->getParams();

                    if (isset($items[$lastitem]) && $items[$lastitem]->id == $item->parent_id && $itemParams->get('menu_show', 1) == 1) {
                        $items[$lastitem]->parent = true;
                    }

                    if ($item->type !== 'vmcategory' && $item->type !== 'module') {
                        if (
                            ($start && $start > $item->level)
                            || ($end && $item->level > $end)
                            || (!$showAll && $item->level > 1 && !\in_array($item->parent_id, $path))
                            || ($start > 1 && !\in_array($item->tree[$start - 2], $path))
                        ) {
                            unset($items[$i]);
                            continue;
                        }
                    } elseif ($item->type === 'vmcategory') {
                        $myVirtuemartCategoryId = $item->called_menu_id . '-' . $virtuemartCategoryId;

                        if ($myVirtuemartCategoryId === $item->id && $item->level > $virtuemartCategoryEndLevel) {
                            self::$activeVirtuemartCategory = clone $item;
                            $virtuemartCategoryEndLevel     = $item->level;
                        }
                    }

                    // Exclude item with menu item option set to exclude from menu modules
                    if (($itemParams->get('menu_show', 1) == 0) || \in_array($item->parent_id, $hidden_parents)) {
                        $hidden_parents[] = $item->id;
                        unset($items[$i]);
                        continue;
                    }

                    $item->current = true;

                    foreach ($item->query as $key => $value) {
                        if (!isset($inputVars[$key]) || $inputVars[$key] !== $value) {
                            $item->current = false;
                            break;
                        }
                    }

                    $item->deeper     = false;
                    $item->shallower  = false;
                    $item->level_diff = 0;

                    if (isset($items[$lastitem])) {
                        $items[$lastitem]->deeper     = ($item->level > $items[$lastitem]->level);
                        $items[$lastitem]->shallower  = ($item->level < $items[$lastitem]->level);
                        $items[$lastitem]->level_diff = ($items[$lastitem]->level - $item->level);
                    }

                    $lastitem    = $i;
                    $item->flink = $item->link;

                    if ($item->type === 'vmcategory') {
                        $item->current = $item->active;
                    } else {
                        $item->active = false;
                    }

                    // Reverted back for CMS version 2.5.6
                    switch ($item->type) {
                        case 'separator':
                            // No further action needed.
                            break;

                        case 'heading':
                            // No further action needed.
                            break;

                        case 'module':
                            // No further action needed.
                            break;

                        case 'url':
                            if ((strpos($item->link, 'index.php?') === 0) && (strpos($item->link, 'Itemid=') === false)) {
                                // If this is an internal Joomla link, ensure the Itemid is set.
                                $item->flink = $item->link . '&Itemid=' . $item->id;
                            }
                            break;

                        case 'alias':
                            $item->flink = 'index.php?Itemid=' . $itemParams->get('aliasoptions');

                            // Get the language of the target menu item when site is multilingual
                            if (Multilanguage::isEnabled()) {
                                $newItem = $app->getMenu()->getItem((int) $itemParams->get('aliasoptions'));

                                // Use language code if not set to ALL
                                if ($newItem != null && $newItem->language && $newItem->language !== '*') {
                                    $item->flink .= '&lang=' . $newItem->language;
                                }
                            }
                            break;

                        case 'vmcategory':
                            $item->flink = $item->link;
                            break;

                        default:
                            $item->flink = 'index.php?Itemid=' . $item->id;
                            break;
                    }

                    if ((strpos($item->flink, 'index.php?') !== false) && strcasecmp(substr($item->flink, 0, 4), 'http')) {
                        $item->flink = Route::_($item->flink, true, $itemParams->get('secure'));
                    } else {
                        $item->flink = Route::_($item->flink);
                    }

                    // We prevent the double encoding because for some reason the $item is shared for menu modules and we get double encoding
                    // when the cause of that is found the argument should be removed
                    $item->title          = htmlspecialchars($item->title, ENT_COMPAT, 'UTF-8', false);
                    $item->menu_icon      = htmlspecialchars($itemParams->get('menu_icon_css', ''), ENT_COMPAT, 'UTF-8', false);
                    $item->anchor_css     = htmlspecialchars($itemParams->get('menu-anchor_css', ''), ENT_COMPAT, 'UTF-8', false);
                    $item->anchor_title   = htmlspecialchars($itemParams->get('menu-anchor_title', ''), ENT_COMPAT, 'UTF-8', false);
                    $item->anchor_rel     = htmlspecialchars($itemParams->get('menu-anchor_rel', ''), ENT_COMPAT, 'UTF-8', false);
                    $item->menu_image     = htmlspecialchars($itemParams->get('menu_image', ''), ENT_COMPAT, 'UTF-8', false);
                    $item->menu_image_css = htmlspecialchars($itemParams->get('menu_image_css', ''), ENT_COMPAT, 'UTF-8', false);

                    if ($start > 1) {
                        $item->menu_level = ($item->menu_level - $start) + 1;
                    }

                    // Prepare mega menu layout parameters.
                    $item->group                 = $item->menu_level > 1 ? $itemParams->get('megamenu_group', 0) : false;
                    $item->dropdown_width        = $itemParams->get('megamenu_dropdown_width', 'auto');
                    $item->dropdown_custom_width = $itemParams->get('megamenu_dropdown_custom_width', 500);
                    $item->direction             = $itemParams->get('megamenu_item_direction', 'row');
                    $item->cols_per_row          = (int) $itemParams->get('megamenu_cols_per_row', 1);
                    $item->col_custom_grids      = $itemParams->get('megamenu_col_custom_grids', '');
                    $item->items_per_column      = (int) $itemParams->get('megamenu_items_per_column', 3);
                    $item->template_columns      = 'initial';
                    $item->template_rows         = 'initial';
                    $item->column_width          = 'auto';

                    if ($item->direction === 'row') {
                        $widthPercentage        = round(100 / $item->cols_per_row, 10);
                        $item->template_columns = 'repeat(' . $item->cols_per_row . ', minmax(' . $widthPercentage . '%, 1fr))';

                        if ($item->cols_per_row === -1 && !empty($item->col_custom_grids) && strstr($item->col_custom_grids, PHP_EOL)) {
                            $item->col_custom_grids = explode(PHP_EOL, $item->col_custom_grids);
                            $item->col_custom_grids = array_filter(array_map('intval', $item->col_custom_grids));
                            $item->template_columns = [];

                            if (!empty($item->col_custom_grids)) {
                                foreach ($item->col_custom_grids as $item->col_custom_grid) {
                                    $item->template_columns[] = trim($item->col_custom_grid) . 'fr';
                                }

                                $item->template_columns = implode(' ', $item->template_columns);
                            }
                        }
                    } else {
                        $item->template_rows = 'repeat(' . $item->items_per_column . ', auto)';

                        if ($itemParams->get('megamenu_column_width', '0')) {
                            $item->column_width = (int) $itemParams->get('megamenu_column_width', '0');
                        }
                    }
                }

                if (isset($items[$lastitem])) {
                    $items[$lastitem]->deeper     = (($start ?: 1) > $items[$lastitem]->level);
                    $items[$lastitem]->shallower  = (($start ?: 1) < $items[$lastitem]->level);
                    $items[$lastitem]->level_diff = ($items[$lastitem]->level - ($start ?: 1));
                }
            }

            $cache->store($items, $cacheKey);
            $cache->store(self::$activeVirtuemartCategory, $cacheKeyForActiveCategory);
        }

        return $items;
    }

    /**
     * Get base menu item.
     *
     * @param   \Joomla\Registry\Registry  &$params  The module options.
     * @param   boolean  Ignore active Virtuemart category if needed.
     *
     * @return  object
     */
    public static function getBase(&$params, $ignoreVirtuemartCategory = false)
    {
        // Get base menu item from parameters
        if ($params->get('base')) {
            /** @var \Joomla\CMS\Application\SiteApplication $app */
            $app  = Factory::getApplication();
            $base = $app->getMenu()->getItem($params->get('base'));
        } else {
            $base = false;
        }

        // Use active menu item if no base found
        if (!$base) {
            if (self::$activeVirtuemartCategory && !$ignoreVirtuemartCategory) {
                $base = self::$activeVirtuemartCategory;
            } else {
                $base = self::getActive($params);
            }
        }

        return $base;
    }

    /**
     * Get active menu item.
     *
     * @param   \Joomla\Registry\Registry  &$params  The module options.
     *
     * @return  object
     */
    public static function getActive(&$params)
    {
        /** @var \Joomla\CMS\Application\SiteApplication $app */
        $app  = Factory::getApplication();
        $menu = $app->getMenu();

        return $menu->getActive() ?: self::getDefault();
    }

    /**
     * Get default menu item (home page) for current language.
     *
     * @return  object
     */
    public static function getDefault()
    {
        /** @var \Joomla\CMS\Application\SiteApplication $app */
        $app  = Factory::getApplication();
        $menu = $app->getMenu();

        // Look for the home menu
        if (Multilanguage::isEnabled()) {
            return $menu->getDefault($app->getLanguage()->getTag());
        }

        return $menu->getDefault();
    }

    /**
     * Get active Virtuemart category ID.
     *
     * @return  int|null
     */
    public static function getActiveVirtuemartCategoryId()
    {
        return self::$activeVirtuemartCategory ? self::$activeVirtuemartCategory->id : null;
    }
}
