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

cs__scoped_require 'contrast/agent/assess/contrast_event'
cs__scoped_require 'contrast/utils/string_utils'

module Contrast
  module Agent
    module Assess
      module Events
        # This class holds the data about an event in the application
        # We'll use it to build an event that TeamServer can consume if
        # the object to which this event belongs ends in a trigger.
        class SourceEvent < Contrast::Agent::Assess::ContrastEvent
          attr_reader :source_name, :source_type, :request

          def initialize policy_node, tagged, object, ret, args, source_type = nil, source_name = nil
            super(policy_node, tagged, object, ret, args)
            @source_type = source_type
            @source_name = source_name
            @request = Contrast::Agent::REQUEST_TRACKER.current&.request
          end

          def find_parent_ids _policy_node, _object, _ret, _args
            nil
          end

          # Convert this event into a DTM that TeamServer can consume
          def to_dtm_event
            event = super
            event.field_name = Contrast::Utils::StringUtils.force_utf8(source_name)
            event_source_dtm = build_event_source_dtm
            event.event_sources << event_source_dtm if event_source_dtm
            event.object_id = event_id.to_i
            event
          end

          def forced_source_type
            @_forced_source_type ||= Contrast::Utils::StringUtils.force_utf8(source_type)
          end

          def forced_source_name
            @_forced_source_name ||= Contrast::Utils::StringUtils.force_utf8(source_name)
          end

          # Probably only for source events, but we'll go
          # with source_type instead. java & .net support source_type
          # in propagation events, so we'll future proof this
          def build_event_source_dtm
            # You can have a source w/o a name, but not w/o a type
            return unless source_type

            dtm = Contrast::Api::Dtm::TraceEventSource.new
            dtm.type = forced_source_type
            dtm.name = forced_source_name
            dtm
          end

          # We have to do a little work to figure out what our TS appropriate
          # target is. To break this down, the logic is as follows:
          # 1) If I have a highlight, it means that I have a P target that is
          #    not in integer form (it was a named / keyword type for which I had
          #    to find the index). I need to address this so that TS can process
          #    it.
          # 2) I'll set the event's source and target to TS values.
          # 3) Return the highlight or the first source/target as the taint
          #    target.
          def determine_taint_target event
            return unless @policy_node&.targets&.any?

            event.source = @policy_node.source_string if @policy_node.source_string
            event.target = if @highlight
                             "P#{ @highlight }"
                           else
                             @policy_node.target_string
                           end
            @highlight || @policy_node.targets[0]
          end
        end
      end
    end
  end
end