module ReportController::Menus
  extend ActiveSupport::Concern

  def get_tree_data
    # build tree for selected role in left div of the right cell
    group_id = x_node(:roles_tree).split('-').last
    session[:role_choice] = MiqGroup.find(group_id).description if group_id.present?
    session[:node_selected] = "" if params[:action] != "menu_field_changed"
    @sb[:menu_default] = false
    if @changed || @menu_lastaction == "discard_changes"
      @rpt_menu = copy_array(@edit[:new])
    elsif @menu_lastaction == "default"
    else
      populate_reports_menu(true)
      tree = build_menu_roles_tree
      @rpt_menu = tree.rpt_menu
    end
    @menu_lastaction = "menu_editor" if @menu_lastaction != "commit" && @menu_lastaction != "discard_changes" && params[:action] == "get_tree_data"
    menu_editor
  end

  def menu_editor
    assert_privileges("miq_report_menu_editor")

    menu_set_form_vars if %w[explorer tree_select x_history].include?(params[:action])
    @in_a_form = true
    @menu_roles_tree = if @menu_lastaction != "menu_editor"
                         build_menu_roles_tree(@edit[:new])
                       else
                         # changing rpt_menu if changes have been commited to show updated tree with changes
                         build_menu_roles_tree
                       end
    @sb[:role_list_flag] = true if params[:id]

    if params[:node_id]
      session[:node_selected] = params[:node_id]
    elsif session[:node_selected].blank?
      session[:node_selected] = "xx-b__Report Menus for #{session[:role_choice]}"
    end
    @sb[:node_clicked] = (params[:node_clicked] == "1")

    @breadcrumbs = []
    drop_breadcrumb(:name => "Edit Report menus for '#{session[:role_choice]}'")
    if session[:node_selected].index(':').nil? || params[:button] == "reset"
      edit_folder
      replace_right_cell if params[:node_clicked]
    else
      edit_reports
    end
  end

  def menu_folder_message_display
    assert_privileges("miq_report_menu_editor")

    text = if params[:typ] == "delete"
             _("Can not delete folder, one or more reports in the selected folder are not owned by your group")
           else
             _("Double Click on 'New Folder' to edit")
           end
    render_flash(text, :warning)
  end

  # AJAX driven routine to check for changes in ANY field on the user form
  def menu_field_changed
    assert_privileges("miq_report_menu_editor")

    return unless load_edit("menu_edit__#{session[:role_choice] ? session[:role_choice] : "new"}", "replace_cell__explorer")
    menu_get_form_vars
    @in_a_form = true
    @edit[:tree_arr]  = []
    @edit[:tree_hash] = {}
    @edit[:temp_new]  = []

    if params[:tree]
      @menu_lastaction = "commit"
      @sb[:tree_err] = false

      rows = JSON.parse(params[:tree], :symbolize_names => true)
      rows.each do |row|
        if row[:text].nil?
          @sb[:tree_err] = true
          add_flash(_("Folder name is required"), :error)
        elsif @edit[:tree_arr].include?(row[:text])
          @sb[:tree_err] = true
          add_flash(_("Folder name '%{value}' is already in use") % {:value => row[:text]}, :error)
        else
          @edit[:tree_arr].push(row[:text])
          @edit[:tree_hash][row[:id].split('_')[1]] = row[:text]
        end
      end

      @edit[:temp_new] = []
      if @edit[:tree_arr].blank?
        # if all subfolders were deleted
        @edit[:temp_new].push(@edit[:temp_arr][0], [])
      else
        @edit[:tree_arr].each do |el|
          old_folder = @edit[:tree_hash].key(el)
          @edit[:temp_arr].each do |arr|
            arr = Array.wrap(arr)
            temp = []
            if session[:node_selected].split('__')[1] == "Report Menus for #{session[:role_choice]}"
              if arr[0] == old_folder
                temp.push(el)
                temp.push(arr[1])
              elsif old_folder.nil?
                temp.push(el) unless temp.include?(el)
                temp.push([]) unless temp.include?([])
              end
              @edit[:temp_new].push(temp) if !temp.empty? && !@edit[:temp_new].include?(temp)
            else
              @edit[:temp_new][0] = @edit[:temp_arr][0]
              arr.each do |a|
                if a[0] == old_folder
                  temp.push(el)
                  temp.push(a[1])
                elsif old_folder.nil?
                  temp.push(el) unless temp.include?(el)
                  temp.push([]) unless temp.include?([])
                end
                @edit[:temp_new][1] = [] if @edit[:temp_new][1].nil?
                @edit[:temp_new][1].push(temp) if !temp.empty? && !@edit[:temp_new][1].include?(temp)
              end
            end
          end
        end
      end

      if !@edit[:idx].nil?
        @edit[:new][@edit[:idx]] = copy_array(@edit[:temp_new])
      else
        @edit[:new] = copy_array(@edit[:temp_new])
      end
    end

    if params[:pressed] == "commit" && (@edit[:selected_reports] || @edit[:available_reports])
      @menu_lastaction = "commit"
      val              = session[:node_selected].split('__')[1]
      old_val          = val.split(':')
      idx              = @edit[:new].index(@edit[:temp_arr])            # index of temp_array that being worked on, in set_data
      idx2             = @edit[:temp_arr].index(old_val[0])             # index of parent folder in temp_array
      unless @edit[:user_typ]
        # remove * at the starting of report names, before saving them for user
        @edit[:selected_reports].each_with_index do |rep, i|
          @edit[:selected_reports][i] = rep[1..rep.length - 1].strip! if rep.starts_with?('* ')
        end
      end
      @edit[:temp_arr].each do |arr|
        next unless arr.class == Array
        arr.each do |a|
          next if a[0] != old_val[1]
          @edit[:temp] = a
          idx4 = @edit[:temp_arr][idx2 + 1].index(@edit[:temp]) # index of temp in temp_array
          if a[1].nil?
            @edit[:temp].push(@edit[:selected_reports])
          else
            @edit[:reports] = a[1]
            idx5 = @edit[:temp].index(@edit[:reports]) # index of reports array in temp
            @edit[:temp][idx5] = @edit[:selected_reports]
          end
          @edit[:temp_arr][idx2 + 1][idx4] = @edit[:temp].dup
          @edit[:new][idx] = @edit[:temp_arr].dup
        end
      end
    end

    @edit[:commited_new] = copy_array(@edit[:new])
    id                   = session[:node_selected].split('__')
    @selected            = id[1].split(':')
    @changed             = (@edit[:new] != @edit[:current]) if @menu_lastaction == "commit"
    get_tree_data if (!params[:selected_reports] && !params[:available_reports]) || @menu_lastaction == "commit"
    # load if something in report selection changed or if something was commited
    if !params[:tree]
      replace_right_cell(:menu_edit_action => "menu_commit_reports")
    else
      replace_right_cell(:menu_edit_action => "menu_commit_folders")
    end
  end

  def discard_changes
    assert_privileges("miq_report_menu_editor")

    @menu_lastaction = "discard_changes"
    id               = session[:node_selected].split('__')
    @selected        = id[1].split(':')
    get_tree_data
    @edit[:new]      = copy_array(@edit[:commited_new]) if @edit[:commited_new]
    @changed         = (@edit[:new] != @edit[:current])
    if params[:pressed] == "discard_reports"
      replace_right_cell(:menu_edit_action => "menu_discard_reports")
    else
      replace_right_cell(:menu_edit_action => "menu_discard_folders")
    end
  end

  def menu_update
    assert_privileges("miq_report_menu_editor")

    menu_get_form_vars
    # @changed = (@edit[:new] != @edit[:current])
    if params[:button] == "cancel"
      add_flash(_("Edit of Report Menu for role \"%{role}\" was cancelled by the user") %
                  {:role => session[:role_choice]})
      session[:node_selected]   = ""
      session[:role_choice]     = nil
      @new_menu_node            = "roleroot"
      @menu_roles_tree = nil
      @edit = session[:edit]    = nil
      @changed                  = session[:changed] = false
      self.x_node = "root"
      replace_right_cell
    elsif params[:button] == "reset"
      @changed                   = session[:changed] = false
      @edit[:new]                = copy_array(@edit[:current])
      @menu_lastaction           = "reset"
      add_flash(_("All changes have been reset"), :warning)
      get_tree_data
      replace_right_cell(:menu_edit_action => "menu_reset")
    elsif params[:button] == "default"
      @sb[:rpt_menu]   = default_reports_menu
      @menu_roles_tree = build_menu_roles_tree
      @edit[:new]      = copy_array(@sb[:rpt_menu])
      @menu_lastaction = "default"
      add_flash(_("Report Menu set to default"), :warning)
      get_tree_data
      # set menu_default flag to true
      @sb[:menu_default] = true
      replace_right_cell(:menu_edit_action => "menu_default")
    elsif params[:button] == "save"
      @menu_lastaction = "save"
      role = session[:role_choice] unless session[:role_choice].nil?
      rec = MiqGroup.find_by(:description => role)
      rec[:settings] ||= {}
      if @sb[:menu_default]
        # delete report_menus from settings if menu set to default
        rec[:settings].delete("report_menus")
      else
        rec[:settings]["report_menus"] ||= {}
        rec[:settings]["report_menus"] = copy_array(@edit[:new])
      end

      if rec.save
        session[:edit] = nil # clean out the saved info
        add_flash(_("Report Menu for role \"%{role}\" was saved") % {:role => session[:role_choice]})
        get_tree_data
        session[:node_selected] = ""
        session[:role_choice] = nil
        @new_menu_node = "roleroot"
        @menu_roles_tree = nil
        @changed = session[:changed] = false
        @edit = session[:edit] = nil
        self.x_node = "root"
        @in_a_form = false
        replace_right_cell(:replace_trees => [:reports])
      else
        rec.errors.each do |error|
          add_flash("#{error.attribute.to_s.capitalize} #{error.message}", :error)
        end
        @in_a_form = true
        session[:changed] = @changed
        @changed = true
      end
    end
  end

  private

  def edit_reports
    @edit[:selected_reports] = []
    current_group_id = current_user.current_group.try(:id).to_i
    id = session[:node_selected].split('__')
    @selected = id[1].split(':')
    all = MiqReport.all.sort_by { |r| [r.rpt_type, r.filename.to_s, r.name] }
    @all_reports = []
    all.each do |r|
      next if r.template_type != "report" && r.template_type.present?
      @all_reports.push(r.name)
    end

    @selected_reports = []
    @assigned_reports = []

    # calculating selected reports for selected folder
    @edit[:new].each do |arr|
      next if arr[0] != @selected[0]
      arr.each do |a|
        next unless a.class == Array
        a.each do |r|
          next if r[0] != @selected[1]
          r.each do |rep|
            next unless rep.class == Array
            rep.each do |report_name|
              report = MiqReport.find_by(:name => report_name.strip)
              r_name = @edit[:user_typ] || report.miq_group_id.to_i == current_group_id ? report_name : "* #{report_name}"
              @selected_reports.push(r_name)
            end
          end
        end
      end
    end

    # Calculating reports that are assigned to any of the folders
    @edit[:new].each do |arr|
      next unless arr.class == Array
      arr.each do |a|
        next unless a.class == Array
        a.each do |r|
          r.each do |rep|
            next unless rep.class == Array
            rep.each do |report_name|
              @assigned_reports.push(report_name)
            end
          end
        end
      end
    end

    @available_reports = @all_reports.reject { |r| @assigned_reports.include?(r) }

    @edit[:old_selected_reports] = @selected_reports.dup
    @edit[:old_available_reports] = @available_reports.dup
    @edit[:selected_reports] = @selected_reports.dup
    @edit[:available_reports] = @available_reports.dup

    # reload grid if folder node was clicked for report management
    # if params[:action] == "menu_editor" && @menu_lastaction != "commit" && @menu_lastaction != "discard_changes"
    if params[:pressed] != "commit" && params[:pressed] != "discard_reports" && params[:button] != "default" && params[:action] != "menu_field_changed"
      replace_right_cell(:menu_edit_action => "menu_edit_reports")
    end
  end

  # Convenience for TreeBuilderMenuRoles
  #
  def build_menu_roles_tree(rpt_menu = nil)
    TreeBuilderMenuRoles.new(
      :menu_roles_tree, # name
      @sb,              # sandbox
      true,             # build
      :role_choice => session[:role_choice],
      :rpt_menu    => rpt_menu
    )
  end

  def move_menu_cols_left
    if params[:available_reports].blank? || params[:available_reports][0] == ""
      add_flash(_("No fields were selected to move left"), :error)
    else
      @edit[:available_reports].each do |af|                  # Go thru all available columns
        if params[:available_reports].include?(af)            # See if this column was selected to move
          @edit[:selected_reports].push(af)                   # Add it to the new fields list
        end
      end
      @edit[:available_reports].delete_if { |af| params[:available_reports].include?(af) } # Remove selected fields
      @refresh_div = "menu_div2"
      @refresh_partial = "/report/menu_form2"
    end
  end

  def move_menu_cols_right
    if params[:selected_reports].blank? || params[:selected_reports][0] == ""
      add_flash(_("No fields were selected to move right"), :error)
      return
    end

    user = current_user
    flg = 0
    @edit[:selected_reports].each do |nf| # Go thru all new fields
      next unless params[:selected_reports].include?(nf) # See if this col was selected to move

      field = nf.split('* ')
      r = MiqReport.find_by(:name => field.length == 1 ? field[0].strip : field[1].strip)
      if !user.report_admin_user? && r.miq_group_id.to_i != user.current_group.id.to_i && flg.zero?
        flg = 1
        # only show this flash message once for all reports
        add_flash(_("One or more selected reports are not owned by your group, they cannot be moved"), :warning)
      end
      if user.report_admin_user? || r.miq_group_id.to_i == user.current_group.id.to_i
        @edit[:available_reports].push(nf) if @edit[:user_typ] || r.miq_group_id.to_i == user.current_group.id.to_i # Add to the available fields list
        @edit[:selected_reports].delete(nf)
      end
    end
    @edit[:available_reports].sort! # Sort the available fields array
    @refresh_div = "menu_div2"
    @refresh_partial = "/report/menu_form2"
  end

  def move_menu_cols_up
    if params[:selected_reports].blank? || params[:selected_reports][0] == ""
      add_flash(_("No fields were selected to move up"), :error)
      return
    end
    consecutive, first_idx, last_idx = selected_menu_consecutive?
    if !consecutive
      add_flash(_("Select only one or consecutive fields to move up"), :error)
    else
      if first_idx.positive?
        @edit[:selected_reports][first_idx..last_idx].reverse_each do |field|
          pulled = @edit[:selected_reports].delete(field)
          @edit[:selected_reports].insert(first_idx - 1, pulled)
        end
      end
      @refresh_div = "menu_div2"
      @refresh_partial = "/report/menu_form2"
    end
    @selected_reps = params[:selected_reports]
  end

  def move_menu_cols_down
    if params[:selected_reports].blank? || params[:selected_reports][0] == ""
      add_flash(_("No fields were selected to move down"), :error)
      return
    end
    consecutive, first_idx, last_idx = selected_menu_consecutive?
    if !consecutive
      add_flash(_("Select only one or consecutive fields to move down"), :error)
    else
      if last_idx < @edit[:selected_reports].length - 1
        insert_idx = last_idx + 1 # Insert before the element after the last one
        insert_idx = -1 if last_idx == @edit[:selected_reports].length - 2 # Insert at end if 1 away from end
        @edit[:selected_reports][first_idx..last_idx].each do |field|
          pulled = @edit[:selected_reports].delete(field)
          @edit[:selected_reports].insert(insert_idx, pulled)
        end
      end
      @refresh_div = "menu_div2"
      @refresh_partial = "/report/menu_form2"
    end
    @selected_reps = params[:selected_reports]
  end

  def move_menu_cols_top
    if params[:selected_reports].blank? || params[:selected_reports][0] == ""
      add_flash(_("No fields were selected to move up") % "", :error)
      return
    end
    consecutive, first_idx, last_idx = selected_menu_consecutive?
    if !consecutive
      add_flash(_("Select only one or consecutive fields to move up"), :error)
    else
      if first_idx.positive?
        @edit[:selected_reports][first_idx..last_idx].reverse_each do |field|
          pulled = @edit[:selected_reports].delete(field)
          @edit[:selected_reports].unshift(pulled)
        end
      end
      @refresh_div = "menu_div2"
      @refresh_partial = "/report/menu_form2"
    end
    @selected_reps = params[:selected_reports]
  end

  def move_menu_cols_bottom
    if params[:selected_reports].blank? || params[:selected_reports][0] == ""
      add_flash(_("No fields were selected to move down"), :error)
      return
    end
    consecutive, first_idx, last_idx = selected_menu_consecutive?
    if !consecutive
      add_flash(_("Select only one or consecutive fields to move down"), :error)
    else
      if last_idx < @edit[:selected_reports].length - 1
        @edit[:selected_reports][first_idx..last_idx].each do |field|
          pulled = @edit[:selected_reports].delete(field)
          @edit[:selected_reports].push(pulled)
        end
      end
      @refresh_div = "menu_div2"
      @refresh_partial = "/report/menu_form2"
    end
    @selected_reps = params[:selected_reports]
  end

  def selected_menu_consecutive?
    first_idx = last_idx = 0
    @edit[:selected_reports].each_with_index do |nf, idx|
      first_idx = idx if nf == params[:selected_reports].first
      if nf == params[:selected_reports].last
        last_idx = idx
        break
      end
    end
    if last_idx - first_idx + 1 > params[:selected_reports].length
      return [false, first_idx, last_idx]
    else
      return [true, first_idx, last_idx]
    end
  end

  def menu_get_form_vars
    copy_params_if_present(@edit[:form_vars], params, %i[selected_reports available_reports])
    @edit[:temp_arr] = []
    id = session[:node_selected].split('__')
    selected = id[1].split(':')
    @edit[:new].each do |a1, a2|
      if a1 == selected[0]
        @edit[:temp_arr].push(a1)
        @edit[:temp_arr].push(a2)
      elsif selected[0] == "Report Menus for #{session[:role_choice]}"
        @edit[:temp_arr] = copy_array(@edit[:new])
      end
      @edit[:idx] = @edit[:new].index(@edit[:temp_arr])
    end

    case params[:button]
    when 'right'  then move_menu_cols_right
    when 'left'   then move_menu_cols_left
    when 'up'     then move_menu_cols_up
    when 'down'   then move_menu_cols_down
    when 'top'    then move_menu_cols_top
    when 'bottom' then move_menu_cols_bottom
    end
  end

  def menu_set_form_vars
    # session[:changed] = @changed = false
    @edit = {}
    @edit[:new] = []
    @edit[:key] = "menu_edit__#{session[:role_choice] ? session[:role_choice] : "new"}"

    @edit[:temp_arr] = []
    @edit[:form_vars] = {}
    @edit[:current] = []
    @edit[:new] = @rpt_menu unless @rpt_menu.nil?
    user = current_user
    @edit[:user_typ] = user.report_admin_user?
    @edit[:user_group] = user.current_group.id
    @edit[:group_reports] = []
    menu_set_reports_for_group
    @edit[:current] = copy_hash(@edit[:new]) unless @edit[:new].nil?
    session[:edit] = @edit
  end

  def menu_set_reports_for_group
    @edit[:new].each do |r|
      r.each_slice(2) do |menu, section|
        title = "#{menu}/"
        next if section.nil? || section.class == String
        section.each do |s|
          next unless s.class == Array
          s.each do |rec|
            if rec.class == String
              @sub_title = title + "#{rec}/"
            else
              rec.each do |report_name|
                rpt = MiqReport.find_by(:name => report_name.strip)
                @edit[:group_reports].push(@sub_title + report_name) if rpt&.miq_group && rpt.miq_group.id.to_i == @edit[:user_group].to_i
              end
            end
          end
        end
      end
    end
  end

  # Render the view data for the grid view
  def menu_folders(view)
    view.compact.map do |row|
      row_id = nil

      if @edit[:user_typ]
        # if user is admin/super admin, no need to add special characters in id
        row_id = "i_#{row}"
      elsif @edit[:group_reports].empty?
        # if group does not own any reports then add special characters in id, they cannot delete any folders.
        row_id = "__|i_#{row}"
      else
        prefix = "|-|"

        # FIXME: this is jast .last really, on purpose?
        @edit[:group_reports].each do |rep|
          # need to check if report is not owned by user add special character to the row id so it can be tracked in JS and folder cannnot be deleted in menu editor
          nodes = rep.split('/')
          val = session[:node_selected].split('__')[0]
          row_id = if val == "b"
                     # if top node
                     if nodes[0] == row
                       "i_#{row}" # if report belongs to group
                     else
                       "#{prefix}i_#{row}" # if report is owned by other group
                     end
                   elsif nodes[1] == row # if second level folder node
                     "i_#{row}" # if report belongs to group
                   else
                     "#{prefix}i_#{row}" # if report is owned by other group
                   end
        end
      end

      {:id => row_id, :text => row}
    end
  end

  def edit_folder
    if params[:button] == "reset" || params[:button] == "default" # resetting node in case reset button is pressed
      session[:node_selected] = "xx-b__Report Menus for #{session[:role_choice]}"
    end

    @selected = session[:node_selected].split('__')
    @folders = []
    @edit[:folders] = []

    # calculating selected reports for selected folder
    if session[:node_selected] == "xx-b__Report Menus for #{session[:role_choice]}"
      @edit[:new].each do |arr|
        if arr[0] != reports_group_title
          @folders.push(arr[0])
        end
      end
    else
      @edit[:new].each do |arr|
        next if arr[0] != @selected[1]
        arr.each do |a|
          next unless a.class == Array
          a.each do |s|
            @folders.push(s[0])
          end
        end
      end
    end
    @edit[:folders] = @folders.dup
    @grid_folders = menu_folders(@edit[:folders])
  end

  def menu_get_all
    roles, title = get_group_roles
    @sb[:menu] = {}
    roles.sort_by { |a| a.name.downcase }.each do |r|
      @sb[:menu][r.id] = r.name
    end
    @right_cell_text = title
    @right_cell_div = "role_list"
    @menu_roles_tree = nil
  end

  def get_menu(_nodeid)
    # build menu for selected role
    get_tree_data
    @right_cell_div  = "role_list"
    @right_cell_text = _("Editing EVM Group \"%{name}\"") % {:name => session[:role_choice]}
  end

  def get_group_roles
    if super_admin_user?
      roles = MiqGroup.non_tenant_groups
      title  = _("All EVM Groups")
    else
      title  = _("My EVM Group")
      roles = [current_user.current_group]
    end
    return roles, title
  end
end
