module Shippinglogic
  class FedEx
    # Methods relating to receiving a response from FedEx and cleaning it up.
    module Response
      SUCCESSFUL_SEVERITIES = ["SUCCESS", "NOTE", "WARNING"]
      
      private
        # Overwriting the request method to clean the response and handle errors.
        def request(*args)
          response = clean_response(super)
          
          if success?(response)
            response
          else
            raise Error.new(response)
          end
        end
        
        # Was the response a success?
        def success?(response)
          response.is_a?(Hash) && SUCCESSFUL_SEVERITIES.include?(response[:highest_severity])
        end
        
        # Cleans the response and returns it in a more 'user friendly' format that is easier
        # to work with.
        def clean_response(response)
          cut_to_the_chase(sanitize_response_keys(response))
        end
        
        # FedEx likes nested XML tags, because they send quite a bit of them back in responses.
        # This method just 'cuts to the chase' and get to the heart of the response.
        def cut_to_the_chase(response)
          if response.is_a?(Hash) && response.keys.first && response.keys.first.to_s =~ /_reply(_details)?$/
            response.values.first
          else
            response
          end
        end
        
        # Recursively sanitizes the response object by clenaing up any hash keys.
        def sanitize_response_keys(response)
          if response.is_a?(Hash)
            response.inject({}) do |r, (key, value)|
              r[sanitize_response_key(key)] = sanitize_response_keys(value)
              r
            end
          elsif response.is_a?(Array)
            response.collect { |r| sanitize_response_keys(r) }
          else
            response
          end
        end

        # FedEx returns a SOAP response. I just want the plain response without all of the SOAP BS.
        # It basically turns this:
        #
        #   {"v3:ServiceInfo" => ...}
        #
        # into:
        #
        #   {:service_info => ...}
        #
        # I also did not want to use the underscore method provided by ActiveSupport because I am trying
        # to avoid using that as a dependency.
        def sanitize_response_key(key)
          key.to_s.gsub(/^(v[0-9]|ns):/, "").underscore.to_sym
        end
    end
  end
end