<?php

namespace Croogo\Core\View\Helper;

use Cake\Core\Configure;
use Cake\ORM\TableRegistry;
use Cake\Utility\Hash;
use Cake\Utility\Inflector;
use Cake\Utility\Text;
use Cake\View\Helper;
use Cake\View\Helper\HtmlHelper;
use Cake\View\View;
use Croogo\Core\Database\Type\ParamsType;
use Croogo\Core\PluginManager;
use Croogo\Core\Status;

/**
 * Croogo Helper
 *
 * @category Helper
 * @package  Croogo.Croogo.View.Helper
 * @version  1.0
 * @author   Fahad Ibnay Heylaal <contact@fahad19.com>
 * @license  http://www.opensource.org/licenses/mit-license.php The MIT License
 * @link     http://www.croogo.org
 */
class CroogoHelper extends Helper
{

    /**
     * @var array
     */
    public $helpers = [
        'Form',
        'Html' => [
            'className' => 'Croogo/Core.Html'
        ],
        'Url',
        'Croogo/Core.Layout',
        'Croogo/Core.Theme',
        'Croogo/Menus.Menus',
        'Croogo/Acl.Acl',
    ];

    /**
     * ParamsType instance
     *
     * @var Croogo\Core\Database\Type\ParamsType;
     */
    protected $_ParamsType;

    /**
     * Default Constructor
     *
     * @param View $View The View this helper is being attached to.
     * @param array $settings Configuration settings for the helper.
     */
    public function __construct(View $View, $settings = [])
    {
        $this->helpers[] = Configure::read('Site.acl_plugin') . '.' . Configure::read('Site.acl_plugin');
        parent::__construct($View, $settings);
        $this->_CroogoStatus = new Status();
        $this->_ParamsType = new ParamsType;
    }

    /**
     * @return array
     */
    public function statuses()
    {
        return $this->_CroogoStatus->statuses();
    }

    /**
     * Convenience method to Html::script() for admin views
     *
     * This method does nothing if request is ajax or not in admin prefix.
     *
     * @param string|array  $url Javascript files to include
     * @param array|bool $options Options or Html attributes
     * @return string|null String of <script /> tags or null
     * @see HtmlHelper::script()
     */
    public function adminScript($url, $options = [])
    {
        $options = Hash::merge(['block' => true, 'defer' => true], $options);
        $request = $this->getView()->getRequest();
        if ($request->is('ajax') || $request->getParam('prefix') !== 'admin') {
            return null;
        }

        return $this->Html->script($url, $options);
    }

