<?php

namespace Mtc\Core\Admin;

use Illuminate\Database\Eloquent\Builder;
use \Illuminate\Database\Eloquent\Model;
use AdminUser;
use CMSNav;
use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Support\Collection;
use Twig\Environment;

/**
 * Admin\Menu class.  This looks after the admin_menu table
 * using Eloquent base modelling
 *
 * @author william.cameron
 */
class Menu extends Model
{

    public $timestamps = false;
    protected $table = "admin_menu";

    /**
     * Cached menu items keyed by id including bound roles.
     * @var array<int,Menu|null>
     */
    protected static array $cachedById = [];

    /**
     * Cached menu items keyed by constant including bound roles.
     * @var array<string,Menu|null>
     */
    protected static array $cachedByConstant = [];

    protected static bool $cacheInitialized = false;

    protected static function ensureCacheLoaded(): void
    {
        if (self::$cacheInitialized) {
            return;
        }

        self::$cacheInitialized = true;

        $menuItems = self::query()
            ->with('boundRoles')
            ->get();

        foreach ($menuItems as $menu) {
            self::$cachedById[$menu->id] = $menu;
            if (!empty($menu->constant)) {
                self::$cachedByConstant[$menu->constant] = $menu;
            }
        }
    }

    const GLOBAL_KEY = 'admin_menu';

    const MENU_MTC_ONLY = 'MTC Only';
    const MENU_CONFIGURATION = 'Configuration';

    protected $fillable = [
        'sub_id',
        'title',
        'path',
        'activePath',
        'constant',
        'icon',
        'new_window',
        'order',
    ];

    /**
     * Does this menu item have sub items
     *
     * @return bool
     */
    public function hasSubItems()
    {
        return ! $this->subItems->isEmpty();
    }

    /**
     * configures and handles the relation of subitems for this menu item
     *
     * @return HasMany
     */
    public function subItems()
    {
        return $this->hasMany(self::class, 'sub_id', 'id')
            ->orderBy('order', 'ASC')
            ->orderBy('title', 'ASC');
    }

    public function boundRoles()
    {
        return $this->hasMany(RolePermission::class, 'permission_id');
    }

    public static function getWithBoundRoles(int $id): ?self
    {
        self::ensureCacheLoaded();

        return self::$cachedById[$id] ?? null;
    }

    public static function getByConstant(string $constant): ?self
    {
        self::ensureCacheLoaded();

        return self::$cachedByConstant[$constant] ?? null;
    }

    /**
     * Define relationship with parent menu item
     * @return \Illuminate\Database\Eloquent\Relations\BelongsTo
     * @author Martins Fridenbergs <martins.fridenbergs@mtcmedia.co.uk>
     */
    public function parent()
    {
        return $this->belongsTo(self::class, 'sub_id');
    }

    /**
     * Generate HTML for all sub pages of a particular ID
     *
     * @param $start Int  ID of parent to get children of
     * @return $navigation mixed Menu structure in HTML
     */
    public static function getSubPages($start = 0)
    {
        $navigation = '';
        $sub_pages = self::where('sub_id', $start)
                    ->orderBy('order', 'ASC')
                    ->orderBy('title', 'ASC')
                    ->get();

        if (!empty($sub_pages)) {
            ob_start();
            require SITE_PATH . '/core/admin/includes/admin_menu_nav.php';
            $navigation = ob_get_contents();
        }

        return $navigation;
    }


    /**
     * Scope - ChildOf()
     * Define scope to check if menu item is a child of different menu item
     *
     * @param Builder $query Query builder object
     * @param int $menu_id parent menu item ID
     * @return Builder
     * @author Martins Fridenbergs <martins.fridenbergs@mtcmedia.co.uk>
     */
    public function scopeChildOf($query, $menu_id)
    {
        return $query->where('sub_id', $menu_id);
    }

    /**
     * Scope - isRoot()
     * Find only root level menu item
     *
     * @param Builder $query Query builder object
     * @return Builder
     * @author Martins Fridenbergs <martins.fridenbergs@mtcmedia.co.uk>
     */
    public function scopeIsRoot($query)
    {
        return $query->ChildOf(0);
    }

    /**
     * Check whether the current admin user is allowed to view this page
     * @return bool whether user is allowed to this admin menu
     * @author Martins Fridenbergs <martins.fridenbergs@mtcmedia.co.uk>
     */
    public function allowed(): bool
    {
        // If the user doesn't have access to this admin menu, hide it
        if (!AdminUser::can($this->id, $_SESSION['adminId'])) {
            return false;
        }

        // If there's no constant, show it always
        if (empty($this->constant)) {
            return true;
        }

        // If the constant is config value and it's enabled, show it
        if (!empty(config($this->constant))) {
            return true;
        }

        // If it's the old approach with constant from settings, and it's true, show it
        return defined($this->constant) &&
            constant($this->constant);
    }

