# frozen_string_literal: true

require "action_dispatch"

module ExceptionNotifier
  class DatadogNotifier < BaseNotifier
    attr_reader :client,
      :default_options

    def initialize(options)
      super
      @client = options.fetch(:client)
      @default_options = options
    end

    def call(exception, options = {})
      client.emit_event(
        datadog_event(exception, options)
      )
    end

    def datadog_event(exception, options = {})
      DatadogExceptionEvent.new(
        exception,
        options.reverse_merge(default_options)
      ).event
    end

    class DatadogExceptionEvent
      include ExceptionNotifier::BacktraceCleaner

      MAX_TITLE_LENGTH = 120
      MAX_VALUE_LENGTH = 300
      MAX_BACKTRACE_SIZE = 3
      ALERT_TYPE = "error"

      attr_reader :exception,
        :options

      def initialize(exception, options)
        @exception = exception
        @options = options
      end

      def request
        @request ||= ActionDispatch::Request.new(options[:env]) if options[:env]
      end

      def controller
        @controller ||= options[:env] && options[:env]["action_controller.instance"]
      end

      def backtrace
        @backtrace ||= exception.backtrace ? clean_backtrace(exception) : []
      end

      def tags
        options[:tags] || []
      end

      def title_prefix
        options[:title_prefix] || ""
      end

      def event
        title = formatted_title
        body = formatted_body

        Dogapi::Event.new(
          body,
          msg_title: title,
          alert_type: ALERT_TYPE,
          tags: tags,
          aggregation_key: [title]
        )
      end

      def formatted_title
        title =
          "#{title_prefix}#{controller_subtitle} (#{exception.class}) #{exception.message.inspect}"

        truncate(title, MAX_TITLE_LENGTH)
      end

      def formatted_body
        text = []

        text << "%%%"
        text << formatted_request if request
        text << formatted_session if request
        text << formatted_backtrace
        text << "%%%"

        text.join("\n")
      end

      def formatted_key_value(key, value)
        "**#{key}:** #{value}"
      end

      def formatted_request
        text = []
        text << "### **Request**"
        text << formatted_key_value("URL", request.url)
        text << formatted_key_value("HTTP Method", request.request_method)
        text << formatted_key_value("IP Address", request.remote_ip)
        text << formatted_key_value("Parameters", request.filtered_parameters.inspect)
        text << formatted_key_value("Timestamp", Time.current)
        text << formatted_key_value("Server", Socket.gethostname)
        text << formatted_key_value("Rails root", Rails.root) if defined?(Rails) && Rails.respond_to?(:root)
        text << formatted_key_value("Process", $PROCESS_ID)
        text << "___"
        text.join("\n")
      end

      def formatted_session
        text = []
        text << "### **Session**"
        text << formatted_key_value("Data", request.session.to_hash)
        text << "___"
        text.join("\n")
      end

      def formatted_backtrace
        size = [backtrace.size, MAX_BACKTRACE_SIZE].min

        text = []
        text << "### **Backtrace**"
        text << "````"
        size.times { |i| text << backtrace[i] }
        text << "````"
        text << "___"
        text.join("\n")
      end

      def truncate(string, max)
        (string.length > max) ? "#{string[0...max]}..." : string
      end

      def inspect_object(object)
        case object
        when Hash, Array
          truncate(object.inspect, MAX_VALUE_LENGTH)
        else
          object.to_s
        end
      end

      private

      def controller_subtitle
        "#{controller.controller_name} #{controller.action_name}" if controller
      end
    end
  end
end
