# frozen_string_literal: true

module LunaPark
  module Extensions
    module Exceptions
      # class-level mixin

      module Substitutive
        def self.extended(base)
          base.extend  ClassMethods
          base.include InstanceMethods
        end

        module ClassMethods
          # Substitute original exception with save original backtrace.
          #
          # @example bad case
          #   class MyException < StandardError
          #     extend LunaPark::Extensions::Exceptions::Substitutive
          #   end
          #
          #     call_exceptional_lib!
          #   rescue ExceptionalLib::SomeException => e
          #     raise MyException # => raised MyException with backtrace started from `raise MyException`
          #                       #      and not contained origin exception backtrace
          #                       #      that can be very painfull for debug
          #   end
          #
          # @example resolve
          #   begin
          #     call_exceptional_lib!
          #   rescue ExceptionalLib::SomeException => e
          #     raise MyException.substitute(e) # => raised MyException with backtrace started
          #                                     #      from library `raise ExceptionalLib::SomeException`
          #                                     #      so you can easily find out where exception starts
          #   end
          def substitute(origin)
            new = new(origin.message)
            new.backtrace = origin.backtrace
            new.origin    = origin
            new
          end
        end

        module InstanceMethods
          attr_accessor :origin
          attr_writer :backtrace

          def backtrace
            super || @backtrace
          end

          # Cover up trace for current exception
          #
          # @example bad case
          #   begin
          #     call_exceptional_lib!
          #   rescue ExceptionalLib::SomeException => e
          #     send_alert_to_developer
          #     raise e # => raised `ExceptionalLib::SomeException` with backtrace started
          #             #      from current line and not contained origin exception backtrace
          #             #      that can be very painful for debug
          #   end
          #
          # @example resolve
          #     begin
          #       call_exceptional_lib!
          #     rescue ExceptionalLib::SomeException => e
          #       send_alert_to_developer
          #       raise e.cover_up_backtrace # => raised `ExceptionalLib::SomeException` with original backtrace
          #                                  #       so you can easily find out where exception starts
          #     end
          def cover_up_backtrace
            new = dup
            new.backtrace = backtrace
            new.origin    = self
            new
          end
        end
      end
    end
  end
end