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

require 'contrast/utils/string_utils'
require 'contrast/utils/assess/tracking_util'
require 'base64'

module Contrast
  module Api
    module Decorators
      # Used to decorate the {Contrast::Api::Dtm::TraceEventSignature} protobuf
      # model.
      module TraceEventSignature
        def self.included klass
          klass.extend(ClassMethods)
        end

        # Class methods for TraceEventSignature
        module ClassMethods
          # Convert the given composites into components that TeamServer can
          # understand in order to build a representation of the method
          # invoked.
          #
          # @param ret_obj [Contrast::Agent::Assess::ContrastObject, nil] the
          #   return value of the method call.
          # @param policy_node [Contrast::Agent::Assess::Policy::PolicyNode]
          #   the policy which pertains to the method invoked.
          # @param args [Array<Contrast::Agent::Assess::ContrastObject>, nil]
          # @return [Contrast::Api::Dtm::TraceEventSignature]
          def build ret_obj, policy_node, args
            signature = new
            signature.return_type = Contrast::Utils::StringUtils.force_utf8(type_name(ret_obj))
            signature.class_name = Contrast::Utils::StringUtils.force_utf8(policy_node.class_name)
            signature.method_name = Contrast::Utils::StringUtils.force_utf8(policy_node.method_name)
            if args
              args&.each do |arg|
                signature.arg_types << Contrast::Utils::StringUtils.force_utf8(type_name(arg))
              end
            end
            signature.constructor = policy_node.method_name == :new
            # if there's a ret, then this method isn't nil. not 100% full proof since you can
            # return nil, but this is the best we've got currently.
            signature.void_method = ret_obj.nil? || ret_obj.object.nil?
            # 8 is STATIC in Java... we have to placate them for now
            # it has been requested that flags be removed since it isn't used
            signature.flags = 8 unless policy_node.instance_method?
            signature
          end

          private

          # While Ruby signatures do not require neither a return type and can
          # return anything depending on inputs, code paths, etc, nor constant
          # parameter types, TeamServer was designed with strongly typed
          # languages (Java) in mind, so we need types in our signatures.
          #
          # @param contrast_object [Contrast::Agent::Assess::ContrastObject, nil]
          #   the object to find the type of
          # @return [String] the name of the module of the ret_obj, or `nil`
          def type_name contrast_object
            contrast_object ? contrast_object.object_type : Contrast::Utils::ObjectShare::NIL_STRING
          end
        end
      end
    end
  end
end

Contrast::Api::Dtm::TraceEventSignature.include(Contrast::Api::Decorators::TraceEventSignature)