# frozen_string_literal: true

module Ci
  class DropPipelinesAndDisableSchedulesForUserService
    def execute(user, reason: :user_blocked, include_owned_projects_and_groups: false)
      if include_owned_projects_and_groups
        # Projects in the user namespace
        Project.personal(user).each_batch do |relation|
          project_ids = relation.pluck_primary_key

          drop_pipelines_for_projects(user, project_ids, reason)
          disable_schedules_for_projects(project_ids)
        end

        # Projects in group and descendant namespaces owned by the user
        user.owned_groups.select(:id, :traversal_ids).each_batch do |owned_groups_relation|
          owned_groups_relation.each do |owned_group|
            Project.in_namespace(owned_group.self_and_descendant_ids).each_batch do |project_relation|
              project_ids = project_relation.pluck_primary_key

              drop_pipelines_for_projects(user, project_ids, reason)
              disable_schedules_for_projects(project_ids)
            end
          end
        end
      end

      drop_pipelines_for_user(user, reason)

      user.notify_and_disable_all_pipeline_schedules_for_user(user.id)
    end

    private

    def drop_pipelines_for_user(user, reason)
      Ci::DropPipelineService.new.execute_async_for_all(
        Ci::Pipeline.for_user(user).with_status(*indexed_alive_statuses),
        reason,
        user,
        worker_class: drop_pipeline_worker_for(reason)
      )
    end

    def drop_pipelines_for_projects(user, project_ids, reason)
      Ci::DropPipelineService.new.execute_async_for_all(
        Ci::Pipeline.for_project(project_ids).with_status(*indexed_alive_statuses),
        reason,
        user,
        worker_class: drop_pipeline_worker_for(reason)
      )
    end

    # Excluded the waiting_for_callback and cancelling for performance reasons
    def indexed_alive_statuses
      Ci::HasStatus::ALIVE_STATUSES - %w[waiting_for_callback canceling]
    end

    # DropPipelineForBlockedUserWorker is used for all pipelines (user and project) when reason is :user_blocked.
    def drop_pipeline_worker_for(reason)
      reason == :user_blocked ? Ci::DropPipelineForBlockedUserWorker : Ci::DropPipelineWorker
    end

    def disable_schedules_for_user(user)
      Ci::PipelineSchedule.owned_by(user).active.each_batch do |relation|
        relation.update_all(active: false)
      end
    end

    def disable_schedules_for_projects(project_ids)
      Ci::PipelineSchedule.for_project(project_ids).active.each_batch do |relation|
        relation.update_all(active: false)
      end
    end
  end
end
