# frozen_string_literal: true

module Appsignal
  module Utils
    class Data
      class << self
        def generate(body)
          if body.is_a?(Hash)
            map_hash(body)
          elsif body.is_a?(Array)
            map_array(body)
          else
            raise TypeError, "Body of type #{body.class} should be a Hash or Array"
          end
        end

        def map_hash(hash_value)
          map = Appsignal::Extension.data_map_new
          hash_value.each do |key, value|
            key = key.to_s
            case value
            when String
              map.set_string(key, value)
            when Integer
              # An Integer too big for C-lang longs to fit
              bigint = 1 << 63
              if value >= bigint
                map.set_string(key, map_bigint(value))
              else
                map.set_integer(key, value)
              end
            when Float
              map.set_float(key, value)
            when TrueClass, FalseClass
              map.set_boolean(key, value)
            when NilClass
              map.set_nil(key)
            when Hash
              map.set_data(key, map_hash(value))
            when Array
              map.set_data(key, map_array(value))
            else
              map.set_string(key, value.to_s)
            end
          end
          map
        end

        def map_array(array_value)
          array = Appsignal::Extension.data_array_new
          array_value.each do |value|
            case value
            when String
              array.append_string(value)
            when Integer
              # An Integer too big for C-lang longs to fit
              bigint = 1 << 63
              if value >= bigint
                array.append_string(map_bigint(value))
              else
                array.append_integer(value)
              end
            when Float
              array.append_float(value)
            when TrueClass, FalseClass
              array.append_boolean(value)
            when NilClass
              array.append_nil
            when Hash
              array.append_data(map_hash(value))
            when Array
              array.append_data(map_array(value))
            else
              array.append_string(value.to_s)
            end
          end
          array
        end

        def map_bigint(value)
          "bigint:#{value}"
        end
      end
    end
  end
end
