module ReportController::Dashboards
  extend ActiveSupport::Concern
  include DashboardHelper

  def db_seq_edit
    assert_privileges("db_seq_edit")
    case params[:button]
    when "cancel"
      @edit = session[:edit] = nil # clean out the saved info
      add_flash(_("Edit of Dashboard Sequence was cancelled by the user"))
      replace_right_cell
    when "save"
      return unless load_edit("db_edit__seq", "replace_cell__explorer")
      err = false
      dashboard_order = []
      @edit[:new][:dashboard_order].each do |n|
        dashboard_order.push(MiqWidgetSet.where_unique_on(n).first.id)
      end
      g = MiqGroup.find(@sb[:nodes][2])
      g.settings ||= {}
      g.settings[:dashboard_order] ||= {}
      g.settings[:dashboard_order] = dashboard_order
      if g.save
        AuditEvent.success(build_saved_audit(g, @edit))
      else
        g.errors.each do |error|
          add_flash("#{error.attribute.to_s.capitalize} #{error.message}", :error)
        end
        err = true
      end
      if !err
        add_flash(_("Dashboard Sequence was saved"))
        @edit = session[:edit] = nil # clean out the saved info
        replace_right_cell(:replace_trees => [:db])
      else
        @in_a_form = true
        @changed = true
        javascript_flash
      end
    when "reset", nil # Reset or first time in
      db_seq_edit_screen
      if params[:button] == "reset"
        add_flash(_("All changes have been reset"), :warning)
      end
      session[:changed] = @changed = false
      replace_right_cell
    end
  end

  def db_new
    assert_privileges("db_new")
    db_edit
  end

  def db_copy
    assert_privileges("db_copy")

    case params[:button]
    when "cancel"
      db_copy_cancel
    when "save"
      dashboard = find_record_with_rbac(MiqWidgetSet, params[:dashboard_id])
      begin
        MiqWidgetSet.copy_dashboard(dashboard, params[:name], params[:description], params[:group_id])
        render :json => { :name => dashboard.name }, :status => :ok
      rescue => bang
        render :json => { :error => { :message => _("Error during 'Validate': %{message}") % {:message => bang.to_s} } }, :status => :bad_request
      end
    else
      checked_id = find_checked_items.first || params[:id]
      @record = find_record_with_rbac(MiqWidgetSet, checked_id)
      @tabactive = false
      @in_a_form = true
      @edit = {}
      @edit[:db_id] = @record.id
      session[:changed] = false
      @right_cell_text = _("Copy of \"%{dashboard}\" Dashboard") % {:dashboard => @record.name}
      replace_right_cell(:action => "copy_dashboard")
    end
  end

  def db_copy_cancel
    add_flash(_("Copy of Dashboard was cancelled by the user"))
    get_node_info
    @edit = session[:edit] = @sb[:action] = nil # clean out the saved info
    @dashboard = nil
    replace_right_cell
  end

  def dashboard_get
    assert_privileges("db_copy")

    if params[:name]
      dashboard = MiqWidgetSet.where(:name => params[:name]).to_a
      render :json => {:length => dashboard.length}
    else
      dashboard = MiqWidgetSet.select(:name, :description, :owner_id).find_by(:id => params[:id])
      render :json => {
        :name        => dashboard.name,
        :description => dashboard.description,
        :owner_id    => dashboard.owner_id.to_s
      }
    end
  end

  def dashboard_render
    assert_privileges("db_copy")

    get_node_info
    @edit = session[:edit] = @sb[:action] = nil # clean out the saved info
    @dashboard = nil
    add_flash(_("Copy of \"%{original_name}\" Dashboard: \"%{name}\" was succesfully saved into \"%{group}\" Group.") % {:original_name => params[:original_name], :name => params[:name], :group => params[:group]})
    replace_right_cell(:replace_trees => [:db])
  end

  def db_edit
    assert_privileges("db_edit")

    case params[:button]
    when "cancel"
      @dashboard = MiqWidgetSet.find_by(:id => session[:edit][:db_id]) if session[:edit] && session[:edit][:db_id]
      if !@dashboard || @dashboard.id.blank?
        add_flash(_("Add of new Dashboard was cancelled by the user"))
      else
        add_flash(_("Edit of Dashboard \"%{name}\" was cancelled by the user") % {:name => get_record_display_name(@dashboard)})
      end
      get_node_info
      @edit = session[:edit] = nil # clean out the saved info
      @dashboard = nil
      replace_right_cell
    when "add", "save"
      assert_privileges("db_#{@edit[:db_id] ? "edit" : "new"}")
      @dashboard = @edit[:db_id] ? MiqWidgetSet.find(@edit[:db_id]) : MiqWidgetSet.new # get the current record
      db_fields_validation
      db_set_record_vars
      if params[:button] == "add"
        g = MiqGroup.find(@sb[:nodes][2])
        @dashboard.owner = g
      end
      if @flash_array.nil? && @dashboard.save
        db_save_members
        AuditEvent.success(build_saved_audit(@dashboard, @edit))
        add_flash(_("Dashboard \"%{name}\" was saved") % {:name => get_record_display_name(@dashboard)})
        if params[:button] == "add"
          widgetset = MiqWidgetSet.where_unique_on(@edit[:new][:name]).first
          settings = g.settings ? g.settings : {}
          settings[:dashboard_order] = settings[:dashboard_order] ? settings[:dashboard_order] : []
          settings[:dashboard_order].push(widgetset.id) unless settings[:dashboard_order].include?(widgetset.id)
          g.save
        end
        params[:id] = @dashboard.id.to_s # reset id in params for show
        @edit = session[:edit] = nil # clean out the saved info
        replace_right_cell(:replace_trees => [:db])
      else
        @dashboard.errors.each do |error|
          add_flash("#{error.attribute.to_s.capitalize} #{error.message}", :error)
        end
        @changed = session[:changed] = (@edit[:new] != @edit[:current])
        javascript_flash
      end
    else
      add_flash(_("All changes have been reset"), :warning) if params[:button] == "reset"
      @dashboard = params[:id] && params[:id] != "new" ? find_record_with_rbac(MiqWidgetSet, params[:id]) : MiqWidgetSet.new
      db_set_form_vars
      session[:changed] = false
      @in_a_form = true
      replace_right_cell
    end
  end

  # Delete all selected or single displayed action(s)
  def db_delete
    assert_privileges("db_delete")
    db = MiqWidgetSet.find(params[:id]) # temp var to determine the parent node of deleted items
    process_elements(db, MiqWidgetSet, "destroy")
    g = MiqGroup.find(@sb[:nodes][2].split('_').first)
    # delete dashboard id from group settings and save
    db_order = g.settings && g.settings[:dashboard_order] ? g.settings[:dashboard_order] : nil
    db_order&.delete(db.id)
    g.save
    nodes = x_node.split('-')
    self.x_node = "#{nodes[0]}-#{nodes[1]}-#{nodes[2].split('_').first}"
    replace_right_cell(:replace_trees => [:db])
  end

  def db_form_field_changed
    assert_privileges(session.fetch_path(:edit, :db_id) ? "db_edit" : "db_new")

    return unless load_edit("db_edit__#{params[:id]}", "replace_cell__explorer")
    db_get_form_vars
    render :update do |page|
      page << javascript_prologue
      changed = (@edit[:new] != @edit[:current])
      if params[:widget]
        page.replace("form_div", :partial => "db_form")
        # url to be used in url in miqDropComplete method
        page << "ManageIQ.widget.dashboardUrl = 'report/db_widget_dd_done'"
        page << "miqInitDashboardCols();"
      end
      if %w[up down].include?(params[:button])
        page.replace("flash_msg_div", :partial => "layouts/flash_msg") unless @refresh_div && @refresh_div != "column_lists"
        page << "miqScrollTop();" if @flash_array.present?
        page.replace(@refresh_div, :partial => @refresh_partial, :locals => {:action => "db_seq_edit"}) if @refresh_div
      end
      page << javascript_for_miq_button_visibility(changed)
      page << "miqSparkle(false);"
    end
  end

  # A widget has been dropped
  def db_widget_dd_done
    assert_privileges("db_delete")

    set_edit_new_cols
    db_available_widgets_options
    render :update do |page|
      page << javascript_prologue
      changed = (@edit[:new] != @edit[:current])
      if params[:widget]
        page.replace("form_div", :partial => "db_form")
        # url to be used in url in miqDropComplete method
        page << "ManageIQ.widget.dashboardUrl = 'report/db_widget_dd_done'"
        page << "miqInitDashboardCols();"
      end
      page << javascript_for_miq_button_visibility(changed)
      page << "miqSparkle(false);"
    end
  end

  def db_widget_remove
    assert_privileges("db_delete")

    return unless load_edit("db_edit__#{params[:id]}", "replace_cell__explorer")
    @dashboard = @edit[:db_id] ? MiqWidgetSet.find(@edit[:db_id]) : MiqWidgetSet.new
    w = params[:widget].to_i
    @edit[:new][:col1].delete(w) if @edit[:new][:col1].include?(w)
    @edit[:new][:col2].delete(w) if @edit[:new][:col2].include?(w)
    db_available_widgets_options
    @in_a_form = true
    render :update do |page|
      page << javascript_prologue
      changed = (@edit[:new] != @edit[:current])
      if params[:widget]
        page.replace("form_div", :partial => "db_form")
        # url to be used in url in miqDropComplete method
        page << "ManageIQ.widget.dashboardUrl = 'report/db_widget_dd_done'"
        page << "miqInitDashboardCols();"
      end
      page << javascript_for_miq_button_visibility(changed)
      page << "miqSparkle(false);"
    end
  end

  private

  def set_edit_new_cols
    if params[:col1] || params[:col2]
      if params[:col1] && params[:col1] != [""]
        @edit[:new][:col1] = params[:col1].collect { |w| w.split("_").last.to_i }
        @edit[:new][:col2].delete_if { |w| @edit[:new][:col1].include?(w) }
      elsif params[:col2] && params[:col2] != [""]
        @edit[:new][:col2] = params[:col2].collect { |w| w.split("_").last.to_i }
        @edit[:new][:col1].delete_if { |w| @edit[:new][:col2].include?(w) }
      end
    end
  end

  def db_get_node_info
    model, rec_id, _ = TreeBuilder.extract_node_model_and_id(x_node)
    @sb[:nodes] = x_node.split('-')
    if @sb[:nodes].length == 1
      @default_ws = MiqWidgetSet.where_unique_on("default").where(:read_only => true).first
      @right_cell_text = _("All Dashboards")
      @right_cell_div  = "db_list"
      @db_nodes = {}
      @db_nodes_order = [@default_ws.name, "All Groups"]

      @db_nodes[@default_ws.name] = {}
      @db_nodes[@default_ws.name][:id] = "xx-#{@default_ws.id}"
      @db_nodes[@default_ws.name][:text] = "#{@default_ws.description} (#{@default_ws.name})"
      @db_nodes[@default_ws.name][:title] = "#{@default_ws.description} (#{@default_ws.name})"
      @db_nodes[@default_ws.name][:glyph] = "fa fa-dashboard"

      @db_nodes["All Groups"] = {}
      @db_nodes["All Groups"][:id] = "xx-g"
      @db_nodes["All Groups"][:glyph] = "pficon pficon-folder-close"
      @db_nodes["All Groups"][:title] = "All Groups"
      @db_nodes["All Groups"][:text] = "All Groups"
    elsif @sb[:nodes].length == 2 && @sb[:nodes].last == "g"
      # All groups node is selected
      @miq_groups = Rbac.filtered(MiqGroup.non_tenant_groups_in_my_region)
      @right_cell_div  = "db_list"
      @right_cell_text = _("All EVM Groups")
    elsif @sb[:nodes].length == 3 && @sb[:nodes][1] == "g_g"
      g = MiqGroup.find(@sb[:nodes].last)
      @right_cell_text = _("Dashboards for \"%{name}\"") % {:name => g.description}
      @right_cell_div  = "db_list"
      widgetsets = MiqWidgetSet.where(:owner_type => "MiqGroup", :owner_id => g.id)
      @widgetsets = []
      if g.settings && g.settings[:dashboard_order]
        g.settings[:dashboard_order].each do |ws_id|
          widgetsets.each do |ws|
            @widgetsets.push(ws) if ws_id == ws.id && !@widgetsets.include?(ws)
          end
        end
      else
        widgetsets.sort_by(&:name).each do |ws|
          @widgetsets.push(ws)
        end
      end
    elsif model == "MiqWidgetSet"
      # default dashboard nodes is selected or one under a specific group is selected
      # g = MiqGroup.find(@sb[:nodes][2])
      @record = @dashboard = MiqWidgetSet.find(rec_id)
      @right_cell_text = _("Dashboard \"%{name}\"") % {:name => "#{@dashboard.description} (#{@dashboard.name})"}
      @right_cell_div  = "db_list"
      @sb[:new] = {}
      @sb[:new][:name] = @dashboard.name
      @sb[:new][:description] = @dashboard.description
      @sb[:new][:locked] = @dashboard[:set_data] && @dashboard[:set_data][:locked] ? @dashboard[:set_data][:locked] : true
      @sb[:new][:reset_upon_login] = @dashboard[:set_data] && @dashboard[:set_data][:reset_upon_login] ? @dashboard[:set_data][:reset_upon_login] : true
      @sb[:new][:col1], @sb[:new][:col2] = column_widgets(@dashboard[:set_data])
    end
  end

  def db_get_form_vars
    @in_a_form = true
    @dashboard = @edit[:db_id] ? MiqWidgetSet.find(@edit[:db_id]) : MiqWidgetSet.new
    if %w[up down].include?(params[:button])
      db_move_cols_up if params[:button] == "up"
      db_move_cols_down if params[:button] == "down"
    else
      copy_params_if_present(@edit[:new], params, %i[name description])

      if params[:locked]
        @edit[:new][:locked] = params[:locked].to_i == 1
      end

      @edit[:new][:reset_upon_login] = params[:reset_upon_login].to_i == 1 if params[:reset_upon_login].present?

      if params[:widget] # Make sure we got a widget in
        w = params[:widget].to_i
        if @edit[:new][:col2].length < @edit[:new][:col1].length
          @edit[:new][:col2].insert(0, w)
        else
          @edit[:new][:col1].insert(0, w)
        end
        db_available_widgets_options
      end
      set_edit_new_cols
    end
  end

  def db_set_record_vars
    @dashboard.name = @edit[:new][:name]
    @dashboard.description = @edit[:new][:description]
    @dashboard.updated_on = Time.now.utc
    @dashboard.set_data = {} unless @dashboard.set_data
    @dashboard.set_data[:col1] = [] if !@dashboard.set_data[:col1] && !@edit[:new][:col1].empty?
    @dashboard.set_data[:col2] = [] if !@dashboard.set_data[:col2] && !@edit[:new][:col2].empty?
    @dashboard.set_data[:col1] = @edit[:new][:col1]
    @dashboard.set_data[:col2] = @edit[:new][:col2]
    @dashboard.set_data[:locked] = @edit[:new][:locked]
    @dashboard.set_data[:reset_upon_login] = @edit[:new][:reset_upon_login]
  end

  def db_save_members
    widget_ids = %i[col1 col2].collect { |key| @dashboard.set_data[key] }.flatten
    widgets = Array(MiqWidget.where(:id => widget_ids))

    @dashboard.replace_children(widgets)
    @dashboard.members.each { |w| w.create_initial_content_for_user(session[:userid]) } # Generate content if not there
  end

  def db_fields_validation
    if @edit[:new][:name] && @edit[:new][:name].index('|')
      add_flash(_("Name cannot contain \"|\""), :error)
      return
    end
    # no need to check this for default dashboard, it doesn't belong to any group
    if @sb[:nodes][1] == "g_g"
      ws = MiqWidgetSet.where(:owner_id => @sb[:nodes][2])
      # make sure description is unique within group
      ws.each do |w|
        if w.description == @edit[:new][:description] && (@edit[:db_id] && w.id != @edit[:db_id])
          add_flash(_("Tab Title must be unique for this group"), :error)
          break
        end
      end
    end
    if @edit[:new][:col1].empty? && @edit[:new][:col2].empty?
      add_flash(_("One widget must be selected"), :error)
      return
    end
  end

  def db_set_form_vars
    @timezone_abbr = get_timezone_abbr
    @edit = {}
    @edit[:db_id] = @dashboard.id
    @edit[:read_only] = !!@dashboard.read_only

    # Remember how this edit started
    @edit[:type] = params[:id] ? "db_edit" : "db_new"
    @edit[:key]  = params[:id] ? "db_edit__#{@dashboard.id}" : "db_edit__new"
    @edit[:new] = {}
    @edit[:new][:name] = @dashboard.name
    @edit[:new][:description] = @dashboard.description
    @edit[:new][:locked] = @dashboard[:set_data] && @dashboard[:set_data][:locked] ? @dashboard[:set_data][:locked] : false
    @edit[:new][:reset_upon_login] = @dashboard[:set_data] && @dashboard[:set_data][:reset_upon_login] ? @dashboard[:set_data][:reset_upon_login] : false
    @edit[:new][:col1], @edit[:new][:col2] = column_widgets(@dashboard[:set_data])
    db_available_widgets_options
    @edit[:current] = copy_hash(@edit[:new])
  end

  def db_seq_edit_screen
    assert_privileges("db_seq_edit")

    @in_a_form = true
    @edit = {}
    @edit[:new] = {}
    @edit[:current] = {}
    @edit[:new][:dashboard_order] = []
    g = MiqGroup.find(@sb[:nodes][2])
    @sb[:group_desc] = g.description # saving for cell header
    if g.settings && g.settings[:dashboard_order]
      dbs = g.settings[:dashboard_order]
      dbs.each do |db|
        ws = MiqWidgetSet.find(db)
        @edit[:new][:dashboard_order].push(ws.name)
      end
    else
      dbs = MiqWidgetSet.where(:owner_type => "MiqGroup", :owner_id => g.id)
      dbs.sort_by(&:name).each do |ws|
        @edit[:new][:dashboard_order].push(ws.name)
      end
    end

    @edit[:key] = "db_edit__seq"
    @edit[:current] = copy_hash(@edit[:new])
    session[:edit] = @edit
  end

  def db_available_widgets_options
    # Build the available widgets for the pulldown
    col_widgets = @edit[:new][:col1] +
                  @edit[:new][:col2]
    if @sb[:nodes].length == 2 && @sb[:nodes][1] != "g"
      # default dashboard selected
      @available_widgets = MiqWidget.available_for_all_roles.to_a
    else
      g = MiqGroup.find(@sb[:nodes][2].split('_').first)
      @available_widgets = MiqWidget.available_for_group(g).to_a
    end
    @available_widgets.sort_by! { |w| [w.content_type, w.title.downcase] }

    if @available_widgets.blank?
      @widgets_options = [["No Widgets available to add", "", {"data-icon" => "fa fa-arrow-right"}]]
    else
      @widgets_options = [["Add a Widget", "", {"data-icon" => "fa fa-arrow-right"}]]

      @available_widgets.each do |w|
        next if col_widgets.include?(w.id) || !w.enabled
        image = case w.content_type
                when "chart"
                  "fa fa-pie-chart"
                when "report"
                  "fa fa-file-text-o"
                when "menu"
                  "fa fa-share-square-o"
                end
        @widgets_options.push([w.title, w.id, {"data-icon" => image.to_s}])
      end
    end
    @widgets_options
  end

  def db_move_cols_up
    return unless load_edit("db_edit__seq", "replace_cell__explorer")
    if params[:seq_fields].blank? || params[:seq_fields][0] == ""
      add_flash(_("No fields were selected to move up"), :error)
      @refresh_div = "column_lists"
      @refresh_partial = "db_seq_form"
      return
    end
    consecutive, first_idx, last_idx = db_selected_consecutive?
    if !consecutive
      add_flash(_("Select only one or consecutive fields to move up"), :error)
    else
      if first_idx.positive?
        @edit[:new][:dashboard_order][first_idx..last_idx].reverse_each do |field|
          pulled = @edit[:new][:dashboard_order].delete(field)
          @edit[:new][:dashboard_order].insert(first_idx - 1, pulled)
        end
      end
      @refresh_div = "column_lists"
      @refresh_partial = "db_seq_form"
    end
    @selected = params[:seq_fields]
  end

  def db_move_cols_down
    return unless load_edit("db_edit__seq", "replace_cell__explorer")
    if params[:seq_fields].blank? || params[:seq_fields][0] == ""
      add_flash(_("No fields were selected to move down"), :error)
      @refresh_div = "column_lists"
      @refresh_partial = "db_seq_form"
      return
    end
    consecutive, first_idx, last_idx = db_selected_consecutive?
    if !consecutive
      add_flash(_("Select only one or consecutive fields to move down"), :error)
    else
      if last_idx < @edit[:new][:dashboard_order].length - 1
        insert_idx = last_idx + 1 # Insert before the element after the last one
        insert_idx = -1 if last_idx == @edit[:new][:dashboard_order].length - 2 # Insert at end if 1 away from end
        @edit[:new][:dashboard_order][first_idx..last_idx].each do |field|
          pulled = @edit[:new][:dashboard_order].delete(field)
          @edit[:new][:dashboard_order].insert(insert_idx, pulled)
        end
      end
      @refresh_div = "column_lists"
      @refresh_partial = "db_seq_form"
    end
    @selected = params[:seq_fields]
  end

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