# frozen_string_literal: true

require 'active_job/arguments'

module MailScheduler
  class NotificationServiceWorker # rubocop:disable Scalability/IdempotentWorker
    include ApplicationWorker

    data_consistency :always

    sidekiq_options retry: 3
    include MailSchedulerQueue

    feature_category :team_planning
    worker_resource_boundary :cpu
    loggable_arguments 0

    def perform(meth, *args)
      check_arguments!(args)

      if NotificationService.permitted_actions.exclude?(meth.to_sym)
        raise(ArgumentError, "#{meth} not allowed for #{self.class.name}")
      end

      deserialized_args = ActiveJob::Arguments.deserialize(args)
      notification_service.public_send(meth, *deserialized_args) # rubocop:disable GitlabSecurity/PublicSend
    rescue ActiveJob::DeserializationError
      # No-op.
      # This exception gets raised when an argument
      # is correct (deserializeable), but it still cannot be deserialized.
      # This can happen when an object has been deleted after
      # rails passes this job to sidekiq, but before
      # sidekiq gets it for execution.
      # In this case just do nothing.
    end

    def self.perform_async(*args)
      super(*ActiveJob::Arguments.serialize(args))
    end

    private

    # This is copied over from https://github.com/rails/rails/blob/v6.0.1/activejob/lib/active_job/arguments.rb#L50
    # because it is declared as a private constant
    PERMITTED_TYPES = [NilClass, String, Integer, Float, BigDecimal, TrueClass, FalseClass].freeze

    private_constant :PERMITTED_TYPES

    # If an argument is in the PERMITTED_TYPES list,
    # it means the argument cannot be deserialized.
    # Which means there's something wrong with our code.
    def check_arguments!(args)
      args.each do |arg|
        if arg.class.in?(PERMITTED_TYPES)
          raise(ArgumentError, "Argument `#{arg}` cannot be deserialized because of its type")
        end
      end
    end
  end
end
