# 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(11_000..11_900)
  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 { |port| return port if port_available?(port) }
  end

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