app/searches/lookup_drugs.rb
class LookupDrugs

  def self.find(search_terms, scope, wrapper_class, filter = FilterChain.new)
    raise 'You must specify at least one search term!' unless search_terms.any?
    unfiltered_results, filtered_results = match_search_terms_to_objects(search_terms, scope, filter)
    results_to_drugs = match_objects_to_drugs(unfiltered_results, filtered_results, search_terms)
    de_dup_results(results_to_drugs).map do |terms, matched_drugs|
      wrapper_class.new(terms, matched_drugs.compact, 'drugs')
    end
  end

  private
  def self.match_objects_to_drugs(unfiltered_results, filtered_results, search_terms)
    results_to_drug_groups = search_terms.each_with_object({}) { |term, h| h[term] = [] }
    ids_to_results = filtered_results.each_with_object({}) { |result, h| h[result.id] = result}
    unfiltered_results.each do |unfiltered_result|
      result = ids_to_results[unfiltered_result.id]
      unless result
        case unfiltered_result
          when DataModel::Drug
            result = unfiltered_result.dup
            result.interactions = []
            result.id = unfiltered_result.id
          when DataModel::DrugAlias
            result = unfiltered_result.dup
            result.drug = unfiltered_result.drug.dup
            result.drug.interactions = []
            result.drug.id = unfiltered_result.drug.id
            result.id = unfiltered_result.id
        end
      end
      case result
        when DataModel::Drug
          results_to_drug_groups[result.name] << result if results_to_drug_groups.include? result.name
          results_to_drug_groups[result.concept_id] << result if results_to_drug_groups.include? result.concept_id
        when DataModel::DrugAlias
          results_to_drug_groups[result.alias] << result.drug
      end
    end
    results_to_drug_groups
  end

  def self.match_search_terms_to_objects(search_terms, scope, filter)
    search_terms = search_terms.dup

    unfiltered_drug_results = DataModel::Drug.send(scope).where(["drugs.name in (?) or concept_id in (?)", search_terms, search_terms])
    filtered_drug_results = filter.filter_rel(unfiltered_drug_results)

    search_terms = search_terms - unfiltered_drug_results.map(&:name) - unfiltered_drug_results.map(&:concept_id)

    unfiltered_drug_alias_results = DataModel::DrugAlias.send(scope).where(alias: search_terms)
    filtered_drug_alias_results = filter.filter_rel(unfiltered_drug_alias_results)

    unfiltered_results = unfiltered_drug_results.to_a.concat(unfiltered_drug_alias_results.to_a)
    filtered_results = filtered_drug_results.to_a.concat(filtered_drug_alias_results.to_a)

    return unfiltered_results, filtered_results
  end

  def self.de_dup_results(results)
    uniq_hash = Hash.new { |h, k| h[k] = [] }
    results.each do |search_term, value|
      if value != []
        uniq_hash[value] << search_term
      else
        drug = DataModel::Drug.for_show.where('lower(drugs.name) = ?', search_term.downcase)
        uniq_hash[drug] << search_term
      end
    end
    uniq_hash.invert
  end
end
