require 'json'

module Billy
  module JSONUtils
    def self.json?(value)
      !!JSON.parse(value)
    rescue JSON::ParserError, TypeError
      false
    end

    # Recursively sorts the key/value pairs of all hashes within the given
    #   data structure while preserving the order of arrays.
    def self.sort_hash_keys(data)
      return data unless data.is_a?(Hash) || data.is_a?(Array)
      if data.is_a? Hash
        data.keys.sort.reduce({}) do |seed, key|
          seed[key] = sort_hash_keys(data[key])
          seed
        end
      else
        data.map do |element|
          sort_hash_keys(element)
        end
      end
    end

    # Recursively sorts the name/value pairs of JSON objects. For instance,
    #   sort_json('{"b" : "2", "a" : "1"}') == sort_json('{"a" : "1", "b" : "2"}')
    #   Per http://json.org/, "An object is an unordered set of name/value pairs"
    #   and "An array is an ordered collection of values". So, we can sort the
    #   name/value pairs by name (key), but we must preserve the order of an array.
    #   Processing JSON in this way enables a consistent SHA to be derived from
    #   JSON payloads which have the same name/value pairs, but different orders.
    def self.sort_json(json_str)
      JSONUtils.sort_hash_keys(JSON.parse(json_str, symbolize_names: true)).to_json
    end
  end
end
