# frozen_string_literal: true

module Packages
  # rubocop: disable Gitlab/BulkInsert
  class CreateDependencyService < BaseService
    attr_reader :package, :dependencies

    delegate :project_id, to: :package, private: true

    def initialize(package, dependencies)
      @package = package
      @dependencies = dependencies
    end

    def execute
      Packages::DependencyLink.dependency_types.each_key do |type|
        create_dependency(type)
      end
    end

    private

    def create_dependency(type)
      return unless dependencies[type].is_a?(Hash)

      names_and_version_patterns = dependencies[type]
      existing_ids, existing_names = find_existing_ids_and_names(names_and_version_patterns)
      dependencies_to_insert = names_and_version_patterns

      if existing_names.any?
        dependencies_to_insert = names_and_version_patterns.reject { |k, _| k.in?(existing_names) }
      end

      ApplicationRecord.transaction do
        inserted_ids = bulk_insert_package_dependencies(dependencies_to_insert)
        bulk_insert_package_dependency_links(type, (existing_ids + inserted_ids))
      end
    end

    def find_existing_ids_and_names(names_and_version_patterns)
      ids_and_names = Packages::Dependency.for_package_project_id_names_and_version_patterns(project_id, names_and_version_patterns)
                                          .pluck_ids_and_names
      ids = ids_and_names.map(&:first) || []
      names = ids_and_names.map(&:second) || []
      [ids, names]
    end

    def bulk_insert_package_dependencies(names_and_version_patterns)
      return [] if names_and_version_patterns.empty?

      rows = names_and_version_patterns.map do |name, version_pattern|
        {
          name: name,
          version_pattern: version_pattern,
          project_id: project_id
        }
      end

      ids = ApplicationRecord.legacy_bulk_insert(Packages::Dependency.table_name, rows, return_ids: true, on_conflict: :do_nothing)
      return ids if ids.size == names_and_version_patterns.size

      Packages::Dependency.uncached do
        # The bulk_insert statement above do not dirty the query cache. To make
        # sure that the results are fresh from the database and not from a stalled
        # and potentially wrong cache, this query has to be done with the query
        # cache disabled.
        Packages::Dependency.ids_for_package_project_id_names_and_version_patterns(project_id, names_and_version_patterns)
      end
    end

    def bulk_insert_package_dependency_links(type, dependency_ids)
      rows = dependency_ids.map do |dependency_id|
        {
          package_id: package.id,
          dependency_id: dependency_id,
          dependency_type: Packages::DependencyLink.dependency_types[type.to_s],
          project_id: project_id
        }
      end

      ApplicationRecord.legacy_bulk_insert(Packages::DependencyLink.table_name, rows)
    end
  end
  # rubocop: enable Gitlab/BulkInsert
end
