module SocialStream
  module Search
    #TODO: sync with ThinkingSphinx min_infix_len and min_prefix_len options
    MIN_QUERY = 3

    class << self
      def keys(search_type)
        case search_type
        when :quick
          SocialStream.quick_search_models
        when :extended
          extended_search_models.keys
        end
      end

      def options_for_type_select
        keys(:extended).map{ |k|
          [ I18n.t("#{ k }", count: :other), k ]
        }.unshift([ I18n.t("search.show_all"), "" ])
      end

      def models(search_type, key = nil)
        case search_type
        when :quick
          quick_search_models
        when :extended
          if key.present?
            if extended_search_models.keys.include?(key.to_sym)
              extended_search_models[key.to_sym]
            else
              if extended_search_models.values.flatten.map{ |k| k.to_s }.include?(key.to_s.classify)
                [key.to_s.classify.constantize]
              else
                raise "Unknown search key #{ key }"
              end
            end
          else
            extended_search_models.values.flatten
          end
        when :repository
          SocialStream.repository_models.map{ |m| m.to_s.classify.constantize }
        else
          raise "Unknown search type #{ search_type }"
        end
      end

      def search(query, subject, options = {})
        ThinkingSphinx.search *args_for_search(query, subject, options)
      end

      def count(query, subject, options = {})
        ThinkingSphinx.count *args_for_search(query, subject, options)
      end

      private

      def quick_search_models
        @quick_search_models ||=
          parse_quick_search_models
      end

      def parse_quick_search_models
        SocialStream.quick_search_models.map{ |m|
          m.to_s.classify.constantize
        }
      end

      def extended_search_models
        @extended_search_models ||=
          parse_extended_search_models 
      end

      # Get a normalized hash from the configuration. Converts this:
      #
      #   [ :excursion, :user, { :resource => [ :post, :comment, :picture ] }
      #
      # into this:
      #
      #   {
      #     :excursion => [ Excursion ],
      #     :user => [ User ],
      #     :resource => [ Post, Comment, Picture ]
      #   }
      #
      def parse_extended_search_models
        SocialStream.extended_search_models.inject({}) do |hash, entry|
          case entry
          when Hash
            hash.update entry.inject({}){ |h, e|
              h[e.first] = Array.wrap(e.last).map{ |f| f.to_s.classify.constantize }
              h
            }
          when Symbol
            hash[entry] = Array.wrap(entry.to_s.classify.constantize)
          else
            raise "Unknown entry in config.extended_search_models #{ entry }"
          end

          hash
        end
      end

      def args_for_search(query, subject, options = {})
        options[:mode] ||= :extended

        models = models(options[:mode], options[:key])

        with = {}

        if options[:owner].present?
          with[:owner_id] = Actor.normalize_id(options[:owner])
        end

        if subject != options[:owner] || options[:owner].blank?
          with[:relation_ids] = Relation.ids_shared_with(subject)
        end

        [
          query,
          classes: models,
          with: with,
          limit: options[:limit],
          :match_mode => :extended,
          :ranker => :wordcount,
          :field_weights => { :title => 50, :tags => 10, :file_name => 10},
          page: options[:page],
          order:  options[:order]   #example: 'popularity DESC'
        ]
      end
    end
  end
end
