module CamaleonCms
  module Frontend
    module NavMenuHelper
      # draw nav menu as html list
      # key: slug for nav menu
      # to register this, go to admin -> appearance -> menus
      # (DEPRECATED)
      def get_nav_menu(key = 'main_menu', class_name = 'navigation')
        draw_menu({ menu_slug: key, container_class: class_name })
      end

      # draw menu as html
      # default configurations is for bootstrap support
      def draw_menu(args = {})
        args_def = {
          menu_slug: 'main_menu', # slug for the menu
          container: 'ul', # type of container for the menu
          container_id: '', # container id for the menu
          container_class: 'nav navbar-nav nav-menu', # container class for the menu
          item_container: 'li', # type of container for the items
          item_current: 'current-menu-item', # class for current menu item
          item_class: 'menu-item', # class for all menu items
          item_class_parent: 'dropdown', # class for all menu items that contain sub items
          sub_container: 'ul', # type of container for sub items
          sub_class: 'dropdown-menu', # class for sub container
          callback_item: ->(args) {}, # rubocop:disable Lint/ShadowingOuterLocalVariable
          # callback executed for each item (args = { menu_item, link, level, settings, has_children, link_attrs = "", index}).
          #     menu_item: (Object) Menu object
          #     link: (Hash) link data: {link: '', name: ''}
          #     level: (Integer) current level
          #     has_children: (boolean) if this item contain sub menus
          #     settings: (Hash) menu settings
          #     index: (Integer) Index Position of this menu
          #     link_attrs: (String) Here you can add your custom attrs for current link, sample: id='my_id' data-title='#{args[:link][:name]}'
          #     item_container_attrs: (String) Here you can add your custom attrs for link container.
          # In settings, you can change the values for this item, like after, before, ..:
          # sample: lambda{|args| args[:settings][:after] = "<span class='caret'></span>" if args[:has_children]; args[:link_attrs] = "id='#{menu_item.id}'";  }
          # sample: lambda{|args| args[:settings][:before] = "<i class='fa fa-home'></i>" if args[:level] == 0 && args[:index] == 0;  }
          before: '', # content before link text
          after: '', # content after link text
          link_current: 'current-link', # class for current menu link
          link_before: '', # content before link
          link_after: '', # content after link
          link_class: 'menu_link', # class for all menu links
          link_class_parent: 'dropdown-toggle', # class for all menu links that contain sub items
          levels: -1, # max of levels to recover, -1 => return all levels
          container_prepend: '', # content prepend for menu container
          container_append: '' # content append for menu container
        }

        args = args_def.merge!(args)
        nav_menu = current_site.nav_menus.find_by_slug(args[:menu_slug])
        nav_menu ||= current_site.nav_menus.first
        html = "<#{args[:container]} class='#{args[:container_class]}' "\
          "id='#{args[:container_id]}'>#{args[:container_prepend]}{__}#{args[:container_append]}</#{args[:container]}>"
        if nav_menu.present?
          html.sub('{__}', cama_menu_draw_items(args, nav_menu.children.reorder(:term_order)))
        else
          html.sub('{__}', '')
        end
      end

      # draw menu items
      def cama_menu_draw_items(args, nav_menu, level = 0)
        html = ''
        parent_current = false
        index = 0
        nav_menu.eager_load(:metas).each do |nav_menu_item|
          _args = args.dup
          data_nav_item = cama_parse_menu_item(nav_menu_item)
          next if data_nav_item == false

          _is_current = data_nav_item[:current] || site_current_path == data_nav_item[:link] ||
                        site_current_path == data_nav_item[:link].sub('.html', '')
          has_children = nav_menu_item.have_children? && (args[:levels] == -1 ||
                         (args[:levels] != -1 && level <= args[:levels]))
          r = {
            menu_item: nav_menu_item.decorate,
            link: data_nav_item,
            level: level,
            settings: _args,
            has_children: has_children,
            link_attrs: '',
            item_container_attrs: '',
            index: index
          }
          args[:callback_item].call(r)
          _args = r[:settings]

          if has_children
            html_children, current_children = cama_menu_draw_items(args, nav_menu_item.children.reorder(:term_order),
                                                                   level + 1)
          else
            html_children = ''
            current_children = false
          end
          parent_current = true if _is_current || current_children

          html += "<#{_args[:item_container]} #{r[:item_container_attrs]} class='#{_args[:item_class]} #{if has_children
                                                                                                           _args[:item_class_parent]
                                                                                                         end} #{if _is_current
                                                                                                                  _args[:item_current].to_s
                                                                                                                end} #{if current_children
                                                                                                                         'current-menu-ancestor'
                                                                                                                       end}'>#{_args[:link_before]}
                <a #{r[:link_attrs]} #{if nav_menu_item.target.present?
                                         " target='#{nav_menu_item.target}'"
                                       end} href='#{data_nav_item[:link]}' class='#{if _is_current
                                                                                      args[:link_current]
                                                                                    end} #{if has_children
                                                                                             _args[:link_class_parent]
                                                                                           end} #{_args[:link_class]}' #{if has_children
                                                                                                                           "data-toggle='dropdown'"
                                                                                                                         end} >#{_args[:before]}#{data_nav_item[:name]}#{_args[:after]}</a> #{_args[:link_after]}
                #{html_children}
              </#{_args[:item_container]}>"
          index += 1
        end

        if level == 0
          html
        else
          html = "<#{args[:sub_container]} class='#{args[:sub_class]} #{if parent_current
                                                                          "parent-#{args[:item_current]}"
                                                                        end} level-#{level}'>#{html}</#{args[:sub_container]}>"
          [html, parent_current]
        end
      end

      # filter and parse all menu items visible for current user and adding the flag for current_parent or current_item
      # max_levels: max levels to iterate
      # return a multidimensional array with all items until level 'max_levels'
      # internal_level: ignore (managed by internal recursion)
      def cama_menu_parse_items(items, max_levels = -1, internal_level = 0)
        res = []
        is_current_parent = false
        levels = [0]
        items.reorder(:term_order).each_with_index do |nav_menu_item, index|
          data_nav_item = cama_parse_menu_item(nav_menu_item)
          next if data_nav_item == false

          _is_current = data_nav_item[:current] || site_current_path == data_nav_item[:link] || site_current_path == data_nav_item[:link].sub(
            '.html', ''
          )
          has_children = nav_menu_item.have_children?
          has_children = false if max_levels > 0 && max_levels == internal_level
          data_nav_item[:label] = data_nav_item[:name]
          data_nav_item[:url] = data_nav_item[:link]
          r = {
            menu_item: nav_menu_item.decorate,
            level: internal_level,
            has_children: has_children,
            index: index,
            current_item: _is_current,
            current_parent: false,
            levels: 0
          }.merge!(data_nav_item.except(:current, :name, :link))

          if has_children
            r[:children], _is_current_parent, r[:levels] = cama_menu_parse_items(nav_menu_item.children, max_levels,
                                                                                 internal_level + 1)
            if _is_current_parent
              is_current_parent = true
              r[:current_parent] = true
            end
            r[:levels] = r[:levels] + 1
          end
          is_current_parent = true if r[:current_item]
          levels << r[:levels]
          res << r
        end

        if internal_level == 0
          res
        else
          [res, is_current_parent, levels.max]
        end
      end

      # ******************* BREADCRUMBS *******************
      # draw the breadcrumb as html list
      def breadcrumb_draw
        res = []
        @_front_breadcrumb.each_with_index do |item, index|
          res << if @_front_breadcrumb.size == (index + 1) # last menu
                   "<li class='active'>#{item[0]}</li>"
                 else
                   "<li><a href='#{item[1]}'>#{item[0]}</a></li>"
                 end
        end
        res.join('')
      end

      # add breadcrumb item at the end
      # label => label of the link
      # url: url for the link
      def breadcrumb_add(label, url = '', prepend = false)
        @_front_breadcrumb ||= []
        if prepend
          @_front_breadcrumb.unshift([label, url])
        else
          @_front_breadcrumb << [label, url]
        end
      end

      def cama_parse_menu_item(nav_menu_item, is_from_backend = false)
        type_menu = nav_menu_item.kind
        result = false
        begin
          case type_menu
          when 'post'
            post = CamaleonCms::Post.find(nav_menu_item.url).decorate
            if is_from_backend || post.can_visit?
              result = { link: post.the_url(as_path: true), name: post.the_title, type_menu: type_menu,
                         url_edit: post.the_edit_url }
              result[:current] = @cama_visited_post.present? && @cama_visited_post.id == post.id unless is_from_backend
            end
          when 'category'
            category = CamaleonCms::Category.find(nav_menu_item.url).decorate
            result = { link: category.the_url(as_path: true), name: category.the_title,
                       url_edit: category.the_edit_url }
            unless is_from_backend
              result[:current] =
                @cama_visited_category.present? && @cama_visited_category.id == category.id
            end
          when 'post_tag'
            post_tag = CamaleonCms::PostTag.find(nav_menu_item.url).decorate
            result = { link: post_tag.the_url(as_path: true), name: post_tag.the_title,
                       url_edit: post_tag.the_edit_url }
            result[:current] = @cama_visited_tag.present? && @cama_visited_tag.id == post_tag.id unless is_from_backend
          when 'post_type'
            post_type = CamaleonCms::PostType.find(nav_menu_item.url).decorate
            result = { link: post_type.the_url(as_path: true), name: post_type.the_title,
                       url_edit: post_type.the_edit_url }
            unless is_from_backend
              result[:current] =
                @cama_visited_post_type.present? && @cama_visited_post_type.id == post_type.id
            end
          when 'external'
            result = { link: nav_menu_item.url.to_s.translate, name: nav_menu_item.name.to_s.translate, current: false }
            # permit to customize or mark as current menu
            # _args: (HASH) {menu_item: Model Menu Item, parsed_menu: Parsed Menu }
            #   Sample parsed_menu: {link: "url of the link", name: "Text of the menu", current: Boolean (true => is current menu, false => not current menu item)}
            unless is_from_backend
              result[:link] = cama_root_path if result[:link] == 'root_url'
              result[:link] = site_current_path if site_current_path == "#{current_site.the_path}#{result[:link]}"
              result[:current] = result[:link] == site_current_url || result[:link] == site_current_path
              _args = { menu_item: nav_menu_item, parsed_menu: result }
              hooks_run('on_external_menu', _args)
              result = _args[:parsed_menu]
            end
          else
            # permit to build custom menu items registered as Custom Menu by hook "nav_menu_custom"
            # sample: def my_parse_custom_menu_item_listener(args);
            #   if args[:menu_item].kind == 'MyModelClass'
            #     my_model = MyModelClass.find(args[:menu_item].url)
            #     res = {name: my_model.name, url_edit: my_model_edit_url(id: my_model.id), link: my_model_public_url(id: my_model.id)}
            #     res[:current] = site_current_path == my_model_public_url(id: my_model.id) unless args[:is_from_backend]
            #     args[:parsed_menu] = res
            #   end
            # end
            hook_args = { menu_item: nav_menu_item, parsed_menu: false, is_from_backend: is_from_backend }
            hooks_run('parse_custom_menu_item', hook_args)
            result = hook_args[:parsed_menu]
          end
        rescue StandardError => e
          Rails.logger.error "Camaleon CMS - Menu Item Not Found => Skipped menu for: #{e.message} (#{nav_menu_item.inspect})".cama_log_style(:red)
        end

        # permit to customize data, like: current, title, ... of parsed menu item or skip menu item by assigning false into :parsed_menu
        if is_from_backend
          result
        else
          _args = { menu_item: nav_menu_item, parsed_menu: result }
          hooks_run('on_render_front_menu_item', _args)
          _args[:parsed_menu]
        end
      end
    end
  end
end
