# frozen_string_literal: true

require 'ffi'

module Facter
  module Util
    module Resolvers
      module Ffi
        class AddrInfo < ::FFI::Struct
          layout  :ai_flags, :int,
                  :ai_family, :int,
                  :ai_socketype, :int,
                  :ai_protocol, :int,
                  :ai_addrlen, :uint,
                  :ai_addr, :pointer,
                  :ai_canonname, :string,
                  :ai_next, :pointer
        end

        module Hostname
          HOST_NAME_MAX = 64

          extend ::FFI::Library
          ffi_lib ::FFI::Library::LIBC

          attach_function :getaddrinfo, %i[string string pointer pointer], :int
          attach_function :gethostname, %i[pointer int], :int
          attach_function :freeaddrinfo, [:pointer], :void

          def self.getffihostname
            raw_hostname = ::FFI::MemoryPointer.new(:char, HOST_NAME_MAX)

            res = Hostname.gethostname(raw_hostname, HOST_NAME_MAX)
            return unless res.zero?

            raw_hostname.read_string
          end

          def self.getffiaddrinfo(hostname)
            ret = FFI::MemoryPointer.new(:pointer)

            hints = Facter::Util::Resolvers::Ffi::AddrInfo.new
            hints[:ai_family] = Socket::AF_UNSPEC
            hints[:ai_socketype] = Socket::SOCK_STREAM
            hints[:ai_flags] = Socket::AI_CANONNAME

            res = Hostname.getaddrinfo(hostname, '', hints.to_ptr, ret)
            log = Log.new(self)
            log.debug("FFI Getaddrinfo finished with exit status: #{res}")
            log.debug("FFI Getaddrinfo returned an AddrInfo struct at address: #{ret}")
            return unless res.zero?

            return if ret == FFI::Pointer::NULL || ret.read_pointer == FFI::Pointer::NULL

            begin
              addr = Facter::Util::Resolvers::Ffi::AddrInfo.new(ret.read_pointer)
              name = addr[:ai_canonname]
              log.debug("FFI AddrInfo struct has the fqdn: #{name}")
              return if !name || hostname == name

              name
            ensure
              Hostname.freeaddrinfo(ret.read_pointer)
            end
          end
        end
      end
    end
  end
end
