module Gitlab
  module Search
    class Query < SimpleDelegator
      def initialize(query, filter_opts = {}, &block)
        @raw_query = query.dup
        @filters = []
        @filter_options = { default_parser: :downcase.to_proc }.merge(filter_opts)

        self.instance_eval(&block) if block_given?

        @query = Gitlab::Search::ParsedQuery.new(*extract_filters)
        # set the ParsedQuery as our default delegator thanks to SimpleDelegator
        super(@query)
      end

      private

      def filter(name, **attributes)
        filter = { parser: @filter_options[:default_parser], name: name }.merge(attributes)

        @filters << filter
      end

      def filter_options(**options)
        @filter_options.merge!(options)
      end

      def extract_filters
        fragments = []

        filters = @filters.each_with_object([]) do |filter, parsed_filters|
          match = @raw_query.split.find { |part| part =~ /\A#{filter[:name]}:/ }
          next unless match

          input = match.split(':')[1..-1].join
          next if input.empty?

          filter[:value] = parse_filter(filter, input)
          filter[:regex_value] = Regexp.escape(filter[:value]).gsub('\*', '.*?')
          fragments << match

          parsed_filters << filter
        end

        query = (@raw_query.split - fragments).join(' ')

        [query, filters]
      end

      def parse_filter(filter, input)
        filter[:parser].call(input)
      end
    end
  end
end
