# Builds node name from mask. Mask could be passed in server's +cid:node-name-mask+ tag,
# default mask is used otherwise. Mask is a string with possible several variables inserted, e.g.
# ':project/:env/:instanceid/:instancename/:increment-group1:'
# Variables meanings:
# - :project is replaced with project name (given in constructor)
# - :env is replaced with env name (given in constructor)
# - :time is replaced with current timestamp
# - :instanceid is replaced with provider instance id (fetched from server info)
# - :instancename is replaced with value of Name tag (fetched from server info)
# - :increment-groupname: is replaced with incremented number tied to group name. There could be several groups in one stack.
# P.S. Colons are used instead of dollar signs, because stacks don't support dollar signs in tags (unlike EC2 instances),
# but it's convenient to set mask tag directly to a stack (not in template): you set tag once and it propagates to all instances.

class Devops::Executor::StackExecutor
  class ChefNodeNameBuilder
    # We need to ensure that node name is uniq by default. We can't use :time to provide uniqueness because
    # several servers are persisting at once on stack creating at it's likely that Time.now.to_i will give similar values
    # to different servers. So (by default) we should use :instanceid.
    DEFAULT_MASK = ':project-:env-:instanceid'

    # @param attrs [Hash] should contain
    #   :provider_server_info
    #   :project_id
    #   :env_id
    def initialize(attrs)
      @server_info = attrs[:provider_server_info]
      @project, @env = attrs[:project_id], attrs[:env_id]
      @mask = @server_info['tags']['cid:node-name-mask'] if @server_info['tags']
      @mask ||= DEFAULT_MASK
    end


    # @param incrementers_values [Hash] is a hash in which key is name of a variable
    # and value is last substituted number for that var.
    # This method modifies incrementers_values, updating values for substituted variables.
    #
    # Examples (assume mask is set to +':project-master-:increment-group1:'+):
    #   incremeters_values = {}
    #   builder.build_node_name!(incremeters_values) # returns 'mpda-master-01'
    #   puts incremeters_values # {'group1' => 1}
    #   builder.build_node_name!(incremeters_values) # returns 'mpda-master-02'
    #   puts incremeters_values # {'group1' => 2}
    def build_node_name!(incrementers_values)
      result = @mask.dup
      replace_variables!(result)
      replace_incrementers!(result, incrementers_values)
      result
    end

    private

    def replace_variables!(result)
      result.gsub!(':project', @project)
      result.gsub!(':env', @env)
      result.gsub!(':instanceid', @server_info['id'])
      result.gsub!(':instancename', @server_info['tags']['Name'] || '')
      result.gsub!(':time', Time.now.to_i.to_s)
    end

    def replace_incrementers!(result, incrementers_values)
      groupname_regexp = /(?<=:increment-)\w+(?=:)/
      result.gsub!(/:increment-\w+:/) do |incrementer|
        group_name = groupname_regexp.match(incrementer)[0]
        prev_value = incrementers_values[group_name] || 0
        current_value = prev_value + 1
        incrementers_values[group_name] = current_value
        current_value.to_s.rjust(2, '0')
      end
    end
  end
end