    /** Generate Admin menus added by Nav::add()
     *
     * @param array $menus
     * @param array $options
     * @return string menu html tags
     */
    public function adminMenus($menus, $options = [], $depth = 0)
    {
        $options = Hash::merge([
            'type' => 'sidebar',
            'children' => true,
            'htmlAttributes' => [
                'class' => 'nav flex-column',
            ],
            'itemTag' => 'li',
            'listTag' => 'ul',
        ], $options);

        $userId = $this->getView()->getRequest()->getSession()->read('Auth.User.id');
        if (empty($userId)) {
            return '';
        }

        $sidebar = $options['type'] === 'sidebar';
        $htmlAttributes = $options['htmlAttributes'];
        $out = null;
        $sorted = Hash::sort($menus, '{s}.weight', 'ASC');
        if (empty($this->Role)) {
            $this->Role = TableRegistry::get('Croogo/Users.Roles');
            $this->Role->addBehavior('Croogo/Core.Aliasable');
        }
        $currentRole = $this->Role->byId($this->Layout->getRoleId());

        foreach ($sorted as $menu) {
            if (isset($menu['separator'])) {
                if ($options['itemTag'] === false) {
                    $liOptions['class'] = 'dropdown-divider';
                    $out .= $this->Html->tag('div', '', $liOptions);
                } else {
                    $liOptions['class'] = 'divider';
                    $out .= $this->Html->tag($options['itemTag'], '', $liOptions);
                }
                continue;
            }
            if ($currentRole != 'superadmin' && !$this->Acl->linkIsAllowedByUserId($userId, $menu['url'])) {
                continue;
            }

            if (empty($menu['htmlAttributes']['class'])) {
                $menuClass = Text::slug(strtolower('menu-' . $menu['title']), '-');
                $menu['htmlAttributes'] = Hash::merge([
                    'class' => $menuClass,
                ], $menu['htmlAttributes']);
            }
            $title = '';
            if ($menu['icon'] === false) {
            } elseif (empty($menu['icon'])) {
                $menu['htmlAttributes'] += ['icon' => 'white'];
            } else {
                $menu['htmlAttributes'] += ['icon' => $menu['icon']];
            }
            if ($sidebar) {
                $title .= '<span>' . h($menu['title']) . '</span>';
            } else {
                $title .= $menu['title'];
            }
            $children = '';
            if (!empty($menu['children'])) {
                $childClass = '';
                if ($sidebar) {
                    $itemTag = 'li';
                    $listTag = 'ul';
                    $childClass = 'nav flex-column sub-nav ';
                    $childClass .= ' submenu-' . Text::slug(strtolower($menu['title']), '-');
                    if ($depth > 0) {
                        $childClass .= ' dropdown-menu';
                    }
                } else {
                    if ($depth == 0) {
                        $childClass = 'dropdown-menu';
                    }
                    $itemTag = false;
                    $listTag = 'div';
                }
                $children = $this->adminMenus($menu['children'], [
                    'type' => $options['type'],
                    'children' => true,
                    'htmlAttributes' => ['class' => $childClass],
                    'itemTag' => $itemTag,
                    'listTag' => $listTag,
                ], $depth + 1);

                $menu['htmlAttributes']['class'] .= ' hasChild dropdown-close';
            }

            $menuUrl = $this->Url->build($menu['url']);
            if ($menuUrl == env('REQUEST_URI')) {
                if (isset($menu['htmlAttributes']['class'])) {
                    $menu['htmlAttributes']['class'] .= ' current';
                } else {
                    $menu['htmlAttributes']['class'] = 'current';
                }
            }

            if (!$sidebar && !empty($children)) {
                $menu['htmlAttributes']['class'] = 'dropdown-toggle';
                $menu['htmlAttributes']['data-toggle'] = 'dropdown';
            }

            if (!$sidebar && $depth == 0) {
                $menu['htmlAttributes']['class'] .= ' nav-link';
            } elseif (!$sidebar && $depth > 0) {
                $menu['htmlAttributes']['class'] .= ' dropdown-item';
            } else {
                $menu['htmlAttributes']['class'] .= ' sidebar-item';
            }

            if (isset($menu['before'])) {
                $title = $menu['before'] . $title;
            }

            if (isset($menu['after'])) {
                $title = $title . $menu['after'];
            }

            $menu['htmlAttributes']['escape'] = false;

            $link = $this->Html->link($title, $menu['url'], $menu['htmlAttributes']);
            if ($options['itemTag'] === false) {
                $out .= $link;
                continue;
            }

            $liOptions = [
                'class' => 'nav-item',
            ];
            if ($sidebar && !empty($children) && $depth > 0) {
                $liOptions['class'] .= ' dropdown-submenu';
            }
            if (!$sidebar && !empty($children)) {
                if ($depth > 0) {
                    $liOptions['class'] .= ' dropdown-submenu';
                } else {
                    $liOptions['class'] .= ' dropdown';
                }
            }

            $out .= $this->Html->tag($options['itemTag'], $link . $children, $liOptions);
        }

        if (!$sidebar && $depth > 0) {
            $htmlAttributes['class'] = 'dropdown-menu';
        }

        return $this->Html->tag($options['listTag'], $out, $htmlAttributes);
    }

    /**
     * Show links under Actions column
     *
     * @param int $id
     * @param array $options
     * @return string
     */
    public function adminRowActions($id, $options = [])
    {
        $request = $this->getView()->getRequest();
        $key = $output = '';
        $plugin = $request->getParam('plugin');
        if ($plugin) {
            $key .= $plugin . '.';
        }
        $prefix = $request->getParam('prefix');
        if ($prefix) {
            $key .= Inflector::camelize($prefix) . '/';
        }
        $key .= Inflector::camelize($this->getView()->getRequest()->getParam('controller')) . '/';
        $key .= $request->getParam('action');
        $encodedKey = base64_encode($key);
        $rowActions = Configure::read('Admin.rowActions.' . $encodedKey);
        if (is_array($rowActions)) {
            foreach ($rowActions as $title => $link) {
                $linkOptions = $options;
                if (is_array($link)) {
                    $config = $link[key($link)];
                    if (isset($config['options'])) {
                        $linkOptions = Hash::merge($options, $config['options']);
                    }
                    if (isset($config['confirm'])) {
                        $linkOptions['confirm'] = $config['confirm'];
                        unset($config['confirm']);
                    }
                    if (isset($config['title'])) {
                        $title = $config['title'];
                    }
                    $link = key($link);
                }
                $link = $this->Menus->linkStringToArray(str_replace(':id', $id, $link));
                if (isset($linkOptions['icon'])) {
                    $linkOptions['escapeTitle'] = false;
                }
                $output .= $this->adminRowAction($title, $link, $linkOptions);
            }
        }

        return $output;
    }

