class StreamSession < ApplicationRecord
  include AASM

  PLATFORMS = %w[matchlivetv youtube facebook twitch].freeze
  STATUSES = %w[idle connecting live reconnecting paused ended error].freeze

  belongs_to :match
  belongs_to :user
  has_many :stream_events, dependent: :destroy
  has_one :score_state, dependent: :destroy
  has_many :device_states, dependent: :destroy

  validates :platform, inclusion: { in: PLATFORMS }
  validates :status, inclusion: { in: STATUSES }

  before_validation :ensure_publish_token, on: :create

  scope :broadcasting, -> { where(status: %w[live connecting reconnecting]) }
  scope :search_by_team_or_opponent, lambda { |query|
    q = query.to_s.strip
    return all if q.blank?

    term = "%#{sanitize_sql_like(q)}%"
    joins(match: { team: :club }).where(
      "teams.name ILIKE :term OR matches.opponent_name ILIKE :term OR clubs.name ILIKE :term OR matches.location ILIKE :term",
      term: term
    )
  }

  aasm column: :status do
    state :idle, initial: true
    state :connecting, :live, :reconnecting, :paused, :ended, :error

    event :begin_connect do
      transitions from: %i[idle paused], to: :connecting
    end

    event :go_live do
      transitions from: %i[connecting reconnecting], to: :live
      after { update!(started_at: Time.current) if started_at.nil? }
    end

    event :lose_connection do
      transitions from: :live, to: :reconnecting
      after { increment!(:disconnection_count) }
    end

    event :reconnect do
      transitions from: :reconnecting, to: :live
    end

    event :pause do
      transitions from: :live, to: :paused
    end

    event :resume do
      transitions from: :paused, to: :live
    end

    event :finish do
      transitions from: %i[live reconnecting paused connecting], to: :ended
      after { close_session! }
    end

    event :fail do
      transitions from: %i[connecting reconnecting idle], to: :error
    end
  end

  def rtmp_ingest_url
    # RootEncoder richiede rtmp://host:port/app/stream (due segmenti).
    # MediaMTX path = live/match_{uuid} (no ?token= nel path).
    "#{MatchLiveTv.mediamtx_rtmp_url}/#{mediamtx_path_name}"
  end

  def mediamtx_path_name
    "live/match_#{id}"
  end

  def matchlivetv_platform?
    platform == "matchlivetv"
  end

  def hls_playback_url
    base = MatchLiveTv.hls_public_url.chomp("/")
    "#{base}/#{mediamtx_path_name}/index.m3u8"
  end

  def watch_page_url
    "#{MatchLiveTv.app_public_url.chomp('/')}/live/#{id}"
  end

  def terminal?
    status.in?(%w[ended error])
  end

  def resumable?
    status.in?(%w[connecting live reconnecting paused])
  end

  def end_stream!
    return if terminal?

    if may_finish?
      finish!
    else
      update!(status: "ended")
      record_ended_timestamps!
    end
  end

  def stream_key
    return @stream_key if instance_variable_defined?(:@stream_key)
    return nil if stream_key_encrypted.blank?

    stream_key_encryptor.decrypt_and_verify(stream_key_encrypted)
  rescue ActiveSupport::MessageEncryptor::InvalidMessage
    nil
  end

  def stream_key=(value)
    @stream_key = value
    self.stream_key_encrypted =
      value.present? ? stream_key_encryptor.encrypt_and_sign(value) : nil
  end

  private

  def stream_key_encryptor
    key = ActiveSupport::KeyGenerator.new(Rails.application.secret_key_base)
                                      .generate_key("stream_session_stream_key", 32)
    ActiveSupport::MessageEncryptor.new(key)
  end

  def ensure_publish_token
    self.publish_token ||= SecureRandom.urlsafe_base64(32)
  end

  def record_ended_timestamps!
    now = Time.current
    update!(ended_at: now) if ended_at.nil?
    if started_at && total_duration_secs.to_i.zero?
      update!(total_duration_secs: (now - started_at).to_i)
    end
  end

  def close_session!
    record_ended_timestamps!
  end
end
