# RPUSHER: a Redis pusher...
# Usage:
#
# r.create queue:"asd", data:"asd=foo, bar=lol", verbose:true
#
# or if you are really lazy:
# r.create verbose:true
#
# Rpusher.new.create verbose:true
# => PONG
# => 2.8.19
# => Pushing on rpusher_queue:
# => {
# =>     :data => {
# =>          "foo" => "bar",
# =>         "ruby" => "yup"
# =>     }
# => }
# => 2
class Rpusher

  DEFAULT_QUEUE = 'pusher:rpusher_queue'
  DEFAULT_DATA = "foo=bar, ruby=yup!"

  # Eventually connects and return the local instance of Redis
  #
  # @return a Redis connection
  def redis
    @redis ||= Redis.new(:host => 'localhost', :port => 6379).tap do |r|
      raise IOError, "Redis is down!" unless r.ping.downcase.include?("pong")
    end
  end

  # LPUSH some data on Redis
  #
  # @param [String] the name of the queue where to push
  # @param [String] the arguments, if form of a list of KEY=VALUE comma separated
  # @param [Boolean] if you feel verbose (default false)
  # @return [String] the contents reversed lexically
  def create_one!(queue: DEFAULT_QUEUE, data: DEFAULT_DATA, verbose: false)
    begin
      raise ArgumentError, "Data argument(s) missing" unless data
      raise ArgumentError, "Data argument(s) should be KEY=VALUE" unless data.include?("=")

      puts "Connecting to Redis server (version #{redis.info["redis_version"]})... #{redis.ping}" if verbose

      message = { data: {} }

      data.split(',').map(&:lstrip).each do |pair|
        pair.split("=").tap do |key, value|
          message[:data][key] = value
        end
      end

      puts "Pushing #{message} on #{queue}..." if verbose

      MultiJson.encode(
        {
          data: {
            operation: 'lpush',
            queue: queue,
            result: redis.lpush(queue, MultiJson.encode(message))
          }
        }
      ).tap { |response| puts response if verbose }
    rescue Redis::CannotConnectError => e
      puts "#{e.message}: #{e.inspect}"
      MultiJson.encode(
        {
          data: {
            operation: 'lpush',
            queue: queue,
            result: 'error',
            message: e.message
          }
        })
      exit(1)
    end
  end

  # LINDEX the last pushed element
  #
  # @param [String] the name of the queue
  # @return [Hash] the last pushed element in a Hash
  def peek_last(queue: DEFAULT_QUEUE)
    MultiJson.decode redis.lindex(queue, 0)
  end

  # LRANGE all the elements in a queue
  #
  # @param [String] the name of the queue
  # @return [Hash] the last pushed element in a Hash
  def peek_all(queue: DFAULT_QUEUE)
    MultiJson.decode redis.lrange(queue, 0, -1)
  end

  # COUNT the elements in a queue
  #
  # @param [String] the name of the queue
  # @return [Hash] the queue and the count in a Hash
  def count_all(queue: DEFAULT_QUEUE)
    MultiJson.encode(
      {
        data: {
          queue: queue,
          count: redis.lrange(queue, 0, -1).length
        }
      })
  end

  # DROP all the elements in a queue
  #
  # @param [String] the name of the queue
  # @return [Hash] the queue name and the result
  def drop_queue!(queue: DEFAULT_QUEUE, verbose: false)
    puts "Dropping #{queue}" if verbose
    MultiJson.encode(
      {
        data: {
          queue: queue,
          result: redis.del(queue)
        }
      })
  end
end
