require "pact_broker/configuration"
require "pact_broker/api/decorators/runtime_error_problem_json_decorator"
require "pact_broker/errors"
require "pact_broker/messages"

# Generates the response headers and body for use when there is a runtime
# error in the business logic (services and repositories) when executing a Webmachine resource request.
# Obfuscates any exception messages that might expose vulnerablities in production.
# Uses the Accept header to determine whether to return application/problem+json
# or application/hal+json, for backwards compatibility.
# In the next major version of the Pact Broker, all error responses
# should use problem+json, regardless of Accept headers.

module PactBroker
  module Api
    module Resources
      class ErrorResponseGenerator
        include PactBroker::Logging
        extend PactBroker::Messages

        # @param error [StandardError]
        # @param error_reference [String] an error reference to display to the user
        # @param env [Hash] the rack env
        # @return [Hash, String] the response headers to set, the response body to set
        def self.call(error, error_reference, env, message: nil)
          body = response_body_hash(error, error_reference, env, display_message(error, message, obfuscated_error_message(error_reference)))
          return headers(env), body.to_json
        end

        def self.display_message(error, message, obfuscated_message)
          if message
            message
          elsif PactBroker.configuration.show_backtrace_in_error_response?
            error.message || obfuscated_message
          else
            PactBroker::Errors.reportable_error?(error) ? obfuscated_message : error.message
          end
        end

        private_class_method def self.response_body_hash(error, error_reference, env, message)
          if problem_json?(env)
            problem_json_response_body(message, env)
          else
            hal_json_response_body(error, error_reference, message)
          end
        end

        private_class_method def self.hal_json_response_body(error, error_reference, message)
          response_body = {
            error: {
              message: message,
              reference: error_reference
            }
          }
          if PactBroker.configuration.show_backtrace_in_error_response?
            response_body[:error][:backtrace] = error.backtrace
          end
          response_body
        end

        private_class_method def self.problem_json_response_body(message, env)
          error_decorator_class(env).new(message).to_hash(user_options: { base_url: env["pactbroker.base_url" ] })
        end

        private_class_method def self.obfuscated_error_message(error_reference)
          message("errors.runtime.with_error_reference", error_reference: error_reference)
        end

        private_class_method def self.headers(env)
          if problem_json?(env)
            { "content-type" => "application/problem+json;charset=utf-8" }
          else
            { "content-type" => "application/hal+json;charset=utf-8" }
          end
        end

        private_class_method def self.problem_json?(env)
          env["HTTP_ACCEPT"]&.include?("application/problem+json")
        end

        private_class_method def self.error_decorator_class(env)
          env["pactbroker.application_context"].decorator_configuration.class_for(:runtime_error_problem_json_decorator)
        end
      end
    end
  end
end
