module JamRuby
  class MusicianSearch < BaseSearch

    cattr_accessor :jschema, :search_meta
    attr_accessor :user_counters

    KEY_STUDIOS = 'studio_sessions'
    KEY_AGES = 'ages'
    KEY_INTERESTS = 'interests'

    SORT_VALS = %W{ latency distance }
    SORT_ORDERS = { 
      SORT_VALS[0] => 'Latency to Me', 
      SORT_VALS[1] => 'Distance to Me' 
    }

    STUDIO_COUNTS = [ANY_VAL_INT, 0, 1, 2, 3, 4]
    STUDIOS_LABELS = { 
      STUDIO_COUNTS[0] => 'Any', 
      STUDIO_COUNTS[1] => 'under 10', 
      STUDIO_COUNTS[2] => '10 to 50', 
      STUDIO_COUNTS[3] => '50 to 100', 
      STUDIO_COUNTS[4] => 'over 100' 
    }

    AGE_COUNTS = [10, 20, 30, 40, 50]
    AGES = { 
      AGE_COUNTS[0] => 'Teens',
      AGE_COUNTS[1] => "20's", 
      AGE_COUNTS[2] => "30's", 
      AGE_COUNTS[3] => "40's", 
      AGE_COUNTS[4] => "50+" 
    }

    INTEREST_VALS = [ANY_VAL_STR,
                     GenrePlayer::VIRTUAL_BAND,
                     GenrePlayer::TRADITIONAL_BAND,
                     GenrePlayer::PAID_SESSION,
                     GenrePlayer::FREE_SESSION,
                     GenrePlayer::COWRITING,
                    ]
    INTERESTS = { 
      INTEREST_VALS[0] => 'Any', 
      INTEREST_VALS[1] => 'Virtual Band',
      INTEREST_VALS[2] => 'Traditional Band',
      INTEREST_VALS[3] => 'Paid Sessions',
      INTEREST_VALS[4] => 'Free Sessions',
      INTEREST_VALS[5] => 'Co-Writing'
    }

    def self.json_schema
      return @@jschema if @@jschema
      @@jschema = BaseSearch.json_schema.merge({
                     KEY_INTERESTS => INTEREST_VALS[0],
                     KEY_STUDIOS => STUDIO_COUNTS[0].to_s,
                     KEY_AGES => [],
                     KEY_SKILL => self::SKILL_VALS[0].to_s,
                   })
    end

    def self.search_filter_meta
      return @@search_meta if @@search_meta
      @@search_meta = super.merge({
                                    interests: { keys: INTEREST_VALS, map: INTERESTS },
                                    ages: { keys: AGE_COUNTS, map: AGES }
                                    })
    end

    def self.search_target_class
      User
    end

    def _ages(rel)
      unless (vals = json[KEY_AGES]).blank?
        return rel if vals.detect { |vv| ANY_VAL_INT == vv }
        arels = []
        vals.each do |val|
          today = Date.today
          case val.to_i
          when 10
            arels << User.where("birth_date >= ? AND birth_date < ?",
                                today - 20.years, today - 10.years)
          when 20
            arels << User.where("birth_date >= ? AND birth_date < ?",
                                today - 30.years, today - 20.years)
          when 30
            arels << User.where("birth_date >= ? AND birth_date < ?",
                                today - 40.years, today - 50.years)
          when 40
            arels << User.where("birth_date >= ? AND birth_date < ?",
                                today - 50.years, today - 40.years)
          when 50
            arels << User.where("birth_date <= ?", today - 50.years)
          end
        end
        rel = rel.where("birth_date IS NOT NULL")
        sql = "(#{arels.map(&:where_values).flatten.join(') OR (')})"
        rel = rel.where(sql)
      end
      rel
    end

    def _studios(rel)
      ss = json[KEY_STUDIOS].to_i
      rel = rel.where('studio_session_count = ?', ss) if 0 <= ss
      rel
    end

    def _gigs(rel)
      gg = json[KEY_GIGS].to_i
      rel = rel.where('concert_count = ?',gg) if 0 <= gg
      rel
    end

    def _skills(rel)
      if 0 < (val = json[KEY_SKILL].to_i)
        rel = rel.where(skill_level: val)
      end
      rel
    end

    def _interests(rel)
      val = json[KEY_INTERESTS]
      if val.present? && ANY_VAL_STR != val
        rel = rel.where("#{val} = ?", true)
      end
      rel
    end

    def _sort_order(rel)
      val = json[self.class::KEY_SORT_ORDER]
      if self.class::SORT_VALS[1] == val
        #locidispid = self.user.last_jam_locidispid || 0
        #my_locid = locidispid / 1000000
        #rel = rel.joins("LEFT JOIN geoiplocations AS my_geo ON my_geo.locid = #{my_locid}")
        #rel = rel.joins("LEFT JOIN geoiplocations AS other_geo ON users.last_jam_locidispid/1000000 = other_geo.locid")
        #rel = rel.group("users.id, my_geo.geog, other_geo.geog")
        #rel = rel.order('st_distance(my_geo.geog, other_geo.geog)')
      else
        rel = rel.joins("LEFT JOIN current_scores ON current_scores.a_userid = users.id AND current_scores.b_userid = '#{self.user.id}'")      
        rel = rel.order('current_scores.full_score ASC')
      end
      rel
    end

    def do_search(params={}) 
      rel = User.musicians.where('users.id <> ?', self.user.id)
      rel = Search.scope_schools_together(rel, self.user)
      rel = self._genres(rel)
      rel = self._ages(rel)
      rel = self._studios(rel)
      rel = self._gigs(rel)
      rel = self._skills(rel)
      rel = self._instruments(rel)
      rel = self._interests(rel)
      rel = self._sort_order(rel)
      rel
    end

    def search_includes(rel)
      rel.includes([:instruments, :followings, :friends])
    end

    def process_results_page(_results)
      @results = _results
      @user_counters = {} and return self unless user

      @user_counters = @results.inject({}) { |hh,val| hh[val.id] = []; hh }
      mids = "'#{@results.map(&:id).join("','")}'"

      # this gets counts for each search result on friends/follows/records/sessions
      @results.each do |uu|
        counters = { }
        counters[COUNT_FRIEND] = Friendship.where(:user_id => uu.id).count
        counters[COUNT_FOLLOW] = Follow.where(:followable_id => uu.id).count
        counters[COUNT_RECORD] = ClaimedRecording.where(:user_id => uu.id).count
        counters[COUNT_SESSION] = MusicSession.where(:user_id => uu.id).count
        @user_counters[uu.id] << counters
      end

      # this section determines follow/like/friend status for each search result
      # so that action links can be activated or not
      rel = User.select("users.id AS uid")
      rel = rel.joins("LEFT JOIN follows ON follows.user_id = '#{user.id}'")
      rel = rel.where(["users.id IN (#{mids}) AND follows.followable_id = users.id"])
      rel.all.each { |val| @user_counters[val.uid] << RESULT_FOLLOW }

      rel = User.select("users.id AS uid")
      rel = rel.joins("LEFT JOIN friendships AS friends ON friends.friend_id = '#{user.id}'")
      rel = rel.where(["users.id IN (#{mids}) AND friends.user_id = users.id"])
      rel.all.each { |val| @user_counters[val.uid] << RESULT_FRIEND }

      self
    end

    private 

    def _count(musician, key)
      if mm = @user_counters[musician.id] 
        return mm.detect { |ii| ii.is_a?(Hash) }[key]
      end if @user_counters
      0
    end

    public

    def follow_count(musician)
      _count(musician, COUNT_FOLLOW)
    end

    def friend_count(musician)
      _count(musician, COUNT_FRIEND)
    end

    def record_count(musician)
      _count(musician, COUNT_RECORD)
    end

    def session_count(musician)
      _count(musician, COUNT_SESSION)
    end

    def is_friend?(musician)
      if mm = @user_counters[musician.id] 
        return mm.include?(RESULT_FRIEND)
      end if @user_counters
      false
    end

    def is_follower?(musician)
      if mm = @user_counters[musician.id] 
        return mm.include?(RESULT_FOLLOW)
      end if @user_counters
      false
    end

    def search_type
      self.class.to_s
    end

    def is_blank?
      self.data_blob == self.class.json_schema
    end

    def description
      if self.is_blank?
        return 'Click search button to look for musicians with similar interests, skill levels, etc.'
      end
      jj = self.json
      str = ''
      if 0 < (val = jj[KEY_INSTRUMENTS]).length
        str += ", Instruments = "
        instr_ids = val.collect { |stored_instrument| stored_instrument['id'] }
        instrs = Instrument.where(["id IN (?)", instr_ids]).order(:description)
        instrs.each_with_index do |ii, idx|
          proficiency = val.detect { |stored_instrument| stored_instrument['id'] == ii.id }['level']
          str += "#{ii.description} / #{INSTRUMENT_PROFICIENCY[proficiency.to_i]}"
          str += ', ' unless idx==(instrs.length-1)
        end
      end

      if (val = jj[KEY_INTERESTS]) != INTEREST_VALS[0]
        str += ", Interest = #{INTERESTS[val]}"
      end
      if (val = jj[KEY_SKILL].to_i) != SKILL_VALS[0]
        str += ", Skill = #{SKILL_LEVELS[val]}"
      end
      if (val = jj[KEY_STUDIOS].to_i) != STUDIO_COUNTS[0]
        str += ", Studio Sessions = #{STUDIOS_LABELS[val]}"
      end
      if (val = jj[KEY_GIGS].to_i) != GIG_COUNTS[0]
        str += ", Concert Gigs = #{GIG_LABELS[val]}"
      end
      val = jj[KEY_AGES].map(&:to_i)
      val.sort!
      if !val.blank?
        str += ", Ages = "
        val.each_with_index do |vv, idx| 
          str += "#{AGES[vv]}"
          str += ', ' unless idx==(val.length-1) 
        end
      end
      if 0 < (val = jj[KEY_GENRES]).length
        str += ", Genres = "
        genres = Genre.where(["id IN (?)", val]).order('description').pluck(:description)
        genres.each_with_index do |gg, idx| 
          str += "#{gg}"
          str += ', ' unless idx==(genres.length-1)
        end
      end
      str += ", Sort = #{SORT_ORDERS[json_value(MusicianSearch::KEY_SORT_ORDER)]}"

      if str.start_with?(', ')
        # trim off any leading ,
        str = str[2..-1]
      end
      str = 'Current Search: ' + str
      str
    end

  end
end
