<?php
    declare(strict_types=1);

    namespace Icing\View\Helper;

    use BootstrapUI\View\Helper\OptionsAwareTrait;
    use Cake\Http\ServerRequestFactory;
    use Cake\Routing\Router;
    use Cake\Utility\Hash;
    use Cake\Utility\Inflector;
    use Cake\Utility\Text;
    use Cake\View\Helper;
    use Cake\View\StringTemplateTrait;

    /**
     * Nav helper
     * @property \Icing\View\Helper\HtmlHelper $Html
     * @property \Cake\View\Helper\UrlHelper $Url
     */
    class NavHelper extends Helper
    {

        use StringTemplateTrait;
        use OptionsAwareTrait;

        /**
         * Default configuration.
         *
         * @var array<string, mixed>
         */
        protected array $_defaultConfig = [
            'iconClassSuffix' => 'fa-lg lh-1 nav-link-icon fa-fw',
            'linkClassSuffix' => 'nav-link',
            'templates'       => [
                'ul'       => '<ul class="nav flex-column {{class}}" {{attrs}}>{{nav}}</ul>',
                'li'       => '<li class="nav-item {{class}}" {{attrs}}>{{link}}{{dropdown}}</li>',
                'link'     => '<a href="{{url}}" class="nav-link {{class}}" {{attrs}}>{{icon}}<span>{{label}}</span></a>',
                'icon'     => '<i class="{{icon}} fa-lg lh-1 nav-link-icon fa-fw"></i>',
                'dropdown' => '<div id="{{id}}" class="collapse {{class}}" {{attrs}}>{{ul}}</div>',
            ]
        ];

        protected array $helpers = ['Html', 'Url'];

        public function _isActive($url): bool
        {
            if (!$url) {
                return false;
            }
            if (is_array($url)) {
                return c($url)->some(
                    function ($url) {
                        return $this->_isActive($url);
                    }
                );
            }
            if (str_starts_with($url, '#')) {
                return false;
            }
            $url  = $this->Url->build($url);
            $path = Router::url(Hash::get(parse_url(urldecode($url)), 'path'));
            if (!$path) {
                return false;
            }

            $regex = self::pathToRegex($path);
            return preg_match($regex, $this->getView()->getRequest()->getPath()) === 1;
        }

        static public function pathToRegex($path): string
        {
            $regex = $path;
            return '/^' . str_replace('/', '\/', $regex) . '$/';
        }

        public function _isChildActive($children = []): bool
        {
            if ($children) {
                $urls = Hash::extract($children, '{n}.url');
                if ($this->_isActive($urls)) {
                    return true;
                }
                $also = Hash::flatten(Hash::extract($children, '{n}.also'));
                if ($this->_isActive($also)) {
                    return true;
                }
                if ($grandchildren = Hash::extract($children, '{n}.children.{n}')) {
                    return $this->_isChildActive($grandchildren);
                }
            }
            return false;
        }

        protected function _normalize($children = []): array
        {
            if (!is_array($children)) {
                return [];
            }
            return c($children)
                ->map(
                    function ($item, $label) {
                        if (is_scalar($item)) {
                            $item = ['url' => $item];
                        }
                        $item['label'] = $item['label'] ?? $label;

                        if (!empty($item['children'])) {
                            $item['children'] = $this->_normalize($item['children']);
                        }

                        return $item;
                    }
                )
                ->toList();
        }

        public function ul($nav, $options = ['class' => 'navbar-nav']): string
        {
            $nav = $this->_normalize($nav);
            $nav = c($nav)->reduce(
                function ($nav, $item) {
                    return $nav . $this->li($item);
                },
                ''
            );

            return $this->templater()->format(
                'ul',
                [
                    'nav'   => $nav,
                    'attrs' => $this->templater()->formatAttributes($options, ['class', 'also']),
                    'class' => $options['class'] ?? ''
                ]
            );
        }

        public function li($label, $url = '', $icon = '', $children = [], $options = []): string
        {
            if (is_array($label)) {
                $options = $label + compact('url', 'icon', 'children');
                ['label' => $label, 'url' => $url, 'icon' => $icon, 'children' => $children] = $options;
                unset($options['label'], $options['url'], $options['icon'], $options['children']);
            }

            $li = $options['li'] ?? [];
            unset($options['li']);

            $active = $options['active'] ?? null;
            unset($options['active']);

            if ($icon) {
                $icon = $this->templater()->format('icon', compact('icon'));
            }

            if (!empty($children)) {
                if (!isset($options['also'])) {
                    $children = c($children)
                        ->map(
                            function ($child) {
                                if (!isset($child['also']) && isset($child['url'])) {
                                    $child['url'] = $this->Url->build($child['url']);

                                    $urlParams = Router::parseRequest(ServerRequestFactory::fromGlobals(['REQUEST_URI' => $child['url']]));
                                    if ($urlParams['action'] === 'index') {
                                        $child['also'] = $this->alsoForAll($urlParams['controller']);
                                    }
                                }

                                if (!empty($child['also'])) {
                                    $child['also'] = c((array)$child['also'])
                                        ->map(fn($url) => $this->Url->build($url))
                                        ->toList();
                                }

                                return $child;
                            }
                        )
                        ->toArray();
                }
            } elseif (!empty($url)) {
                $url = $this->Url->build($url);

                //Automatically attach CRUD operations (unless also is set false manually)
                if (!isset($options['also'])) {
                    $urlParams = Router::parseRequest(ServerRequestFactory::fromGlobals(['REQUEST_URI' => $url]));

                    //debug($urlParams);
                    if ($urlParams['action'] === 'index') {
                        $options['also'][] = ['action' => 'edit', 0 => '.*'] + $urlParams;
                        $options['also'][] = ['action' => 'view', 0 => '.*'] + $urlParams;
                    }
                }
            }

            if (!empty($options['also'])) {
                $options['also'] = c((array)$options['also'])->map(fn($url) => $this->Url->build($url))->toList();
            }

            if ($active === null) {
                $active = $this->_isActive($url) || $this->_isChildActive($children) || $this->_isActive($options['also'] ?? []);
            }
            if ($active) {
                $options = $this->injectClasses('active', $options);
            }

            if (!empty($children)) {

                $li = $this->injectClasses(['dropdown'], $li);

                $id  = 'collapse-' . Text::uuid();
                $url = "#{$id}";

                $options += [
                    'data-bs-toggle' => "collapse",
                    'role'           => "button",
                    'aria-controls'  => $id,
                    'aria-expanded' => $active ? 'true' : 'false'
                ];

                $dropdown = $this->templater()->format(
                    'dropdown',
                    [
                        'id'   => $id,
                        'ul'   => $this->ul($children, ['class' => '']),
                        'class' => $active ? 'show' : '',
                    ]
                );
            }

            $link = $this->templater()->format(
                'link',
                [
                    'label' => $label,
                    'icon'  => $icon,
                    'url'   => $url,
                    'class' => $options['class'] ?? '',
                    'attrs' => $this->templater()->formatAttributes($options, ['class', 'also']),
                ]
            );

            return $this->templater()->format(
                'li',
                [
                    'link'     => $link,
                    'dropdown' => $dropdown ?? '',
                    'attrs'    => $this->templater()->formatAttributes($li, ['class']),
                    'class'    => $li['class'] ?? ''
                ]
            );
        }

        public function alsoForAll($table): array
        {
            $table = Inflector::dasherize(Inflector::pluralize($table));
            return [
                "/{$table}",
                "/{$table}/index",
                "/{$table}/add",
                "/{$table}/edit/.*",
                "/{$table}/view/.*",
            ];
        }

    }
