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

require 'contrast/components/logger'

module Contrast
  module Utils
    # Utility for saving raw findings for later
    class Findings
      include Contrast::Components::Logger::InstanceMethods

      def initialize
        @_collection = []
      end

      def collection
        @_collection ||= []
      end

      def push trigger_node, source, object, ret, *args
        return Contrast::Utils::ObjectShare::EMPTY_ARRAY unless trigger_node.collectable?

        @_collection << { trigger_node: trigger_node, source: source, object: object, ret: ret, args: args }
      end

      # Some rules requires response to be available before validating them correctly,
      # so we check if trigger_node.rule_id is collectable and then save them for
      # later report, when we have the response.
      #
      # @param trigger_node [Contrast::Agent::Assess::Policy::TriggerNode] the node to direct applying this
      # trigger event
      # @param source [Object] the source of the Trigger Event
      # @param object [Object] the Object on which the method was invoked
      # @param ret [Object] the Return of the invoked method
      # @param args [Array<Object>] the Arguments with which the method was invoked
      def collect_finding trigger_node, source, object, ret, *args
        push(trigger_node, source, object, ret, args)
        logger.trace('Finding collected', node_id: trigger_node.id,
                                          source_id: source.__id__,
                                          rule: trigger_node.rule_id)
      end

      # Build and report all collected findings for the collectable rules.
      #
      # We make sure the content-type is present before reporting, because some
      # findings do require it for validation.
      #
      # @return [true, nil]
      def report_collected_findings
        return if @_collection.empty?
        return if Contrast::Agent::REQUEST_TRACKER.current&.response&.content_type.nil?

        while @_collection.any?
          finding = @_collection.pop
          Contrast::Agent::Assess::Policy::TriggerMethod.build_finding(finding[:trigger_node],
                                                                       finding[:source],
                                                                       finding[:object],
                                                                       finding[:ret],
                                                                       finding[:args])
        end
        true
      end
    end
  end
end