class RSpec::Rails::Matchers::ActionCable::HaveBroadcastedTo

@private
rubocop: disable Metrics/ClassLength

def at_least(count)

def at_least(count)
  set_expected_number(:at_least, count)
  self
end

def at_most(count)

def at_most(count)
  set_expected_number(:at_most, count)
  self
end

def base_description

def base_description
  "#{message_expectation_modifier} #{@expected_number} messages to #{stream}".tap do |msg|
    msg << " with #{data_description(@data)}" unless @data.nil?
  end
end

def base_message

def base_message
  "#{base_description}, but broadcast #{@matching_msgs_count}"
end

def check(messages)

def check(messages)
  @matching_msgs, @unmatching_msgs = messages.partition do |msg|
    decoded = ActiveSupport::JSON.decode(msg)
    decoded = decoded.with_indifferent_access if decoded.is_a?(Hash)
    if @data.nil? || values_match?(@data, decoded)
      @block.call(decoded)
      true
    else
      false
    end
  end
  @matching_msgs_count = @matching_msgs.size
  case @expectation_type
  when :exactly then @expected_number == @matching_msgs_count
  when :at_most then @expected_number >= @matching_msgs_count
  when :at_least then @expected_number <= @matching_msgs_count
  end
end

def check_channel_presence

def check_channel_presence
  return if @channel.present? && @channel.respond_to?(:channel_name)
  error_msg = "Broadcasting channel can't be inferred. Please, specify it with `from_channel`"
  raise ArgumentError, error_msg
end

def data_description(data)

def data_description(data)
  if data.is_a?(RSpec::Matchers::Composable)
    data.description
  else
    data.inspect
  end
end

def description

def description
  "have broadcasted #{base_description}"
end

def exactly(count)

def exactly(count)
  set_expected_number(:exactly, count)
  self
end

def failure_message

def failure_message
  "expected to broadcast #{base_message}".tap do |msg|
    if @unmatching_msgs.any?
      msg << "\nBroadcasted messages to #{stream}:"
      @unmatching_msgs.each do |data|
        msg << "\n   #{data}"
      end
    end
  end
end

def failure_message_when_negated

def failure_message_when_negated
  "expected not to broadcast #{base_message}"
end

def from_channel(channel)

def from_channel(channel)
  @channel = channel
  self
end

def initialize(target, channel:)

def initialize(target, channel:)
  @target = target
  @channel = channel
  @block = proc { }
  @data = nil
  set_expected_number(:exactly, 1)
end

def matches?(proc)

def matches?(proc)
  raise ArgumentError, "have_broadcasted_to and broadcast_to only support block expectations" unless Proc === proc
  original_sent_messages_count = pubsub_adapter.broadcasts(stream).size
  proc.call
  in_block_messages = pubsub_adapter.broadcasts(stream).drop(original_sent_messages_count)
  check(in_block_messages)
end

def message_expectation_modifier

def message_expectation_modifier
  case @expectation_type
  when :exactly then "exactly"
  when :at_most then "at most"
  when :at_least then "at least"
  end
end

def once

def once
  exactly(:once)
end

def pubsub_adapter

def pubsub_adapter
  ::ActionCable.server.pubsub
end

def set_expected_number(relativity, count)

def set_expected_number(relativity, count)
  @expectation_type = relativity
  @expected_number =
    case count
    when :once then 1
    when :twice then 2
    when :thrice then 3
    else Integer(count)
    end
end

def stream

def stream
  @stream ||= case @target
              when String
                @target
              when Symbol
                @target.to_s
              else
                check_channel_presence
                @channel.broadcasting_for(@target)
              end
end

def supports_block_expectations?

def supports_block_expectations?
  true
end

def thrice

def thrice
  exactly(:thrice)
end

def times

def times
  self
end

def twice

def twice
  exactly(:twice)
end

def with(data = nil, &block)

def with(data = nil, &block)
  @data = data
  @data = @data.with_indifferent_access if @data.is_a?(Hash)
  @block = block if block
  self
end