module Resque
  module Plugins
    module AppInstanceJob
      def around_perform(args)
        case args.class.name
        when "Array"
          if args.first.is_a?(Hash)
            data = args.first.merge({:worker_class => self.to_s})
          else
            data = {:worker_class => self.to_s, :args => args.to_json}
          end
        when "Hash"
          data = args.merge({:worker_class => self.to_s})
        end

        if Rails.logger.is_a?(Ougai::Logger) && !Rails.env.development?
          Rails.logger.with_fields = { zecs_service: data.transform_keys(&:to_sym), trace_id: SecureRandom.uuid }
        end

        begin
          connection_count ||= 0
          @appinstance = ZuoraConnect::AppInstance.find(args['app_instance_id'].to_i)
          job_start_log

          @appinstance.new_session(holding_pattern: true)
        rescue ActiveRecord::RecordNotFound => exception
          # If we can't find app_instance let make sure we cleanup
          if Redis.current.zscore("AppInstance:Deleted", args['app_instance_id'].to_i).present?
            Rails.logger.info("No instance found, purge")
            ZuoraConnect::AppInstance.new(id: args['app_instance_id'].to_i).prune_data
            return
          else
            raise
          end
        rescue ActiveRecord::StatementInvalid => exception
          if (connection_count += 1) <= 3 &&
            (
              exception.message.include?("PG::UnableToSend: no connection to the server") ||
              exception.message.include?("PG::ConnectionBad: PQconsumeInput()") ||
              exception.message.include?("PG::ConnectionBad: PQsocket()") ||
              exception.message.include?("PG::UnableToSend: SSL SYSCALL")
            )

            sleep 30
            ActiveRecord::Base.establish_connection
            retry
          else
            raise
          end
        rescue PG::ConnectionBad => exception
          Rails.logger.warn("Bad Connection Restart", exception)
          Resque.enqueue_to(self.job.queue, self.job.payload['class'], args)
          return
        rescue ZuoraConnect::Exceptions::ConnectCommunicationError => exception
          Rails.logger.warn("Enqueue Job Again ~ 2 mins", exception)
          @appinstance.queue_pause(time: 2.minutes.to_i)
          Resque.enqueue_to(self.job.queue, self.job.payload['class'], args)
          return
        rescue Net::ReadTimeout, Net::OpenTimeout, Errno::ECONNRESET, Errno::ECONNREFUSED, SocketError => exception
          Rails.logger.warn("Enqueue Job Again ~ 2 mins", exception)
          @appinstance.queue_pause(time: 2.minutes.to_i)
          Resque.enqueue_to(self.job.queue, self.job.payload['class'], args)
          return
        end
        yield
      rescue
        raise
      ensure
        @appinstance.cache_app_instance if defined?(@appinstance)
        Rails.logger.flush if Rails.logger.methods.include?(:flush)
      end

      def job_start_log
        Rails.logger.info('Starting job')
      end
    end
  end
end