# frozen_string_literal: true

module BingAdsRubySdk
  module Errors
    # Parses the response from the API to raise errors if they are returned
    class ErrorHandler
      def initialize(response)
        @response = response
      end

      def call
        # Some operations don't return a response, for example:
        # https://msdn.microsoft.com/en-us/library/bing-ads-customer-management-deleteaccount.aspx
        return unless response.is_a? Hash
        raise fault_class.new(response) if contains_error?
      end

      private

      attr_reader :response

      def contains_error?
        partial_error_keys.any? || contains_fault?
      end

      def contains_fault?
        (ERROR_KEYS & response.keys).any?
      end

      def fault_class
        ERRORS_MAPPING.fetch(hash_with_error.keys.first, BASE_FAULT)
      end

      def hash_with_error
        response[:detail] || partial_errors || {}
      end

      def partial_errors
        response.select { |key| partial_error_keys.include?(key) }
      end

      # Gets populated partial error keys from the response
      # @return [Array] array of symbols for keys in the response
      #   that are populated with errors
      def partial_error_keys
        @partial_error_keys ||= (PARTIAL_ERROR_KEYS & response.keys).reject do |key|
          response[key].nil? || response[key].is_a?(String)
        end
      end

      BASE_FAULT = BingAdsRubySdk::Errors::GeneralError
      PARTIAL_ERROR_KEYS = %i[partial_errors nested_partial_errors].freeze
      ERROR_KEYS = %i[faultcode error_code]
      ERRORS_MAPPING = {
        api_fault_detail: BingAdsRubySdk::Errors::ApiFaultDetail,
        ad_api_fault_detail: BingAdsRubySdk::Errors::AdApiFaultDetail,
        editorial_api_fault_detail: BingAdsRubySdk::Errors::EditorialApiFaultDetail,
        api_batch_fault: BingAdsRubySdk::Errors::ApiBatchFault,
        api_fault: BingAdsRubySdk::Errors::ApiFault,
        nested_partial_errors: BingAdsRubySdk::Errors::NestedPartialError,
        partial_errors: BingAdsRubySdk::Errors::PartialError
      }
    end
  end
end
