require 'sidekiq/exception_handler'

module Sidekiq
  module ExpectedFailures
    class Middleware
      include ExceptionHandler
      attr_reader :handled_exceptions

      def call(worker, msg, queue)
        setup_exceptions(worker)

        yield

        rescue *handled_exceptions.keys => ex
          data = {
            failed_at: Time.now.strftime("%Y/%m/%d %H:%M:%S %Z"),
            args:      msg['args'],
            exception: ex.class.to_s,
            error:     ex.message,
            worker:    msg['class'],
            queue:     queue
          }

          log_exception(data, ex, msg)
      end

      private

      def setup_exceptions(worker)
        @handled_exceptions = worker.class.get_sidekiq_options['expected_failures'] || Sidekiq.expected_failures
      end

      def exception_intervals(ex)
        [handled_exceptions[ex.class]].flatten.compact
      end

      def log_exception(data, ex, msg)
        result = Sidekiq.redis do |conn|
          conn.multi do |m|
            m.lpush("expected:#{today}", Sidekiq.dump_json(data))
            m.sadd("expected:dates", today)
            m.hincrby("expected:count", data[:exception], 1)
          end
        end

        handle_exception(ex, msg) if exception_intervals(ex).include?(result[0])
      end

      def today
        Date.today.to_s
      end
    end
  end
end
