# frozen_string_literal: true

class ContainerExpirationPolicyWorker # rubocop:disable Scalability/IdempotentWorker
  include ApplicationWorker
  include CronjobQueue
  include ExclusiveLeaseGuard

  feature_category :container_registry

  InvalidPolicyError = Class.new(StandardError)

  BATCH_SIZE = 1000.freeze

  def perform
    throttling_enabled? ? perform_throttled : perform_unthrottled
  end

  private

  def perform_unthrottled
    with_runnable_policy(preloaded: true) do |policy|
      with_context(project: policy.project,
                   user: policy.project.owner) do |project:, user:|
        ContainerExpirationPolicyService.new(project, user)
                                        .execute(policy)
      end
    end
  end

  def perform_throttled
    try_obtain_lease do
      with_runnable_policy do |policy|
        ContainerExpirationPolicy.transaction do
          policy.schedule_next_run!
          ContainerRepository.for_project_id(policy.id)
                             .each_batch do |relation|
                               relation.update_all(expiration_policy_cleanup_status: :cleanup_scheduled)
                             end
        end
      end

      ContainerExpirationPolicies::CleanupContainerRepositoryWorker.perform_with_capacity
    end
  end

  # TODO : remove the preload option when cleaning FF container_registry_expiration_policies_throttling
  def with_runnable_policy(preloaded: false)
    ContainerExpirationPolicy.runnable_schedules.each_batch(of: BATCH_SIZE) do |policies|
      # rubocop: disable CodeReuse/ActiveRecord
      cte = Gitlab::SQL::CTE.new(:batched_policies, policies.limit(BATCH_SIZE))
      # rubocop: enable CodeReuse/ActiveRecord
      scope = cte.apply_to(ContainerExpirationPolicy.all).with_container_repositories

      scope = scope.preloaded if preloaded

      scope.each do |policy|
        if policy.valid?
          yield policy
        else
          disable_invalid_policy!(policy)
        end
      end
    end
  end

  def disable_invalid_policy!(policy)
    policy.disable!
    Gitlab::ErrorTracking.log_exception(
      ::ContainerExpirationPolicyWorker::InvalidPolicyError.new,
      container_expiration_policy_id: policy.id
    )
  end

  def throttling_enabled?
    Feature.enabled?(:container_registry_expiration_policies_throttling)
  end

  def lease_timeout
    5.hours
  end
end
