<?php
/**
 * Site Menu Class
 *
 * This code is used to make up Site Menu plugin
 *
 * @category Plugins
 * @package  Mtc_Site_Menu
 * @author   mtc. Martins Fridenbergs <martins.fridenbergs@mtcmedia.co.uk>
 * @author   mtc. Peter Carlyle <peter.carlyle@mtcmedia.co.uk>
 * @author   mtc. Dawid Bestrzynski <dawid.bestrzynski@mtcmedia.co.uk>
 */

namespace Mtc\Plugins\SiteMenu\Classes;

use Exception;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Collection;
use Illuminate\Database\Eloquent\Relations\HasOne;
use Illuminate\Database\Eloquent\SoftDeletes;
use Illuminate\Support\Facades\App;
use Mtc\Shop\Category;
use CategoryHelper;
use Mtc\Shop\Brand;
use CMSNav;
use Util;
use MtcPharmacy\Multisite\Classes\MultisiteManager;

/**
 * Site Menu plugin Class.
 * Creates and manipulates with menu items.
 * Extends Eloquent model
 *
 * @author   mtc. Martins Fridenbergs <martins.fridenbergs@mtcmedia.co.uk>
 *
 * @version  2017-04-18
 */
class SiteMenu extends Model
{
    use SoftDeletes;
    /**
     * @var String Classes associated table
     */
    protected $table = 'site_menu';

    /**
     * @var Array List of fillable variables
     */
    protected $fillable = [
        'name',
        'url',
        'target',
        'parent_id',
        'category_list',
        'brand_list',
        'site_tree',
        'css_class',
        'published',
        'category_id',
        'single_category',
        'single_cms_page',
    ];


    public static function boot() : void
    {
        parent::boot();

        self::deleting(function (self $menu_entry) {

            $menu_entry->children
                ->each (function ($menu_entry) {
                    $menu_entry->delete();
                });

            foreach (self::getMenus() as $menu) {
                self::invalidateCache($menu);
            }
        });

        self::saved(function ($menu_entry) {
            foreach (self::getMenus() as $menu) {
                self::invalidateCache($menu);
            };
        });
    }

    /**
     * @var boolean Disables use of eloquent timestamps
     */
    public $timestamps = false;


    public static function fetchTree($menu)
    {
        $menu_tree = $menu->getSiteMenuTree(false);

        return $menu_tree;
    }


    private static function getMenu($name, $menu, $options)
    {
        $html = App::make('twig')->render('SiteMenu/menu.twig', [
            'items'     => $menu,
            'options'   => $options,
            'top_level' => true,
        ]);

        return $html;
    }


    private static function getCachedMenu($name, $menu, $options)
    {
        if (!is_dir(self::getCacheDir())) {
            mkdir(self::getCacheDir());
        }

        // Make sure cache refreshes every 5 minutes
        if (!file_exists(self::getCacheHtmlFile($name))
            || time() - filemtime(self::getCacheHtmlFile($name)) > Util::SECONDS_IN_A_MINUTE * 30
            || DEV_MODE
        ) {
            $html = App::make('twig')->render('SiteMenu/menu.twig', [
                'items'     => $menu,
                'options'   => $options,
                'top_level' => true,
            ]);
            $fh = fopen(self::getCacheHtmlFile($name), 'w');
            fwrite($fh, $html);
        } else {
            $html = file_get_contents(self::getCacheHtmlFile($name));
        }

        return $html;

    }

    /**
     * SiteMenu::renderMenu()
     *
     * Render menu for site usage
     * Nothing returned as called via hooks and they do not support value returning
     *
     * @access public
     * @static
     *
     * @param String $menu name of menu called as String
     *
     * @return null
     * @throws Exception
     */
    public static function renderMenu(string $menu): ?bool
    {
        global $p;

        // This method can be slow. Return here to bypass rending.
        //return;


        // fallback to container class defined in settings
        if (!isset($options['first_container_class'])) {
            $options['first_container_class'] = SITE_MENU_FIRST_CONTAINER_CLASS;
        }


        $root_menu = SiteMenu::find(CMS_ROOT_PAGE_ID);
        $mm = new MultisiteManager($root_menu);
        $display_root_menu = $mm->getDisplayEntity();

        $current_menu = null;

        $candidate_menus = self::where('name', $menu)->get();
        foreach ($candidate_menus as $candidate_menu) {
            $candidate_root_menu = $candidate_menu->getAncestors()->last();
            if ($candidate_root_menu->id == $display_root_menu->id) {
                $current_menu = $candidate_menu;
                break;
            }
        }

        if (! $current_menu) {
            return false;
        }

        if (! $current_menu->published) {
            return false;
        }

        $options['container_class'] = $current_menu->css_class;

        $menu_tree = self::fetchTree($current_menu);

        if (count($menu_tree)) {
            if (!empty($p->pagedata['Hide Pharmacy Items'][0]['value']) || (app('twig')->getGlobals()['hide_pharmacy_items'] ?? false)) {
                $menu .= '_without_children';
            }

            //echo self::getCachedMenu($menu, $menu_tree, $options);
            echo self::getMenu($menu, $menu_tree, $options);
        }

        return false;
    }