    /**
     * Show link under Actions column
     *
     * ### Options:
     *
     * - `method` - when 'POST' is specified, the FormHelper::postLink() will be
     *              used instead of HtmlHelper::link()
     * - `rowAction` when bulk submissions is used, defines which action to use.
     *
     * @param string $title The content to be wrapped by <a> tags.
     * @param string|array $url Cake-relative URL or array of URL parameters, or external URL (starts with http://)
     * @param array $options Array of HTML attributes.
     * @param string $confirmMessage JavaScript confirmation message.
     * @return string An `<a />` element
     */
    public function adminRowAction($title, $url = null, $options = [], $confirmMessage = false)
    {
        $action = false;
        $options = Hash::merge([
            'escapeTitle' => true,
            'escape' => true,
            'confirm' => $confirmMessage,
        ], $options);

        if (is_array($url)) {
            $action = $url['action'];
            if (isset($options['class'])) {
                $options['class'] .= ' ' . $url['action'];
            } else {
                $options['class'] = $url['action'];
            }
        }
        if (isset($options['icon']) && empty($title)) {
            $options['iconInline'] = false;
        }

        if (!empty($options['rowAction'])) {
            $options['data-row-action'] = $options['rowAction'];
            unset($options['rowAction']);

            return $this->_bulkRowAction($title, $url, $options);
        }

        if (!empty($options['method']) && strcasecmp($options['method'], 'post') == 0) {
            $usePost = true;
            unset($options['method']);
        }

        if ($action == 'delete' || isset($usePost)) {
            $options['block'] = true;
            $postLink = $this->Form->postLink($title, $url, $options);

            return $postLink;
        }

        return $this->Html->link($title, $url, $options);
    }

    /**
     * Creates a special type of link for use in admin area.
     *
     * Clicking the link will automatically check a corresponding checkbox
     * where element id is equal to $url parameter and immediately submit the form
     * it's on.  This works in tandem with Admin.processLink() in javascript.
     */
    protected function _bulkRowAction($title, $url = null, $options = [])
    {
        if (!empty($options['confirm'])) {
            $options['data-confirm-message'] = $options['confirm'];
            unset($options['confirm']);
        }
        if (isset($options['icon'])) {
            $options['iconInline'] = false;
        }
        $output = $this->Html->link($title, $url, $options);

        return $output;
    }

    /**
     * Create an action button
     *
     * @param string $title Button title
     * @param url|string $url URL
     * @param array $options Options array
     * @param string $confirmMessage Confirmation message
     * @return string
     */
    public function adminAction($title, $url, $options = [], $confirmMessage = false)
    {
        $options = Hash::merge([
            'button' => 'outline-secondary',
            'class' => 'btn-sm',
            'list' => false,
            'confirm' => $confirmMessage,
            'escape' => false,
        ], $options);
        if ($options['list'] === true) {
            $list = true;
            unset($options['list']);
        }
        if (isset($options['method']) && strcasecmp($options['method'], 'post') == 0) {
            $options['block'] = 'scriptBottom';
            $out = $this->Form->postLink($title, $url, $options);
        } else {
            $out = $this->Html->link($title, $url, $options);
        }
        if (isset($list)) {
            $out = $this->Html->tag('li', $out);
        } else {
            $out = $this->Html->div('btn-group', $out);
        }

        return $out;
    }

    /**
     * Create a tab title/link
     */
    public function adminTab($title, $url, $options = [])
    {
        $options = Hash::merge([
            'data-toggle' => 'tab',
        ], $options);

        $options = $this->addClass($options, 'nav-link');

        return $this->Html->tag('li', $this->Html->link($title, $url, $options), [
            'class' => 'nav-item',
        ]);
    }

