# frozen_string_literal: true

require 'json'
require 'uri'
require 'httpclient'

module Bolt
  module PuppetDB
    class Client
      def self.from_config(config)
        uri = if config['server_urls'].is_a? String
                config['server_urls']
              else
                config['server_urls'].first
              end
        uri = URI.parse(uri)
        uri.port ||= 8081

        cacert = File.expand_path(config['cacert'])
        token = config.token

        cert = config['cert']
        key = config['key']

        new(uri, cacert, token: token, cert: cert, key: key)
      end

      def initialize(uri, cacert, token: nil, cert: nil, key: nil)
        @uri = uri
        @cacert = cacert
        @token = token
        @cert = cert
        @key = key
      end

      def query_certnames(query)
        return [] unless query

        results = make_query(query)

        if results&.first && !results.first&.key?('certname')
          fields = results.first&.keys
          raise Bolt::PuppetDBError, "Query results did not contain a 'certname' field: got #{fields.join(', ')}"
        end
        results&.map { |result| result['certname'] }.uniq
      end

      # This method expects an array of certnames to get facts for
      def facts_for_node(certnames)
        return {} if certnames.empty? || certnames.nil?

        certnames.uniq!
        name_query = certnames.map { |c| ["=", "certname", c] }
        name_query.insert(0, "or")
        result = make_query(name_query, 'inventory')

        result&.each_with_object({}) do |node, coll|
          coll[node['certname']] = node['facts']
        end
      end

      def make_query(query, path = nil)
        body = JSON.generate(query: query)
        url = "#{@uri}/pdb/query/v4"
        url += "/#{path}" if path

        begin
          response = http_client.post(url, body: body, header: headers)
        rescue StandardError => err
          raise Bolt::PuppetDBError, "Failed to query PuppetDB: #{err}"
        end
        if response.code != 200
          raise Bolt::PuppetDBError, "Failed to query PuppetDB: #{response.body}"
        end
        begin
          JSON.parse(response.body)
        rescue JSON::ParserError
          raise Bolt::PuppetDBError, "Unable to parse response as JSON: #{response.body}"
        end
      end

      def http_client
        return @http if @http
        @http = HTTPClient.new
        @http.ssl_config.set_client_cert_file(@cert, @key) if @cert
        @http.ssl_config.add_trust_ca(@cacert)

        @http
      end

      def headers
        headers = { 'Content-Type' => 'application/json' }
        headers['X-Authentication'] = @token if @token
        headers
      end
    end
  end
end