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

require 'json'
require 'contrast/components/logger'
require 'contrast/agent/reporting/reporting_events/reporting_event'
require 'contrast/utils/object_share'

module Contrast
  module Agent
    module Reporting
      # This is the new Route Coverage class which will include all the needed information for the new reporting system
      # to relay this information in the Route Observation messages. These observations are used by TeamServer to
      # construct the route coverage information for the assess feature. They represent those methods which map to
      # externally accessible endpoints within the application, as registered to the application framework. This also
      # includes the literal URL and HTTP Verb used to invoke them, as they must have been called at this point to be
      # recorded.
      #
      # @attr_accessor route [String] the method signature used to uniquely identify the coverage report.
      # @attr_accessor url [String] the normalized URL used to access the method in the route.
      # @attr_accessor verb [String] the HTTP Verb used  to access the method in the route.
      # @attr_accessor count [Integer] the number of times this was hit; no longer needed as it can be derived.
      class RouteCoverage
        attr_accessor :route, :url, :verb, :count

        def initialize
          @route = Contrast::Utils::ObjectShare::EMPTY_STRING
          @verb = Contrast::Utils::ObjectShare::EMPTY_STRING
          @url = Contrast::Utils::ObjectShare::EMPTY_STRING
          @count = 0
        end

        def attach_rack_based_data final_controller, method, route_pattern, url = nil
          if route_pattern.cs__is_a?(Grape::Router::Route)
            safe_pattern = route_pattern.pattern&.path&.to_s
            safe_url = source_or_string(url || safe_pattern)
          else
            safe_pattern = source_or_string(route_pattern)
            safe_url = source_or_string(url || route_pattern)
          end

          self.route = "#{ final_controller }##{ method } #{ safe_pattern }"
          self.verb = Contrast::Utils::StringUtils.force_utf8(method)
          self.url = Contrast::Utils::StringUtils.force_utf8(safe_url)
          self
        end

        # Convert ActionDispatch::Journey::Route to Contrast::Agent::Reporting::RouteCoverage
        #
        # @param journey_obj [ActionDispatch::Journey::Route] a rails route
        # @param url [String, nil] use url from string instead of journey object.
        # @return [Contrast::Agent::Reporting::RouteCoverage]
        def attach_rails_data journey_obj, url = nil
          self.route = "#{ journey_obj.defaults[:controller] }##{ journey_obj.defaults[:action] }"

          verb = source_or_string(journey_obj.verb)
          self.verb = Contrast::Utils::StringUtils.force_utf8(verb)

          url ||= source_or_string(journey_obj.path.spec)
          self.url = Contrast::Utils::StringUtils.force_utf8(url)
          self
        end

        def source_or_string obj
          if obj.cs__is_a?(Regexp)
            obj.source
          elsif obj.cs__respond_to?(:safe_string)
            obj.safe_string
          else
            obj.to_s
          end
        end
      end
    end
  end
end