# frozen_string_literal: true

class FinalDestination::Resolver
  @mutex = Mutex.new
  def self.lookup(addr, timeout: nil)
    timeout ||= default_dns_query_timeout

    @mutex.synchronize do
      @result = nil

      @queue ||= Queue.new
      ensure_lookup_thread

      @lookup = addr
      @parent = Thread.current

      # Wakeup the worker thread
      @queue << ""

      # This sleep will be interrupted by the lookup thread
      # if completed within timeout
      sleep timeout
      if !@result
        @thread.kill
        @thread.join
        @thread = nil
        if @error
          @error.backtrace.push(*caller)
          raise @error
        else
          raise Timeout::Error
        end
      end
      @result
    end
  end

  private

  def self.default_dns_query_timeout
    if gs = GlobalSetting.dns_query_timeout_secs.presence
      Integer(gs)
    else
      2
    end
  end

  def self.ensure_lookup_thread
    return if @thread&.alive?

    @thread =
      Thread.new do
        while true
          @queue.deq
          @error = nil
          begin
            @result = Addrinfo.getaddrinfo(@lookup, 80, nil, :STREAM).map(&:ip_address)
          rescue => e
            @error = e
          end
          @parent.wakeup
        end
      end
    @thread.name = "final-destination_resolver_thread"
  end
end