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

cs__scoped_require 'rubygems/version'

module Contrast
  module Components
    module Agent
      # A wrapper build around the Common Agent Configuration project to allow
      # for access of the values contained in its
      # parent_configuration_spec.yaml.
      # Specifically, this allows for querying the state of the Agent.
      class Interface
        include Contrast::Components::ComponentBase
        include Contrast::Components::Interface

        access_component :config

        def enabled?
          !!@enabled
        end

        def disabled?
          !enabled?
        end

        def enable!
          @enabled = true
        end

        def disable!
          @enabled = false
        end

        def unavailable?
          !enabled?
        end

        def ready?
          state.agent_ready?
        end

        def patch_interpolation?
          interpolation_patch_possible?
        end

        def rewrite_interpolation?
          !interpolation_patch_possible?
        end

        def patch_yield?
          !Contrast::Utils::BooleanUtil.false?(CONFIG.root.agent.ruby.propagate_yield)
        end

        def interpolation_enabled?
          !Contrast::Utils::BooleanUtil.false?(CONFIG.root.agent.ruby.interpolate)
        end

        def report_custom_code_sysfile_access?
          Contrast::Agent::FeatureState.instance.report_custom_code_sysfile_access?
        end

        def skip_instrumentation? loaded_module_name
          return true unless loaded_module_name

          loaded_module_name.start_with?(*CONFIG.root.agent.ruby.uninstrument_namespace)
        end

        # All this method chain does anymore is load the Thread patch that
        # propagates request contexts.  That can go away RUBY-700.
        SHARED_LIBRARIES = %w[contrast/extensions/ruby_core/thread].cs__freeze
        def run_instrumentation
          Contrast::Agent::FeatureState.instance.tap do |settings|
            SHARED_LIBRARIES.each { |lib| settings.instrument lib }
          end
          Contrast::Agent::Patching::Policy::Patcher.patch # This acts as a catch up for everything we didn't see get loaded
          enable_tracepoint # This handles all class loads & required instrumentation going forward
        end

        # TODO: RUBY-564: This responsibility should be extracted out of this agent component and moved to
        # the new component that handles one-time-whole-app initialization procedures.
        def insert_middleware app
          app.middleware.insert_before 0, Contrast::Agent::Middleware # Keep our middleware at the outermost layer of the onion
        end

        def enable_tracepoint
          Contrast::Agent::TracePointHook.enable!
        end

        protected

        INTERPOLATION_HOOKABLE_VERSION = Gem::Version.new('2.6.0')
        # Ruby exposed the C method for interpolation in version 2.6.0, meaning
        # we can attempt to patch using Funchook for that version and later.
        def interpolation_patch_possible?
          @_interpolation_patch_possible = Gem::Version.new(RUBY_VERSION) >= INTERPOLATION_HOOKABLE_VERSION if @_interpolation_patch_possible.nil?
          @_interpolation_patch_possible
        end
      end

      COMPONENT_INTERFACE = Interface.new
    end
  end
end