module Teams # Elenco pubblico delle squadre con presenza su Match Live TV (diretta, replay o calendario). class PublicDirectory Entry = Struct.new( :team, :live_now, :replay_count, :upcoming_count, :last_activity_at, keyword_init: true ) def self.call(q: nil, sport: nil, live: false, replays: false) new(q: q, sport: sport, live: live, replays: replays).call end def initialize(q: nil, sport: nil, live: false, replays: false) @q = q.to_s.strip @sport = normalize_sport(sport) @live = cast_bool(live) @replays = cast_bool(replays) end def call teams = base_teams return [] if teams.empty? entries = build_entries(teams) entries = apply_toggle_filters(entries) sort_entries(entries) end private def normalize_sport(value) key = value.to_s.strip return nil if key.blank? return nil unless Sports::Catalog.find_optional(key) Sports::Catalog.normalize_key(key) end def cast_bool(value) ActiveModel::Type::Boolean.new.cast(value) end def base_teams scope = Team.includes(:club).where(id: active_team_ids) scope = scope.where(sport_key: @sport) if @sport scope = apply_search(scope) if @q.present? scope.to_a end def active_team_ids live = live_team_ids replays = replay_team_ids upcoming = upcoming_team_ids (live + replays + upcoming).uniq end def live_team_ids StreamSession.broadcasting .publicly_listed .where(platform: "matchlivetv") .joins(:match) .distinct .pluck("matches.team_id") end def replay_team_ids Recording.ready.publicly_listed.joins(stream_session: :match).distinct.pluck("matches.team_id") end def upcoming_team_ids broadcasting_match_ids = StreamSession.broadcasting.select(:match_id) Match.scheduled_for_live .where.not(id: broadcasting_match_ids) .distinct .pluck(:team_id) end def apply_search(scope) term = "%#{ActiveRecord::Base.sanitize_sql_like(@q)}%" scope.left_joins(:club, :matches).where( "teams.name ILIKE :term OR clubs.name ILIKE :term OR matches.location ILIKE :term OR matches.opponent_name ILIKE :term", term: term ).distinct end def build_entries(teams) team_ids = teams.map(&:id) live_set = live_team_ids.to_set replay_counts = Recording.ready.publicly_listed .joins(stream_session: :match) .where(matches: { team_id: team_ids }) .group("matches.team_id") .count last_replay_at = Recording.ready.publicly_listed .joins(stream_session: :match) .where(matches: { team_id: team_ids }) .group("matches.team_id") .maximum(:recorded_at) last_live_at = StreamSession.broadcasting .publicly_listed .joins(:match) .where(matches: { team_id: team_ids }) .group("matches.team_id") .maximum(:started_at) broadcasting_match_ids = StreamSession.broadcasting.select(:match_id) upcoming_counts = Match.scheduled_for_live .where(team_id: team_ids) .where.not(id: broadcasting_match_ids) .group(:team_id) .count teams.map do |team| last_activity = [last_replay_at[team.id], last_live_at[team.id]].compact.max Entry.new( team: team, live_now: live_set.include?(team.id), replay_count: replay_counts[team.id].to_i, upcoming_count: upcoming_counts[team.id].to_i, last_activity_at: last_activity ) end end def apply_toggle_filters(entries) entries = entries.select(&:live_now) if @live entries = entries.select { |entry| entry.replay_count.positive? } if @replays entries end def sort_entries(entries) entries.sort_by do |entry| [ entry.live_now ? 0 : 1, -(entry.last_activity_at&.to_i || 0), entry.team.name.downcase ] end end end end