Maintenance::MigrateTreeNodeToMboProfilesTask

Source code
# frozen_string_literal: true

class Maintenance::MigrateTreeNodeToMboProfilesTask < MaintenanceTasks::Task
  collection_batch_size(1000)
  def collection
    TreeNode.all
  end

  def process(tree_node)
    mbo_profiles = tree_node.mbo_profiles.reload.to_a
    all_custom_fields = collect_all_custom_fields(tree_node)

    mbo_profiles = ensure_mbo_profiles_exist(tree_node, mbo_profiles, all_custom_fields)
    associate_custom_fields_with_mbo_profiles(all_custom_fields, mbo_profiles, tree_node)
  end

  def collect_all_custom_fields(tree_node)
    custom_field_ids = tree_node.all_custom_field_ids
    CustomField.where(id: custom_field_ids)
  end

  private

  def find_orphaned_custom_fields(tree_node)
    return CustomField.none unless CustomField.connection.column_exists?(:custom_fields, :tree_node_id)

    CustomField.where(custom_fields: { tree_node_id: tree_node.id })
               .where.missing(:mbo_profiles)
  end

  def ensure_mbo_profiles_exist(tree_node, mbo_profiles, all_custom_fields)
    return mbo_profiles unless all_custom_fields.exists? && mbo_profiles.empty?

    [create_default_mbo_profile(tree_node)]
  end

  def associate_custom_fields_with_mbo_profiles(all_custom_fields, mbo_profiles, tree_node)
    processed_custom_field_ids = Set.new
    all_custom_fields.find_each do |cf|
      next if processed_custom_field_ids.include?(cf.id)

      processed_custom_field_ids.add(cf.id)
      if mbo_profiles.empty?
        associate_with_new_default_mbo_profile(cf, tree_node)
      elsif mbo_profiles.one?
        associate_with_single_mbo_profile(cf, mbo_profiles.first)
      else
        associate_with_all_mbo_profiles(cf, mbo_profiles)
      end
    end
  end

  def associate_with_new_default_mbo_profile(custom_field, tree_node)
    mbo_profile = create_default_mbo_profile(tree_node)
    CustomFieldToMboProfile.find_or_create_by!(custom_field: custom_field, mbo_profile: mbo_profile)
    Rails.logger.info(
      "Created default MboProfile #{mbo_profile.mbo_id} for TreeNode #{tree_node.id} " \
      "and associated CustomField #{custom_field.id}"
    )
  end

  def associate_with_single_mbo_profile(custom_field, mbo_profile)
    CustomFieldToMboProfile.find_or_create_by!(custom_field: custom_field, mbo_profile: mbo_profile)
    Rails.logger.info(
      "Associated CustomField #{custom_field.id} → single MboProfile #{mbo_profile.id}"
    )
  end

  def associate_with_all_mbo_profiles(custom_field, mbo_profiles)
    mbo_profiles.each do |mbo|
      CustomFieldToMboProfile.find_or_create_by!(custom_field: custom_field, mbo_profile: mbo)
    end
    Rails.logger.info(
      "Associated CustomField #{custom_field.id}#{mbo_profiles.size} MboProfiles"
    )
  end

  delegate :count, to: :TreeNode

  # Create a unique default MboProfile for the given TreeNode
  def create_default_mbo_profile(tree_node)
    MboProfiles::CreateDefaultMboProfileService.new(tree_node: tree_node).call
  end
end