# frozen_string_literal: true

class TemporaryRedis
  REDIS_TEMP_DIR = "/tmp/discourse_temp_redis"
  REDIS_LOG_PATH = "#{REDIS_TEMP_DIR}/redis.log"
  REDIS_PID_PATH = "#{REDIS_TEMP_DIR}/redis.pid"

  attr_reader :instance

  def initialize
    set_redis_server_bin
  end

  def port
    @port ||= find_free_port(11000..11900)
  end

  def start
    return if @started
    FileUtils.rm_rf(REDIS_TEMP_DIR)
    Dir.mkdir(REDIS_TEMP_DIR)
    FileUtils.touch(REDIS_LOG_PATH)

    puts "Starting redis on port: #{port}"
    @thread = Thread.new do
      system(
        @redis_server_bin,
        "--port", port.to_s,
        "--pidfile", REDIS_PID_PATH,
        "--logfile", REDIS_LOG_PATH,
        "--databases", "1",
        "--save", '""',
        "--appendonly", "no",
        "--daemonize", "no",
        "--maxclients", "100",
        "--dir", REDIS_TEMP_DIR
      )
    end

    puts "Waiting for redis server to start..."
    success = false
    instance = nil
    config = {
      port: port,
      host: "127.0.0.1",
      db: 0
    }
    start = Time.now
    while !success
      begin
        instance = DiscourseRedis.new(config, namespace: true)
        success = instance.ping == "PONG"
      rescue Redis::CannotConnectError
      ensure
        if !success && (Time.now - start) >= 5
          STDERR.puts "ERROR: Could not connect to redis in 5 seconds."
          self.remove
          exit(1)
        elsif !success
          sleep 0.1
        end
      end
    end
    puts "Redis is ready"
    @instance = instance
    @started = true
  end

  def remove
    if @instance
      @instance.shutdown
      @thread.join
      puts "Redis has been shutdown."
    end
    FileUtils.rm_rf(REDIS_TEMP_DIR)
    @started = false
    puts "Redis files have been cleaned up."
  end

  private

  def set_redis_server_bin
    path = `which redis-server 2> /dev/null`.strip
    if path.size < 1
      STDERR.puts 'ERROR: redis-server is not installed on this machine. Please install it'
      exit(1)
    end
    @redis_server_bin = path
  rescue => ex
    STDERR.puts 'ERROR: Failed to find redis-server binary:'
    STDERR.puts ex.inspect
    exit(1)
  end

  def find_free_port(range)
    range.each do |port|
      return port if port_available?(port)
    end
  end

  def port_available?(port)
    TCPServer.open(port).close
    true
  rescue Errno::EADDRINUSE
    false
  end
end