class EasyAutoCompletesController < ApplicationController
  helper :issues
  include IssuesHelper

  before_action :set_self_only
  accept_api_auth :allowed_issue_statuses, :issue_priorities, :assignable_users,
    :allowed_target_projects_on_move, :allowed_issue_trackers, :time_entry_activities, :issue_author_values

  def query_entities
    if (options = params[:autocomplete_options]) && (entity = options.delete(:entity))
      begin
        entity_class = entity.constantize
        easy_query = entity.downcase.include?('easy') ? "#{entity}Query" : "Easy#{entity}Query"
        easy_query_class = easy_query.constantize
      rescue
      end
    end
    options ||= {}

    if entity_class.is_a?(Class) && easy_query_class.is_a?(Class)
      q = easy_query_class.new
      q.use_free_search = true
      q.free_search_tokens = params[:term]
      q.sort_criteria = q.sort_criteria_init
      q_options = {limit: EasySetting.value('easy_select_limit').to_i}.merge!(options)
      @entities = q.entities(q_options).to_a
      @name_column = :name if entity_class.method_defined?(:name)
    else
      @entities = []
    end
    @name_column ||= :to_s

    respond_to do |format|
      format.api { render template: 'easy_auto_completes/entities_with_id', formats: [:api], locals: {additional_select_options: options[:additional_select_options]}}
    end
  end

  def issues_with_parents
    @entities = get_issues_with_parents(params[:term], EasySetting.value('easy_select_limit').to_i)

    @name_column = :to_s
    respond_to do |format|
      format.api { render :template => 'easy_auto_completes/entities_with_id', :formats => [:api], locals: {additional_select_options: false} }
    end
  end

  def issues_with_children
    @entities = get_issues_with_children(params[:term], EasySetting.value('easy_select_limit').to_i)

    @name_column = :to_s
    respond_to do |format|
      format.api { render :template => 'easy_auto_completes/entities_with_id', :formats => [:api], locals: {additional_select_options: false} }
    end
  end

  def root_issues
    @entities = get_root_issues(params[:term], EasySetting.value('easy_select_limit').to_i)

    @name_column = :to_s
    respond_to do |format|
      format.api { render :template => 'easy_auto_completes/entities_with_id', :formats => [:api], locals: {additional_select_options: false} }
    end
  end

  def parent_issues
    project = Project.find(params[:project_id]) if params[:project_id].present?
    @issues = get_available_parent_issues(project, params[:term], EasySetting.value('easy_select_limit').to_i)

    respond_to do |format|
      format.api
    end
  rescue ActiveRecord::RecordNotFound
    render_404
  end

  def my_projects
    @projects = get_visible_projects(params[:term], EasySetting.value('easy_select_limit').to_i)

    respond_to do |format|
      format.api { render :template => 'easy_auto_completes/projects_with_url', :formats => [:api]}
    end
  end

  def visible_projects
    @projects = get_visible_projects(params[:term], EasySetting.value('easy_select_limit').to_i)

    respond_to do |format|
      format.api { render :template => 'easy_auto_completes/projects_with_id', :formats => [:api]}
    end
  end

  def visible_search_suggester_entities
    search_types = EasySetting.value('easy_search_suggester')['entity_types'].to_a & Redmine::Search.available_search_types
    limit_per_entity = 5

    @proposer_items = EasyExtensions::ActionProposer.get_items(params[:term], limit_per_entity)

    @suggest_entities = {}
    search_types.each do |search_type|
      klass = search_type.classify.constantize
      @suggest_entities[search_type.to_sym] = klass.search_results(params[:term], User.current, nil, titles_only: true, limit: limit_per_entity, open_issues: true).to_a
    end

    @suggest_entities.delete_if{|_k, v| v.blank? }

    respond_to do |format|
      format.api { render template: 'easy_auto_completes/entities_with_url', formats: [:api] }
    end
  end

  def project_templates
    @projects = get_template_projects(params[:term], EasySetting.value('easy_select_limit').to_i)

    respond_to do |format|
      format.api { render :template => 'easy_auto_completes/projects_with_id', :formats => [:api]}
    end
  end

  def add_issue_projects
    @projects = get_visible_projects_with_permission(:add_issues, params[:term], EasySetting.value('easy_select_limit').to_i)

    respond_to do |format|
      format.api { render :template => 'easy_auto_completes/projects_with_id', :formats => [:api]}
    end
  end

  def allowed_target_projects_on_move
    @projects = get_visible_projects_with_permission(:move_issues, params[:term], EasySetting.value('easy_select_limit').to_i)

    respond_to do |format|
      format.api { render :template => 'easy_auto_completes/projects_with_id', :formats => [:api]}
    end
  end

  def allowed_issue_statuses
    @allowed_statuses = begin
      if @issue = Issue.where(id: params[:issue_id]).first
        @issue.new_statuses_allowed_to
      else
        Issue.new.new_statuses_allowed_to(User.current, true)
      end
    end

    render :json => @allowed_statuses.collect{|s| {:text => s.name, :value => s.id}}
  end

  def allowed_issue_trackers
    @allowed_trackers = begin
      if params[:issue_id]
        Issue.where(id: params[:issue_id]).first.try(:project).try(:trackers)
      elsif params[:project_id]
        Project.where(id: params[:project_id]).first.try(:trackers)
      end
    end

    render :json => @allowed_trackers.to_a.collect{|t| {:text => t.name, :value => t.id}}
  end

  def issue_priorities
    render :json => IssuePriority.active.collect{|p| {:text => p.name, :value => p.id}}
  end

  def assignable_users
    entity_type = begin; (params[:entity_type] || 'Issue').constantize; rescue; end
    return render_404 if entity_type.nil?
    @entity = entity_type.where(id: (params[:entity_id] || params[:issue_id])).first
    @entity ||= entity_type.new
    if params[:project_id] && @entity.respond_to?(:project)
      @entity.project = Project.where(id: params[:project_id]).first
    end
    select_options = entity_assigned_to_collection_for_select_options(@entity, params[:project_id])

    if params[:easy_autocomplete]
      users = select_options.map { |k,v| v }.first.map { |o| { value: o.first, id: o.last } }
      json = { users: users }
    else
      json = [['', '']] + select_options.collect{|o| {:text => o[0], :children => o[1].map{|i| {i[1].to_s => i[0].to_s}}}}
    end

    render json: json
  rescue ActiveRecord::RecordNotFound
    render_404
  end

  def assignable_versions
    @issue = Issue.find(params[:issue_id])
    options = [['', '']]
    options.concat(@issue.assignable_versions.map { |v| { text: v.name, value: v.id } })

    render :json => options
  rescue ActiveRecord::RecordNotFound
    render_404
  end

  def users
    @users = get_active_users_scope(params[:term], EasySetting.value('easy_select_limit').to_i).to_a

    respond_to do |format|
      format.api { render :template => 'easy_auto_completes/users_with_id', :formats => [:api]}
    end
  end

  def internal_users
    @users = get_active_internals_users_scope(params[:term], EasySetting.value('easy_select_limit').to_i).to_a

    respond_to do |format|
      format.api { render :template => 'easy_auto_completes/users_with_id', :formats => [:api]}
    end
  end

  def users_in_meeting_calendar
    @users = get_active_users_in_meeting_calendar_scope(params[:term], EasySetting.value('easy_select_limit').to_i).to_a

    respond_to do |format|
      format.api { render :template => 'easy_auto_completes/users_with_id', :formats => [:api]}
    end
  end

  def principals
    @users = get_active_principals_scope(params[:term], EasySetting.value('easy_select_limit').to_i).to_a

    respond_to do |format|
      format.api { render :template => 'easy_auto_completes/users_with_id', :formats => [:api]}
    end
  end

  def issue_author_values
    issue = Issue.find(params[:issue_id])
    users = (get_project_users_scope(issue.project).to_a + Array(issue.author)).uniq.collect{|u| {:text => u.name, :value => u.id}}

    render :json => users
  rescue ActiveRecord::RecordNotFound
    render_404
  end

  def custom_field_possible_values
    cf = CustomField.find(params[:custom_field_id])

    render :json => cf.possible_values.map { |name, code| { text: name, value: code } }
  rescue ActiveRecord::RecordNotFound
    render_404
  end

  def time_entry_activities
    @available_activities = TimeEntryActivity.shared.sorted
    activities = @available_activities.map { |tea| { text: tea.name, value: tea.id } }
    activities.unshift({text: '', value: ''}) if params[:include_blank].present?
    render json: activities
  end

  def saved_filters
    values = {}
    saved_queries = EasyQuery.preload(:user).named(params[:entity_type], params[:term].to_s)
    saved_queries.each do |x|
      if values[x.user_id]
        values[x.user_id][:children] << {x.id => x.name}
      else
        values[x.user_id] = {text: x.user ? x.user.name : I18n.t(:label_nobody), children: [{x.id => x.name}]}
      end
    end
    render :json => values.values
  end

  def easy_entity_activity_category
    render :json => EasyEntityActivityCategory.sorted.collect{|s| {:text => s.name, :value => s.id}}
  end

  def easy_entity_activity_attendees
    render :json => {easy_entity_activity_attendees: EasyEntityActivityAttendee.all_attendees_values(params[:term], EasySetting.value('easy_select_limit').to_i)}
  end

  private

  def set_self_only
    @self_only = params[:term].blank?
  end

  def get_available_parent_issues_scope(project, term = '', limit = nil)
    if EasySetting.value('show_issue_id', project)
      sql_where = ["#{Issue.table_name}.subject like ? OR #{Issue.table_name}.id = ?", "%#{term}%", term.to_i]
    else
      sql_where = ["#{Issue.table_name}.subject like ?", "%#{term}%"]
    end

    Issue.cross_project_scope(project, Setting.cross_project_subtasks).visible.where(sql_where).order(:subject).limit(limit)
  end

  def get_available_parent_issues(project, term = '', limit = nil)
    scope = get_available_parent_issues_scope(project, term, limit)
    scope.to_a
  end

  def get_visible_projects_scope(term='', limit=nil)
    scope = Project.visible.non_templates.sorted.like(term).limit(limit).reorder("#{Project.table_name}.lft")
    scope
  end

  def get_template_projects_scope(term='', limit=nil)
    scope = Project.templates.like(term).limit(limit).reorder("#{Project.table_name}.lft")
    scope
  end

  def get_template_projects(term='', limit=nil)
    scope = get_template_projects_scope(term, limit)
    scope.to_a
  end

  def get_visible_projects(term='', limit=nil)
    scope = get_visible_projects_scope(term, limit)
    scope.to_a
  end

  def get_visible_projects_with_permission(permission, term='', limit=nil)
    scope = get_visible_projects_scope(term, limit)
    scope = scope.allowed_to(permission)
    scope.to_a
  end

  def get_active_users_scope(term='', limit=nil)
    scope = User.active.like(term).limit(limit).sorted
    scope
  end

  def get_active_internals_users_scope(term='', limit=nil)
    scope = get_active_users_scope(term, limit)
    scope = scope.easy_type_internal
    scope
  end

  def get_active_users_in_meeting_calendar_scope(term='', limit=nil)
    scope = get_active_users_scope(term, limit)
    scope = scope.users_in_meeting_calendar
    scope += Group.visible.givable.like(term).limit(limit).sorted
    scope
  end

  def get_active_principals_scope(term='', limit=nil)
    scope = Principal.active.like(term).limit(limit).sorted
    scope
  end

  def get_project_users_scope(project, term='', limit=nil)
    scope = project.users.active.non_system_flag.like(term).limit(limit).sorted
    scope
  end

  def get_issues_with_parents(term='', limit=nil)
    Issue.visible.where.not(parent_id: nil).joins(:project).where(projects: {easy_is_easy_template: false}).like(term).limit(limit).to_a
  end

  def get_issues_with_children(term='', limit=nil)
    Issue.visible.where("#{Issue.table_name}.rgt - #{Issue.table_name}.lft > 1").joins(:project).where(get_project_if_exist).like(term).limit(limit).to_a
  end

  def get_root_issues(term='', limit=nil)
    Issue.visible.where(parent_id: nil).joins(:project).where(get_project_if_exist).like(term).limit(limit).to_a
  end

  def get_project_if_exist
    if params[:source_options] && params[:source_options][:project_id]
      {project_id: params[:source_options][:project_id]}
    else
      {projects: {easy_is_easy_template: false}}
    end
  end

end
