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

require 'contrast/utils/string_utils'

module Contrast
  module Api
    module Decorators
      # Used to decorate the {Contrast::Api::Dtm::RouteCoverage} protobuf model
      # to handle conversion between framework route classes and the dtm.
      module RouteCoverage
        def self.included klass
          klass.extend(ClassMethods)
        end

        # Class methods for RouteCoverage
        module ClassMethods
          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

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

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

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

          # Convert Sinatra route data to dtm message.
          #
          # @param controller [::Sinatra::Base] the route's final controller.
          # @param method [String] GET, PUT, POST, etc...
          # @param pattern [::Mustermann::Sinatra]  the pattern that was matched in routing.
          # @param url [String, nil] use url from string instead matched pattern.
          # @return [Contrast::Api::Dtm::RouteCoverage]
          def from_sinatra_route controller, method, pattern, url = nil
            safe_pattern = source_or_string(pattern)
            safe_url = source_or_string(url || pattern)

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

          # Convert Grape route data to dtm message.
          #
          # @param controller [::Grape::API] the route's final controller.
          # @param method [String] GET, PUT, POST, etc...
          # @param url [String, nil] use url from string instead matched pattern.
          # @param pattern [String, Grape::Router::Route] the pattern that was matched in routing.
          # @return [Contrast::Api::Dtm::RouteCoverage]
          def from_grape_controller controller, method, pattern, url = nil
            if pattern.cs__is_a?(Grape::Router::Route)
              safe_pattern = pattern.pattern&.path&.to_s
              safe_url = source_or_string(url || safe_pattern)
            else
              safe_pattern = source_or_string(pattern)
              safe_url = source_or_string(url || pattern)
            end

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

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