#-- copyright
# OpenProject is a project management system.
# Copyright (C) 2012-2018 the OpenProject Foundation (OPF)
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License version 3.
#
# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
# Copyright (C) 2006-2017 Jean-Philippe Lang
# Copyright (C) 2010-2013 the ChiliProject Team
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
#
# See docs/COPYRIGHT.rdoc for more details.
#++

class Queries::BaseQuery
  class << self
    attr_accessor :model

    def i18n_scope
      :activerecord
    end
  end

  attr_accessor :filters, :orders

  include Queries::AvailableFilters
  include Queries::AvailableOrders
  include ActiveModel::Validations

  validate :filters_valid,
           :sortation_valid

  def initialize(user: nil)
    @filters = []
    @orders = []
    @user = user
  end

  def results
    scope = default_scope

    if valid?
      filters.each do |filter|
        scope = scope.merge(filter.scope)
      end

      orders.each do |order|
        scope = scope.merge(order.scope)
      end
    else
      scope = empty_scope
    end

    scope
  end

  def where(attribute, operator, values)
    filter = filter_for(attribute)
    filter.operator = operator
    filter.values = values
    filter.context = context

    filters << filter

    self
  end

  def order(hash)
    hash.each do |attribute, direction|
      order = order_for(attribute)
      order.direction = direction
      orders << order
    end

    self
  end

  def default_scope
    self.class.model.all
  end

  def find_active_filter(name)
    filters.index_by(&:name)[name]
  end

  def find_available_filter(name)
    available_filters.detect { |f| f.name == name }
  end

  protected

  attr_accessor :user

  def filters_valid
    filters.each do |filter|
      next if filter.valid?

      add_error(:filters, filter.human_name, filter)
    end
  end

  def sortation_valid
    orders.each do |order|
      next if order.valid?

      add_error(:orders, order.name, order)
    end
  end

  def add_error(local_attribute, attribute_name, object)
    messages = object
               .errors
               .messages
               .values
               .flatten
               .join(" #{I18n.t('support.array.sentence_connector')} ")

    errors.add local_attribute, errors.full_message(attribute_name, messages)
  end

  def empty_scope
    self.class.model.where(Arel::Nodes::Equality.new(1, 0))
  end

  def context
    nil
  end
end
