# frozen_string_literal: true

# Performs regexp replacement on a string or array of strings.
Puppet::Functions.create_function(:regsubst) do
  # @param target [String]
  #      The string or array of strings to operate on.  If an array, the replacement will be
  #      performed on each of the elements in the array, and the return value will be an array.
  # @param pattern [String, Regexp, Type[Regexp]]
  #      The regular expression matching the target string.  If you want it anchored at the start
  #      and or end of the string, you must do that with ^ and $ yourself.
  # @param replacement [String, Hash[String, String]]
  #      Replacement string. Can contain backreferences to what was matched using \\0 (whole match),
  #      \\1 (first set of parentheses), and so on.
  #      If the second argument is a Hash, and the matched text is one of its keys, the corresponding value is the replacement string.
  # @param flags [Optional[Pattern[/^[GEIM]*$/]], Pattern[/^G?$/]]
  #      Optional. String of single letter flags for how the regexp is interpreted (E, I, and M cannot be used
  #      if pattern is a precompiled regexp):
  #        - *E*         Extended regexps
  #        - *I*         Ignore case in regexps
  #        - *M*         Multiline regexps
  #        - *G*         Global replacement; all occurrences of the regexp in each target string will be replaced.  Without this, only the first occurrence will be replaced.
  # @param encoding [Enum['N','E','S','U']]
  #      Optional. How to handle multibyte characters when compiling the regexp (must not be used when pattern is a
  #      precompiled regexp). A single-character string with the following values:
  #        - *N*         None
  #        - *E*         EUC
  #        - *S*         SJIS
  #        - *U*         UTF-8
  # @return [Array[String], String] The result of the substitution. Result type is the same as for the target parameter.
  # @example Get the third octet from the node's IP address:
  #   ```puppet
  #   $i3 = regsubst($ipaddress,'^(\\d+)\\.(\\d+)\\.(\\d+)\\.(\\d+)$','\\3')
  #   ```
  dispatch :regsubst_string do
    param          'Variant[Array[String],String]',       :target
    param          'String',                              :pattern
    param          'Variant[String,Hash[String,String]]', :replacement
    optional_param 'Optional[Pattern[/^[GEIM]*$/]]',      :flags
    optional_param "Enum['N','E','S','U']",               :encoding
  end

  # @param target [String, Array[String]]
  #      The string or array of strings to operate on.  If an array, the replacement will be
  #      performed on each of the elements in the array, and the return value will be an array.
  # @param pattern [Regexp, Type[Regexp]]
  #      The regular expression matching the target string.  If you want it anchored at the start
  #      and or end of the string, you must do that with ^ and $ yourself.
  # @param replacement [String, Hash[String, String]]
  #      Replacement string. Can contain backreferences to what was matched using \\0 (whole match),
  #      \\1 (first set of parentheses), and so on.
  #      If the second argument is a Hash, and the matched text is one of its keys, the corresponding value is the replacement string.
  # @param flags [Optional[Pattern[/^[GEIM]*$/]], Pattern[/^G?$/]]
  #      Optional. String of single letter flags for how the regexp is interpreted (E, I, and M cannot be used
  #      if pattern is a precompiled regexp):
  #        - *E*         Extended regexps
  #        - *I*         Ignore case in regexps
  #        - *M*         Multiline regexps
  #        - *G*         Global replacement; all occurrences of the regexp in each target string will be replaced.  Without this, only the first occurrence will be replaced.
  # @param encoding [Enum['N','E','S','U']]
  #      Optional. How to handle multibyte characters when compiling the regexp (must not be used when pattern is a
  #      precompiled regexp). A single-character string with the following values:
  #        - *N*         None
  #        - *E*         EUC
  #        - *S*         SJIS
  #        - *U*         UTF-8
  # @return [Array[String], String] The result of the substitution. Result type is the same as for the target parameter.
  # @example Put angle brackets around each octet in the node's IP address:
  #   ```puppet
  #   $x = regsubst($ipaddress, /([0-9]+)/, '<\\1>', 'G')
  #   ```
  dispatch :regsubst_regexp do
    param          'Variant[Array[String],String]',       :target
    param          'Variant[Regexp,Type[Regexp]]',        :pattern
    param          'Variant[String,Hash[String,String]]', :replacement
    optional_param 'Pattern[/^G?$/]',                     :flags
  end

  def regsubst_string(target, pattern, replacement, flags = nil, encoding = nil)
    re_flags = 0
    operation = :sub
    if !flags.nil?
      flags.split(//).each do |f|
        case f
        when 'G' then operation = :gsub
        when 'E' then re_flags |= Regexp::EXTENDED
        when 'I' then re_flags |= Regexp::IGNORECASE
        when 'M' then re_flags |= Regexp::MULTILINE
        end
      end
    end
    inner_regsubst(target, Regexp.compile(pattern, re_flags, encoding), replacement, operation)
  end

  def regsubst_regexp(target, pattern, replacement, flags = nil)
    pattern = (pattern.pattern || '') if pattern.is_a?(Puppet::Pops::Types::PRegexpType)
    inner_regsubst(target, pattern, replacement, flags == 'G' ? :gsub : :sub)
  end

  def inner_regsubst(target, re, replacement, op)
    target.respond_to?(op) ? target.send(op, re, replacement) : target.collect { |e| e.send(op, re, replacement) }
  end
  private :inner_regsubst
end
