class SessionChannel < ApplicationCable::Channel
  include CableBroadcastable
  def subscribed
    session = resolve_session
    reject unless session && session.id.to_s == params[:session_id].to_s

    stream_for session
    session.device_states.find_or_create_by!(device_role: params[:device_role] || "controller")
  end

  def resolve_session
    if connection.regia_session
      connection.regia_session
    else
      StreamSession.joins(match: :team)
                   .merge(connection.current_user.manageable_teams)
                   .find_by(id: params[:session_id])
    end
  end

  def unsubscribed
    stop_all_streams
  end

  def receive(data)
    session = resolve_session
    return unless session

    handle_message(session, data)
  end

  private

  def handle_message(session, data)
    case data["type"]
    when "command"
      SessionChannel.broadcast_message(session, data)
      execute_command(session, data)
    when "score_update"
      update_score(session, data)
    when "timeout"
      update_timeout(session, data)
    when "device_state"
      update_device(session, data)
    end
  end

  def execute_command(session, data)
    case data["action"]
    when "start_stream"
      Sessions::Start.new(session).call
    when "stop_stream"
      Sessions::Stop.new(session).call
    when "pause_stream"
      Sessions::Pause.new(session).call
    end
  end

  def update_score(session, data)
    score = session.score_state || session.create_score_state!
    score.update!(
      home_sets: data["home_sets"] || score.home_sets,
      away_sets: data["away_sets"] || score.away_sets,
      home_points: data["home_points"] || score.home_points,
      away_points: data["away_points"] || score.away_points,
      current_set: data["current_set"] || score.current_set,
      set_partials: data.key?("set_partials") ? data["set_partials"] : score.set_partials
    )
    SessionChannel.broadcast_message(session, score.as_cable_payload)
  end

  def update_timeout(session, data)
    score = session.score_state
    return unless score

    if data["team"] == "home"
      score.update!(timeout_home: true)
    else
      score.update!(timeout_away: true)
    end
    SessionChannel.broadcast_message(session, { type: "timeout", team: data["team"] })
  end

  def update_device(session, data)
    state = session.device_states.find_or_initialize_by(device_role: data["device_role"] || "camera")
    state.update!(
      battery_level: data["battery"],
      network_type: data["network"],
      current_bitrate: data["bitrate"],
      fps: data["fps"],
      last_seen_at: Time.current
    )
    SessionChannel.broadcast_message(session, state.as_cable_payload)
  end
end