    /**
     * SiteMenu::allBrands()
     *
     * Retrieve root menu items
     *
     * @access public
     * @return array list of all brands
     */
    public function allBrands()
    {
        return Brand::getAllBrands();
    }

    /**
     * SiteMenu::listCategories()
     *
     * Retrieve list of categories to display
     *
     * @access public
     * @return array list of categories as an array
     */
    private function listCategories($parent_menu_item_id = 0)
    {
        if ($this->category_list == -1) {
            $categories = CategoryHelper::getCategoryTree(0, 1);
        } else {
            $categories = CategoryHelper::getCategoryTree($this->category_list, 1);
        }

        //Limit categories
        $limited_categories = [];
        $added = 0;
        $max = 35;

        foreach ($categories as $k => $category) {
            if ($added < $max) {
                $limited_categories[$k] = $category;
            } else {
                break;
            }

            $added++;
        }

        $categories = $limited_categories;

        foreach ($categories as $k => $cat) {

            $category = (new Category)->find($cat['id']);

            if (! $category) continue;

            $hidden = (bool) $category->hide;
            $parentID = 9999 + $category->id;
            $categories[$k]['id'] = $parentID;
            $categories[$k]['parent_id'] = $parent_menu_item_id;
            $categories[$k]['category_id'] = (int) $category->id;
            $categories[$k]['name'] = $category->name;
            $categories[$k]['published'] = !$hidden ? '1' : '0';
            $categories[$k]['target'] = '_self';
            $categories[$k]['css_class'] = '';

            $sub_tree = [];

            foreach($categories[$k]['sub_tree'] as $x => $subsub) {
                $sub_tree[$x]['id'] = 9999 + $subsub['id'];
                $sub_tree[$x]['parent_id'] = $parentID;
                $sub_tree[$x]['category_id'] = (int) $subsub['id'];
                $sub_tree[$x]['name'] = $subsub['name'];
                $sub_tree[$x]['url'] = $subsub['url'];
                $hiddensub = (bool) $category->hide;
                $sub_tree[$x]['published'] = !$hiddensub ? '1' : '0';
                $sub_tree[$x]['target'] = '_self';
                $sub_tree[$x]['css_class'] = '';
                $sub_tree[$x]['subs'] = [];
            }

            $categories[$k]['subs'] = $sub_tree;
            unset($categories[$k]['sub_tree']);
        }

        if ($categories) {
            foreach ($categories as $k => $category) {
                // Remove brackets and it's contents from category name
                $categories[$k]['name'] = trim(preg_replace('/\s*\([^)]*\)/', '', $category['name']));
            }
        }

        return $categories;
    }

    /**
     * SiteMenu::invalidateCache()
     *
     * Delete the cache file
     *
     * @access public
     * @static
     *
     * @param String $menu to remove the specific cache file for the menu being updated
     *
     * @return null
     */
    public static function invalidateCache($menu)
    {
        if (file_exists(self::getCacheFile($menu['name']))) {
            unlink(self::getCacheFile($menu['name']));
        }

        if (file_exists(self::getCacheHtmlFile($menu['name']))) {
            unlink(self::getCacheHtmlFile($menu['name']));
        }
    }

    /**
     * SiteMenu::getCacheFile()
     *
     * Retrieve cache file name
     * Cache file is built to be SITE and fork independent
     *
     * @access public
     * @static
     *
     * @param $menu
     *
     * @return string path to cache file
     */
    public static function getCacheFile($menu)
    {
        return self::getCacheDir() . SITE . '_' . MYSQL_DB . '_site_menu_' . $menu . '.cache';
    }
    /**
     * SiteMenu::getCacheHtmlFile()
     *
     * Retrieve html cache file name
     * Cache file is built to be SITE and fork independent
     *
     * @access public
     * @static
     *
     * @param $menu
     *
     * @return string path to cache file
     */
    public static function getCacheHtmlFile($menu)
    {
        return self::getCacheDir() . SITE . '_' . MYSQL_DB . '_site_menu_' . $menu . '_html.cache';
    }

