# Copyright (c) 2023 Contrast Security, Inc. See https://www.contrastsecurity.com/enduser-terms-0317a for more details.
# frozen_string_literal: true

module Contrast
  module Utils
    module Patching
      # This module will include patch methods from Patcher module
      # in case of need to add new logic to the Patcher
      # please do it here
      module PatcherUtils
        # This method is called by TracePointHook to instrument a specific class during a require
        # or eval of dynamic class definition
        def patch_specific_module mod
          with_contrast_scope do
            mod_name = mod.cs__name
            return unless Contrast::Utils::ClassUtil.truly_defined?(mod_name)
            return if ::Contrast::AGENT.skip_instrumentation?(mod_name)

            load_patches_for_module(mod_name)

            return if all_module_names.none?(mod_name)

            module_data = Contrast::Agent::ModuleData.new(mod, mod_name)
            patch_into_module(module_data)
            all_module_names.delete(mod_name) if status_type.get_status(mod).patched?
          rescue StandardError => e
            logger.error('Unable to patch module', e, module: mod_name)
          end
        end

        # We did it, team. We found a patcher(s) that applies to the given
        # class (or module) and the given method. Time to do some tracking.
        #
        # @param mod [Module] the module in which the patch should be
        #   placed.
        # @param methods [Array(Symbol)] all the instance or singleton
        #   methods in this mod.
        # @param method_policy [Contrast::Agent::Patching::Policy::MethodPolicy]
        #   the policy that applies to the given method_name.
        # @return [Boolean] if patched, either by this invocation or a
        #   previous, or not
        def patch_method mod, methods, method_policy
          return false unless methods&.any? # don't even build the name if there are no methods

          if Contrast::Utils::ClassUtil.prepended_method?(mod, method_policy)
            Contrast::Agent::Patching::Policy::Patch.instrument_with_prepend(mod, method_policy)
          else
            Contrast::Agent::Patching::Policy::Patch.instrument_with_alias(mod, methods, method_policy)
          end
        end
      end
    end
  end
end