# frozen_string_literal: true

# Copyright 2019 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

module Gapic
  module PathPattern
    ##
    # A positional segment in a path pattern.
    #  positional segments have a pattern of wildcards and do not carry a name
    #
    # @!attribure [r] type
    #   @return [String] The type of this segment
    # @!attribute [r] position
    #   @return [Integer] The argument position of this segment i.e.
    #     it's position if we remove all non-positional segments from the pattern
    # @!attribute [r] pattern
    #   @return [String] The pattern of the segment, for the positional segment it is also
    #     a pattern of its resource
    class PositionalSegment
      attr_reader :type, :position, :pattern
      def initialize position, pattern
        @type       = :positional
        @position   = position
        @pattern    = pattern
      end

      ##
      # Whether the segment is positional
      # @return [Boolean]
      def positional?
        true
      end

      ##
      # Whether the segment provides a resource pattern
      # @return [Boolean]
      def resource_pattern?
        true
      end

      ##
      # Whether the segment provides a nontrivial resource pattern
      # @return [Boolean]
      def nontrivial_resource_pattern?
        false
      end

      ##
      # Whether the segment provides arguments
      # @return [Boolean]
      def provides_arguments?
        true
      end

      ##
      # Names of the segment's arguments
      # @return [Array<String>]
      def arguments
        [position.to_s]
      end

      ##
      # Returns a segment's pattern filled with dummy values
      #   names of the values are generated starting from the index provided
      # @param start_index [Integer] a starting index for dummy value generation
      # @return [String] a pattern filled with dummy values
      def expected_path_for_dummy_values start_index
        "value#{start_index}"
      end

      ##
      # Path string for this segment
      # @return [String]
      def path_string
        "\#{#{position}}"
      end

      ##
      # A pattern template for this segment
      # @return [String]
      def pattern_template
        pattern
      end

      # @private
      def == other
        return false unless other.is_a? self.class

        (pattern == other.pattern && position == other.position)
      end
    end

    # A ResourceId segment in a path pattern.
    #  ResourceId segments can be simple, with one resource name
    #  or complex, with multiple resource names divided by separators
    #
    # @!attribure [r] type
    #   @return [String] The type of this segment
    # @!attribute [r] pattern
    #   @return [String] The pattern of the segment, for the positional segment it is also
    #     a pattern of its resource
    # @!attribute [r] resource_names
    #   @return [Array<String>] The resource names in this segment
    # @!attribute [r] resource_patterns
    #   @return [Array<String>] The resource patterns associated with
    #     the resource_names of this segment
    class ResourceIdSegment
      attr_reader :type, :pattern, :resource_names, :resource_patterns

      def initialize type, pattern, resource_names, resource_patterns = []
        @type              = type
        @pattern           = pattern
        @resource_names    = resource_names
        @resource_patterns = resource_patterns
      end

      ##
      # Whether the segment is positional
      # @return [Boolean]
      def positional?
        false
      end

      ##
      # Whether the segment provides a resource pattern
      # @return [Boolean]
      def resource_pattern?
        resource_patterns.any?
      end

      ##
      # Whether the segment provides a nontrivial resource pattern
      # @return [Boolean]
      def nontrivial_resource_pattern?
        resource_patterns.any? { |res_pattern| !res_pattern.match?(/^\*+$/) }
      end

      ##
      # Whether the segment provides arguments
      # @return [Boolean]
      def provides_arguments?
        true
      end

      ##
      # Names of the segment's arguments
      # @return [Array<String>]
      def arguments
        resource_names
      end

      ##
      # Returns a segment's pattern filled with dummy values
      #   names of the values are generated starting from the index provided
      # @param start_index [Integer] a starting index for dummy value generation
      # @return [String] a pattern filled with dummy values
      def expected_path_for_dummy_values start_index
        return "value#{start_index}" if type == :simple_resource_id

        resource_names.each_with_index.reduce pattern do |exp_path, (res_name, index)|
          exp_path.sub "{#{res_name}}", "value#{start_index + index}"
        end
      end

      ##
      # Path string for this segment
      # @return [String]
      def path_string
        type == :simple_resource_id ? "\#{#{resource_names[0]}}" : pattern.gsub("{", "\#{")
      end

      ##
      # A pattern template for this segment
      # @return [String]
      def pattern_template
        "*"
      end

      ##
      # Initialization helper to create a simple resource without a pattern
      # @param name [String] resource name
      # @return [ResourceIdSegment]
      def self.create_simple name
        ResourceIdSegment.new :simple_resource_id, "{#{name}}", [name]
      end

      # @private
      def == other
        return false unless other.is_a? self.class

        (type == other.type && pattern == other.pattern &&
          resource_names == other.resource_names &&
          resource_patterns == other.resource_patterns)
      end
    end

    ##
    # A CollectionId segment in a path template.
    #  CollectionId segments are basically string literals
    #
    # @!attribure [r] type
    #   @return [String] The type of this segment
    # @!attribute [r] pattern
    #   @return [String] The pattern of the segment, for the positional segment it is also
    #     a pattern of its resource
    class CollectionIdSegment
      attr_reader :type, :pattern

      def initialize pattern
        @type     = :collection_id
        @pattern  = pattern
      end

      ##
      # Whether the segment is positional
      # @return [Boolean]
      def positional?
        false
      end

      ##
      # Whether the segment provides a resource pattern
      # @return [Boolean]
      def resource_pattern?
        false
      end

      ##
      # Whether the segment provides a nontrivial resource pattern
      # @return [Boolean]
      def nontrivial_resource_pattern?
        false
      end

      ##
      # Whether the segment provides arguments
      # @return [Boolean]
      def provides_arguments?
        false
      end

      ##
      # Path string for this segment
      # @return [String]
      def path_string
        pattern
      end

      ##
      # A pattern template for this segment
      # @return [String]
      def pattern_template
        pattern
      end

      # @private
      def == other
        return false unless other.is_a? self.class

        (pattern == other.pattern)
      end
    end
  end
end