    /**
     * Show tabs
     *
     * @return string
     */
    public function adminTabs($show = null)
    {
        if (!isset($this->adminTabs)) {
            $this->adminTabs = false;
        }

        $output = '';
        $actions = '';
        $request = $this->getView()->getRequest();
        if ($request->getParam('prefix')) {
            $actions .= Inflector::camelize($request->getParam('prefix')) . '/';
        }
        $actions .= Inflector::camelize($request->getParam('controller')) . '/' . $request->getParam('action');
        $tabs = Configure::read('Admin.tabs.' . $actions);
        if (is_array($tabs)) {
            foreach ($tabs as $title => $tab) {
                $tab = Hash::merge([
                    'options' => [
                        'linkOptions' => [],
                        'elementData' => [],
                        'elementOptions' => [],
                    ],
                ], $tab);

                if (!isset($tab['options']['type']) ||
                    (isset($tab['options']['type']) &&
                        (in_array($this->_View->viewVars['typeAlias'], $tab['options']['type'])))
                ) {
                    $domId = strtolower(Inflector::singularize($request->getParam('controller'))) .
                        '-' .
                        strtolower(Text::slug($title, '-'));
                    if ($this->adminTabs) {
                        if (isset($this->_View->viewVars['viewVar'])) {
                            $entity = $this->_View->viewVars[$this->_View->viewVars['viewVar']];
                            $tab['options']['elementData']['entity'] = $entity;
                        }
                        $output .= $this->Html->tabStart($domId);
                        $output .= $this->_View->element(
                            $tab['element'],
                            $tab['options']['elementData'],
                            $tab['options']['elementOptions']
                        );
                        $output .= $this->Html->tabEnd();
                    } else {
                        $output .= $this->adminTab(__d('croogo', $title), '#' . $domId, $tab['options']['linkOptions']);
                    }
                }
            }
        }

        $this->adminTabs = true;

        return $output;
    }

    /**
     * Show Boxes
     *
     * @param array $boxName
     */
    public function adminBoxes($boxName = null)
    {
        if (!isset($this->boxAlreadyPrinted)) {
            $this->boxAlreadyPrinted = [];
        }

        $output = '';
        $request = $this->getView()->getRequest();
        $box = $request->getParam('controller') . '/' . $request->getParam('action');
        if ($request->getParam('prefix')) {
            $box = Inflector::camelize($request->getParam('prefix')) . '/' . $box;
        }
        $allBoxes = Configure::read('Admin.boxes.' . $box);
        $allBoxes = empty($allBoxes) ? [] : $allBoxes;
        $boxNames = [];

        if (is_null($boxName)) {
            foreach ($allBoxes as $boxName => $value) {
                if (!in_array($boxName, $this->boxAlreadyPrinted)) {
                    $boxNames[$boxName] = $allBoxes[$boxName];
                }
            }
        } elseif (!in_array($boxName, $this->boxAlreadyPrinted)) {
            if (array_key_exists($boxName, $allBoxes)) {
                $boxNames[$boxName] = $allBoxes[$boxName];
            }
        }

        foreach ($boxNames as $title => $box) {
            $box = Hash::merge([
                'options' => [
                    'linkOptions' => [],
                    'elementData' => [],
                    'elementOptions' => [],
                ],
            ], $box);
            $issetType = isset($box['options']['type']);
            $typeInTypeAlias = $issetType && in_array($this->_View->viewVars['typeAlias'], $box['options']['type']);
            if (!$issetType || $typeInTypeAlias) {
                if (isset($this->_View->viewVars['viewVar'])) {
                    $entity = $this->_View->viewVars[$this->_View->viewVars['viewVar']];
                    $box['options']['elementData']['entity'] = $entity;
                }
                $output .= $this->Html->beginBox($title);
                $output .= $this->_View->element(
                    $box['element'],
                    $box['options']['elementData'],
                    $box['options']['elementOptions']
                );
                $output .= $this->Html->endBox();
                $this->boxAlreadyPrinted[] = $title;
            }
        }

        return $output;
    }

    /**
     * @param $target
     *
     * @return \Cake\View\Cell
     */
    public function linkChooser($target)
    {
        $linkChooser = $this->_View->element('Croogo/Core.admin/modal', [
            'id' => 'link-chooser',
            'modalSize' => 'modal-lg'
        ]);
        if (!strstr($this->_View->fetch('page-footer'), $linkChooser)) {
            $this->_View->append('page-footer', $linkChooser);
        }

        return $this->_View->cell('Croogo/Core.Admin/LinkChooser', [$target]);
    }

    /**
     * @param $theme
     * @param $path
     * @param null $allowedMimeTypes
     *
     * @return string|null
     */
    public function dataUri($theme, $path, $allowedMimeTypes = null)
    {
        $allowedMimeTypes = array_filter(array_merge([
            'image/jpeg',
            'image/png',
        ], (array)$allowedMimeTypes));
        if ($theme) {
            $file = PluginManager::path($theme) . '/webroot/' . $path;
        } else {
            $file = WWW_ROOT . $path;
        }
        if (!file_exists($file)) {
            return null;
        }
        $mimeType = mime_content_type($file);
        if (!in_array($mimeType, $allowedMimeTypes)) {
            return null;
        }
        $dataUri = sprintf(
            'data:%s;base64,%s',
            $mimeType,
            base64_encode(file_get_contents($file))
        );

        return $dataUri;
    }
}
