# frozen_string_literal: true

require_relative 'query'
require_relative 'where_clause'

module ErpIntegration
  module Fulfil
    module QueryMethods
      attr_accessor :selected_fields, :where_clauses

      # The `QueryMethods#select` works in two unique ways
      #
      # First, it takes a block to allow it to function like a regular `Enumerable#select`.
      #
      # @example
      #   $ ErpIntegration::SalesOrder.select { |sales_order| sales_order.id > 100  }
      #   # => [<ErpIntegration::SalesOrder @id=101 />, <ErpIntegration::Resource @id=102 />]
      #
      # Secondly, it allows us to select specific fields to be returned by Fulfil.
      #
      # Fulfil only returns an ID by default when one would query their API. However,
      # the ID is seldom the only value one will need. The `select` method allows
      # selecting multiple fields at once.
      #
      # @example
      #   $ ErpIntegration::SalesOrder.select(:id, :name, 'product.name')
      #   # => <ErpIntegration::Fulfil::Resources::SalesOrder @selected_fields=[:id, :name, "product.name"] />
      #
      # When one calls the `all` method, it will fetch all the resources from Fulfil
      # and it will return all the given fields (if available).
      #
      # @example
      #   $ ErpIntegration::SalesOrder.select(:id, :name, 'product.name').all
      #   # => [<ErpIntegration::SalesOrder @id=101 />, <ErpIntegration::Resource @id=102 />]
      #
      # Both a list of Strings, Symbols or a combination of these can be passed
      # to the `select` method.
      def select(*fields)
        if block_given?
          raise ArgumentError, "'select' with block doesn't take arguments." if fields.any?

          return super()
        end

        clone.select!(*fields)
      end

      def select!(*fields)
        self.selected_fields = (selected_fields || []).concat(fields)
        self
      end

      # Fulfil has a very powerful query syntax. However, that query syntax is rather
      # complicated and it's really specific to Fulfil.
      #
      # The `where` method introduces a well-known interface (e.g. ActiveRecord::Relation)
      # to query resources in Fulfil.
      #
      # @example
      #   $ ErpIntegration::SalesOrder.where(id: 100).all
      #   # => <ErpIntegration::Fulfil::Collection @items=[<ErpIntegration::SalesOrder @id=100 />] />
      #
      # If one adds the `comparison_operator` key to the arguments, it will use
      # that comparison operator to build the query to Fulfil.
      #
      # All the other `where_` methods use this technique to provide logic for all
      # the different comparison operators.
      def where(*args)
        clone.where!(*args)
      end

      def where!(args)
        comparison_operator = args.delete(:comparison_operator)

        args.each_pair do |key, value|
          self.where_clauses =
            (where_clauses || []) << WhereClause.new(
              key: key, value: value, comparison_operator: comparison_operator
            )
        end

        self
      end

      def where_ilike(args)
        where(args.merge(comparison_operator: 'ilike'))
      end

      def where_in(args)
        where(args.merge(comparison_operator: 'in'))
      end

      def where_less_than(args)
        where(args.merge(comparison_operator: '<'))
      end

      def where_less_or_equal_to(args)
        where(args.merge(comparison_operator: '<='))
      end

      def where_like(args)
        where(args.merge(comparison_operator: 'like'))
      end

      def where_more_than(args)
        where(args.merge(comparison_operator: '>'))
      end

      def where_more_or_equal_to(args)
        where(args.merge(comparison_operator: '>='))
      end

      def where_not(args)
        where(args.merge(comparison_operator: '!='))
      end

      def where_not_in(args)
        where(args.merge(comparison_operator: 'not in'))
      end
    end
  end
end