class BulkSqlDelete
  STATEMENT_TIMEOUT = ENV.fetch("STATEMENT_TIMEOUT_BULK_DELETE", 10_000).to_i.seconds / 1_000.to_f

  def self.delete_in_batches(sql)
    ActiveRecord::Base.connection_pool.with_connection do |connection|
      perform_and_log(sql) do
        unless Rails.env.test?
          connection.begin_db_transaction
        end
        result = ApplicationRecord.with_statement_timeout STATEMENT_TIMEOUT, connection: connection do
          connection.exec_delete(sql)
        end
        connection.commit_db_transaction unless Rails.env.test?

        result
      end
    end
  end

  def self.perform_and_log(delete_sql)
    deleted = 0

    loop do
      result = 0

      duration = Benchmark.realtime do
        result = yield
      end

      log_deletion_batch(delete_sql, result, duration)

      deleted += result

      break if result.zero?
    end

    deleted
  rescue StandardError => e
    log_deletion_error(delete_sql, e)
    raise e
  end
  private_class_method :perform_and_log

  def self.log_deletion_batch(statement, count, duration)
    Rails.logger.info(
      tag: "notification_delete",
      statement: statement,
      rows_deleted: count,
      duration: duration,
    )
  end
  private_class_method :log_deletion_batch

  def self.log_deletion_error(statement, exception)
    Rails.logger.error(
      tag: "notification_delete",
      statement: statement,
      exception_message: exception.message,
      backtrace: exception.backtrace.join("\n"),
    )
  end
  private_class_method :log_deletion_error
end