    /**
     * Check whether menu item has children that are active
     * @return bool whether menu item has children that are active
     * @author Martins Fridenbergs <martins.fridenbergs@mtcmedia.co.uk>
     */
    public function hasActiveChild(): bool
    {
        foreach ($this->subItems as $child_menu) {
            if ($child_menu->activePath && strstr($_SERVER['SCRIPT_NAME'], $child_menu->activePath)) {
                return true;
            }
        }
        return false;
    }

    /**
     * Check whether menu item is in active path
     * @return bool whether the menu item is within active path
     * @author Martins Fridenbergs <martins.fridenbergs@mtcmedia.co.uk>
     */
    public function isActive()
    {
        return $this->activePath && (bool)strstr($_SERVER['REQUEST_URI'], $this->activePath);
    }

    /**
     * Check if CMS tree should be shown for this menu item
     * @return string CMS tree under this item or empty string
     * @author Martins Fridenbergs <martins.fridenbergs@mtcmedia.co.uk>
     * //TODO: Move this to CMS
     */
    public function showCMSTreeIfAllowed($admin_user)
    {
        $is_admin = $admin_user instanceof AdminUser ?
            $admin_user->isMtcAdmin() :
            $admin_user->role === 0;

        if (
            !empty($this->title)
            && !empty($this->parent->title)
            && $this->activePath
            && strstr($_SERVER['SCRIPT_NAME'], $this->activePath)
            && $this->parent->title === "Content Manager"
            && $this->title === "Manage Pages"
        ) {

            $site_tree = CMSNav::siteTree(0, PHP_INT_MAX, [
                'check_innav' => false,
                'check_published' => false
            ]);

            $current_active_page = filter_input(INPUT_GET, 'page');

            $options = [
                'first_container_id' => 'site-tree',
                'title_container_tag' => 'span',
                'active_page_id' => $current_active_page,
                'is_admin' => $is_admin
            ];

            return CMSNav::generateSiteTreeMenu($site_tree, $options);
        }
        return '';
    }

    /**
     * Check if CMS tree should be shown for this menu item
     * @return string CMS tree under this item or empty string
     * @author Martins Fridenbergs <martins.fridenbergs@mtcmedia.co.uk>
     * //TODO: Move this to CMS
     */
    public static function showCMSTree(User $admin_user)
    {
        if (stripos(request()->getRequestUri(), 'admin/content_manager') === false) {
            return '';
        }

        $site_tree = CMSNav::siteTree(0, PHP_INT_MAX, [
            'check_innav' => false,
            'check_published' => false
        ]);


        $is_admin = $admin_user->hasRole('mtc');

        $options = [
            'first_container_id' => 'site-tree',
            'title_container_tag' => 'span',
            'active_page_id' => request()->input('page', 0),
            'is_admin' => $is_admin
        ];

        ob_start();
        CMSNav::generateSiteTreeMenu($site_tree, $options);
        return ob_get_clean();
    }

    /**
     * Gets admin menu and injects plugin menus
     *
     * @return mixed
     */
    public static function getAdminMenu()
    {
        static $cachedMenu;

        if ($cachedMenu !== null) {
            return $cachedMenu;
        }

        $adminMenu = self::query()
            ->with([
                'subItems',
                'subItems.parent',
                'subItems.boundRoles',
                'boundRoles',
            ])
            ->orderBy('order', 'ASC')
            ->isRoot()
            ->get()
            ->reject(function (Menu $menu_item) {
                // We can't allow non-mtc users to see mtc only
                if ($_SESSION['adminId'] != MTCADMIN_USERID && $menu_item->id == 1) {
                    return true;
                }

                // reject disallowed entries
                if (!$menu_item->allowed()) {
                    return true;
                }

                // reject menus without children
                if (count($menu_item->subItems) == 0) {
                    return true;
                }

                // Check if we have child elements that are allowed to show
                $allowed_menu_entries = $menu_item->subItems
                    ->each(function (Menu $sub_item) {
                        // reject disallowed entries
                        if (!$sub_item->allowed()) {
                            return false;
                        }

                        // allow if no constant is set
                        if (empty($sub_item->constant)) {
                            return true;
                        }

                        // Check the constant value
                        return constant($sub_item->constant);
                    });

                // Reject elements without any allowed children
                return count($allowed_menu_entries) == 0;
            });

        return $cachedMenu = $adminMenu;
    }
}
