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

module Contrast
  module Components
    # This component encapsulates the statefulness of settings.
    # When we say 'settings', we're referring specifically to external
    # directives (likely provided by TeamServer) about product operation.
    # 'Settings' is not a generic term for 'configurable stuff'.
    module Settings
      APPLICATION_STATE_BASE = Struct.new(:modes_by_id, :exclusion_matchers).new(
          Hash.new { Contrast::Api::Settings::ProtectionRule::Mode::NO_ACTION }, [])
      PROTECT_STATE_BASE = Struct.new(:enabled, :rules).new(false, {})
      ASSESS_STATE_BASE = Struct.new(:enabled, :sampling_settings, :disabled_assess_rules).new(false, nil, []) do
        def sampling_settings= new_val
          @sampling_settings = new_val
          Contrast::Utils::Assess::SamplingUtil.instance.update
        end
      end

      # This is a class.
      class Interface
        include Contrast::Components::ComponentBase
        include Contrast::Components::Interface
        access_component :config

        # tainted_columns are database columns that receive unsanitized input.
        attr_reader :tainted_columns # This can probably go into assess_state?
        attr_reader :assess_state, :protect_state, :application_state

        def initialize
          reset_state
        end

        def code_exclusions
          @application_state.exclusion_matchers.select(&:code?)
        end

        # @param server_features [Contrast::Api::Settings::ServerFeatures]
        def update_from_server_features server_features
          @protect_state.enabled = server_features.protect_enabled?
          @assess_state.enabled = server_features.assess_enabled?
          @assess_state.sampling_settings = server_features.assess.sampling
        end

        # @param application_settings [Contrast::Api::Settings::ApplicationSettings]
        def update_from_application_settings application_settings
          new_vals = application_settings.application_state_translation
          @application_state.modes_by_id = new_vals[:modes_by_id]
          @application_state.exclusion_matchers = new_vals[:exclusion_matchers]
          @assess_state.disabled_assess_rules = new_vals[:disabled_assess_rules]
        end

        # Wipe state to zero.
        def reset_state
          @protect_state = PROTECT_STATE_BASE.dup
          @assess_state = ASSESS_STATE_BASE.dup
          @application_state = APPLICATION_STATE_BASE.dup
          @tainted_columns = {}
        end

        def build_protect_rules
          @protect_state.rules = {}

          # Rules. They add themselves on initialize.
          Contrast::Agent::Protect::Rule::CmdInjection.new
          Contrast::Agent::Protect::Rule::Deserialization.new
          Contrast::Agent::Protect::Rule::HttpMethodTampering.new
          Contrast::Agent::Protect::Rule::NoSqli.new
          Contrast::Agent::Protect::Rule::PathTraversal.new
          Contrast::Agent::Protect::Rule::Sqli.new
          Contrast::Agent::Protect::Rule::UnsafeFileUpload.new
          Contrast::Agent::Protect::Rule::Xss.new
          Contrast::Agent::Protect::Rule::Xxe.new
        end
      end

      COMPONENT_INTERFACE = Interface.new
    end
  end
end