    /**
     * SiteMenu::getCacheDir()
     *
     * Return path to cache dir
     * This will be cache dir outside public_html
     *
     * @access public
     * @static
     * @return string path to cache dir
     */
    public static function getCacheDir()
    {
        return rtrim(SITE_PATH, '/') . '/cache/';
    }

    /**
     * SiteMenu::getMenus()
     *
     * Retrieve all top level Site menus
     * Used to get list of menus for use in admin area
     *
     * @access   public
     * @static
     * @return Collection
     *
     * @author   mtc. Dawid Bestrzynski <dawid.bestrzynski@mtcmedia.co.uk>
     */
    public static function getMenus()
    {
        return self::where('parent_id', 0)->get();
    }

    /**
     * SiteMenu::getSubMenu()
     *
     * Retrieve a collection of child SiteMenus either all or only direct ones
     * @author   mtc. Dawid Bestrzynski <dawid.bestrzynski@mtcmedia.co.uk>
     *
     * @param boolean $direct just direct children or all children
     *
     * @return  Collection
     */
    public function getSubMenu($direct = false)
    {
        $sub_menu  = new Collection;
        $parent_id = $this->id ? $this->id : 0;

        $sub_items = self::where('parent_id', $parent_id)
                         ->orderBy('order', 'ASC')
                         ->get();

        foreach ($sub_items as $sub_item) {
            $sub_menu->push($sub_item);
            if (!$direct) {
                $sub_menu = $sub_menu->merge($sub_item->getSubMenu());
            }
        }

        return $sub_menu;
    }

    /**
     * SiteMenu::getSiteMenuTree()
     *
     * Retrieve a collection of child SiteMenus either all or only direct ones
     * @author   mtc. Dawid Bestrzynski <dawid.bestrzynski@mtcmedia.co.uk>
     *
     * @param boolean $all all SiteMenus in the tree or only published ones
     *
     * @return  Collection
     */
    public function getSiteMenuTree($all = true)
    {
        $tree = new Collection;


        if ($all) {
            $sub_items = self::where('parent_id', $this->id)
                             ->orderBy('order', 'ASC')
                             ->get();
        } else {
            $sub_items = self::where('parent_id', $this->id)
                             ->where('published', 1)
                             ->orderBy('order', 'ASC')
                             ->get();
        }

        foreach ($sub_items as $sub_item) {
            $sub_item->subs = $sub_item->getSiteMenuTree($all);

            if (!empty($sub_item->site_tree)) {
                $sub_item->cms_tree = CMSNav::tree($sub_item->site_tree);
            }

            if (!empty($sub_item->brand_list)) {
                $sub_item->sub_brands = self::allBrands();
            }
            
            
            if (!empty($sub_item->category_list)) {
                $sub_item->sub_categories = $sub_item->listCategories($sub_item->id);
                if(empty($sub_item->url)) {
                    $sub_item->url = browse_url($sub_item->category_list);
                }
                if(!empty($sub_item->sub_categories)) {
                    $sub_item->subs = $sub_item->sub_categories;
                }
            }

            if(empty($sub_item->url) && !empty($sub_item->link)) {
                $sub_item->url = $sub_item->link;
            }

            

            if(empty($sub_item->css_class)) {
                $sub_item->css_class = '';
            }

            $sub_item->parent_id = (int) $sub_item->parent_id;

            $tree->push($sub_item);

        }


        return $tree;
    }

    /**
     * SiteMenu::getAllSiteMenuOptions()
     *
     * Retrieve a string with option tags containing all SiteMenus to use in SiteMenu add/edit form
     * @author   mtc. Dawid Bestrzynski <dawid.bestrzynski@mtcmedia.co.uk>
     *
     * @param integer $direct_parent ID of direct parent SiteMenu to mark the correct option tag as selected
     *
     * @static
     * @return  array
     */
    public static function getAllSiteMenuOptions($direct_parent = 0)
    {
        $options   = [];
        $options[] = [
            'id'       => 0,
            'name'     => 'Root',
            'selected' => $direct_parent == 0,
            'sublevel' => 0,
        ];

        foreach (self::where('parent_id', 0)->orderBy('order', 'ASC')->get() as $item) {
            $item->getSiteMenuOptions($options, $direct_parent);
        }

        return $options;
    }

