# frozen_string_literal: true
require "action_controller/log_subscriber"

module Sapience
  module Extensions
    module ActionController
      class LogSubscriber < ::ActionController::LogSubscriber
        alias orig_start_processing start_processing
        alias orig_process_action process_action

        # Log as debug to hide Processing messages in production
        def start_processing(event)
          debug { "Processing ##{event.payload[:action]}" }
        end

        def process_action(event)
          return unless logger.info?
          data = request(event.payload)
          data.merge! request_id(event)
          data.merge! runtimes(event)
          data.merge! exception(event.payload)
          info(data)
        end

        private

        def request(payload) # rubocop:disable AbcSize
          {
            method: payload[:method].upcase,
            request_path: request_path(payload),
            format: format(payload),
            status: payload[:status].to_i,
            controller: payload[:params]["controller"],
            action: payload[:params]["action"],
            host: Sapience.config.host,
            route: "#{payload[:params].delete("controller")}##{payload[:params]["action"]}",
            message: "Completed ##{payload[:params].delete("action")}",
            tags: Sapience.tags,
            params: payload[:params],
          }
        end

        def format(payload)
          if ::ActionPack::VERSION::MAJOR == 3 && ::ActionPack::VERSION::MINOR == 0
            payload[:formats].first
          else
            payload[:format]
          end
        end

        def request_id(event)
          { request_id: event.transaction_id }
        end

        def runtimes(event)
          {
            total: event.duration,
            view: event.payload[:view_runtime],
            db: event.payload[:db_runtime],
          }.each_with_object({}) do |(name, runtime), runtimes|
            runtimes[:runtimes] ||= {}
            runtimes[:runtimes][name] = runtime.to_f.round(2) if runtime
            runtimes
          end
        end

        # Monkey patching to enable exception logging
        def exception(payload)
          if payload[:exception]
            exception, message = payload[:exception]
            message ||= exception.message
            status = ActionDispatch::ExceptionWrapper.status_code_for_exception(exception)
            backtrace = $ERROR_INFO.try(:backtrace).try(:first)
            backtrace ||= exception.backtrace.first
            message = "#{exception}\n#{message}\n#{backtrace}"
            { status: status, error: message }
          else
            {}
          end
        end

        def request_path(payload)
          payload[:path].split("?").first
        end
      end
    end
  end
end
