src/derails/inaction_mailbomb/base.rb
# frozen_string_literal: true

require "active_support/rescuable"

require "inaction_mailbomb/callbacks"
require "inaction_mailbomb/routing"

module InactionMailbomb
  # The base class for all application mailboxes.
  class Base
    include ActiveSupport::Rescuable
    include InactionMailbomb::Callbacks, InactionMailbomb::Routing

    attr_reader :inbound_email
    delegate :mail, :delivered!, :bounced!, to: :inbound_email

    delegate :logger, to: InactionMailbomb

    def self.receive(inbound_email)
      new(inbound_email).perform_processing
    end

    def initialize(inbound_email)
      @inbound_email = inbound_email
    end

    def perform_processing # :nodoc:
      ActiveSupport::Notifications.instrument "process.inaction_mailbomb", instrumentation_payload do
        track_status_of_inbound_email do
          run_callbacks :process do
            process
          end
        end
      rescue => exception
        # TODO: Include a reference to the inbound_email in the exception raised so error handling becomes easier
        rescue_with_handler(exception) || raise
      end
    end

    def process
      # Override in subclasses
    end

    def finished_processing? # :nodoc:
      inbound_email.delivered? || inbound_email.bounced?
    end

    # Enqueues the given +message+ for delivery and changes the inbound email's status to +:bounced+.
    def bounce_with(message)
      inbound_email.bounced!
      message.deliver_later
    end

    # Immediately sends the given +message+ and changes the inbound email's status to +:bounced+.
    def bounce_now_with(message)
      inbound_email.bounced!
      message.deliver_now
    end

    private
      def instrumentation_payload
        {
          mailbox: self,
          inbound_email: inbound_email.instrumentation_payload
        }
      end

      def track_status_of_inbound_email
        inbound_email.processing!
        yield
        inbound_email.delivered! unless inbound_email.bounced?
      rescue
        inbound_email.failed!
        raise
      end
  end
end

ActiveSupport.run_load_hooks :inaction_mailbomb, InactionMailbomb::Base