    /**
     * SiteMenu::getSiteMenuOptions()
     *
     * Add items containing child SiteMenus to the $option_array to be used in SiteMenu add/edit form
     * @author   mtc. Dawid Bestrzynski <dawid.bestrzynski@mtcmedia.co.uk>
     *
     * @param integer $direct_parent ID of direct parent SiteMenu to mark the correct option tag as selected
     * @param integer $sublevel level of nesting of the submenu
     *
     * @return  void
     */
    public function getSiteMenuOptions(&$option_array, $direct_parent = 0, $sublevel = 0)
    {
        array_push($option_array, [
            'id'       => $this->id,
            'name'     => $this->name,
            'selected' => $direct_parent == $this->id,
            'sublevel' => $sublevel,
        ]);

        foreach (self::where('parent_id', $this->id)->orderBy('order', 'ASC')->get() as $sub_item) {
            $sub_item->getSiteMenuOptions($option_array, $direct_parent, $sublevel + 1);
        }
    }

    /**
     * SiteMenu::getBreadcrumbs()
     *
     * Retrieve a array with links to be used for breadcrumbs
     * @author   mtc. Dawid Bestrzynski <dawid.bestrzynski@mtcmedia.co.uk>
     *
     * @param boolean $end_class flag for adding class="end" for the current SiteMenu
     *
     * @return  array
     */
    public function getBreadcrumbs($end_class = true)
    {
        $breadcrumbs   = [];
        $breadcrumbs[] = [
            'url'       => '/admin/',
            'title'     => 'Home',
            'end_class' => false,
        ];

        if ($_SERVER['SCRIPT_NAME'] == '/plugins/SiteMenu/admin/index.php'
            && !isset($_REQUEST['id'])
            && (!isset($_REQUEST['menu_id']) || $this->id == null)) {
            $manage_end = true;
        } else {
            $manage_end = false;
        }
        $breadcrumbs[] = [
            'url'       => '/plugins/SiteMenu/admin/index.php',
            'title'     => 'Manage menus',
            'end_class' => $manage_end,
        ];

        foreach ($this->getAncestors()->reverse() as $ancestor) {
            $breadcrumbs[] = [
                'url'       => '/plugins/SiteMenu/admin/index.php?id=' . $ancestor->id,
                'title'     => clean_page($ancestor->name),
                'end_class' => false,
            ];
        }

        $breadcrumbs[] = [
            'url'       => !$end_class ? '/plugins/SiteMenu/admin/index.php?id=' . $this->id : '',
            'title'     => clean_page($this->name),
            'end_class' => $end_class,
        ];

        if ((!$end_class || $this->id == 0) && $_SERVER['SCRIPT_NAME'] == '/plugins/SiteMenu/admin/edit.php') {
            $breadcrumbs[] = [
                'url'       => '',
                'title'     => 'Add menu item',
                'end_class' => true,
            ];
        }

        return $breadcrumbs;
    }

    /**
     * SiteMenu::getAncestors()
     *
     * Retrieve a collection of the ancestors of a SiteMenu
     * @author   mtc. Dawid Bestrzynski <dawid.bestrzynski@mtcmedia.co.uk>
     *
     * @return  Collection
     */
    public function getAncestors()
    {
        $ancestors = new Collection;

        if ($this->parent_id != 0) {
            $ancestors->push($this->parentMenu);
            $ancestors = $ancestors->merge($this->parentMenu->getAncestors());
        }

        return $ancestors;
    }

    /**
     * SiteMenu::parentMenu()
     *
     * Eloquent relation with the direct parent SiteMenu
     * @author   mtc. Dawid Bestrzynski <dawid.bestrzynski@mtcmedia.co.uk>
     *
     * @return  HasOne
     */

    public function parentMenu()
    {
        return $this->hasOne(SiteMenu::class, 'id', 'parent_id');
    }

    /**
     * Relationship with child menu entries
     *
     * @return \Illuminate\Database\Eloquent\Relations\HasMany
     */
    public function children()
    {
        return $this->hasMany(SiteMenu::class, 'parent_id');
    }

    /**
     * SiteMenu::directChildrenCount()
     *
     * Retrieve the number of direct children of a SiteMenu
     * @author   mtc. Dawid Bestrzynski <dawid.bestrzynski@mtcmedia.co.uk>
     *
     * @return  integer
     */
    public function directChildrenCount()
    {
        return self::where('parent_id', $this->id)->count();
    }

    /**
     * Resets cache
     */
    public static function resetCache() {
        foreach (self::getMenus() as $menu) {
            self::invalidateCache($menu);
        };
    }


    public function replicateWithRelations()
    {
        $clone = $this->replicate();
        $clone->save();

        foreach ($this->children as $sub_menu) {
            $sub_menu_clone = $sub_menu->replicateWithRelations();
            $clone->children()->save($sub_menu_clone);
        }

        return $clone;
    }


    public function getAdminUrl()
    {
        $url = "/plugins/SiteMenu/admin/?id={$this->id}";

        return $url;
    }
}
