require 'yaml'

# Cross-platform way of finding an executable in the $PATH.
def which(cmd)
  exts = ENV['PATHEXT'] ? ENV['PATHEXT'].split(';') : ['']
  ENV['PATH'].split(File::PATH_SEPARATOR).each do |path|
    exts.each do |ext|
      exe = File.join(path, "#{cmd}#{ext}")
      return exe if File.executable?(exe) && !File.directory?(exe)
    end
  end
  nil
end

# Recursively walk an tree and run provided block on each value found.
def walk(obj, &fn)
  if obj.is_a?(Array)
    obj.map { |value| walk(value, &fn) }
  elsif obj.is_a?(Hash)
    obj.each_pair { |key, value| obj[key] = walk(value, &fn) }
  else
    obj = yield(obj)
  end
end

# Resolve jinja variables in hash.
def resolve_jinja_variables(vconfig)
  walk(vconfig) do |value|
    while value.is_a?(String) && value.match(/{{ .* }}/)
      value = value.gsub(/{{ (.*?) }}/) { vconfig[Regexp.last_match(1)] }
    end
    value
  end
end

# Return the combined configuration content all files provided.
def load_config(files)
  vconfig = {}
  files.each do |config_file|
    if File.exist?(config_file)
      optional_config = YAML.load_file(config_file)
      vconfig.merge!(optional_config) if optional_config
    end
  end
  resolve_jinja_variables(vconfig)
end

# Return the path to the ansible-playbook executable.
def ansible_bin
  @ansible_bin ||= which('ansible-playbook')
end

# Return the ansible version parsed from running the executable path provided.
def ansible_version
  /^[^\s]+ (.+)$/.match(`#{ansible_bin} --version`) { |match| return match[1] }
end

# Require that if installed, the ansible version meets the requirements.
def require_ansible_version(requirement)
  return unless ansible_bin
  req = Gem::Requirement.new(requirement)
  return if req.satisfied_by?(Gem::Version.new(ansible_version))
  raise_message "You must install an Ansible version #{requirement} to use this version of Drupal VM."
end

def raise_message(msg)
  raise Vagrant::Errors::VagrantError.new, msg
end

# Return which Vagrant provisioner to use.
def vagrant_provisioner
  ansible_bin ? :ansible : :ansible_local
end

def ensure_plugins(plugins)
  logger = Vagrant::UI::Colored.new
  installed = false

  plugins.each do |plugin|
    plugin_name = plugin['name']
    manager = Vagrant::Plugin::Manager.instance

    next if manager.installed_plugins.key?(plugin_name)

    logger.warn("Installing plugin #{plugin_name}")

    manager.install_plugin(
      plugin_name,
      sources: plugin.fetch('source', %w[https://rubygems.org/ https://gems.hashicorp.com/]),
      version: plugin['version']
    )

    installed = true
  end

  return unless installed

  logger.warn('`vagrant up` must be re-run now that plugins are installed')
  exit
end

def get_apache_vhosts(vhosts)
  aliases = []
  vhosts.each do |host|
    aliases.push(host['servername'])
    aliases.concat(host['serveralias'].split) if host['serveralias']
  end
  aliases
end

def get_nginx_vhosts(vhosts)
  aliases = []
  vhosts.each do |host|
    aliases.push(host['server_name'])
    aliases.concat(host['server_name_redirect'].split) if host['server_name_redirect']
  end
  aliases
end

# Return a list of all virtualhost server names and aliases from a config hash.
def get_vhost_aliases(vconfig)
  if vconfig['drupalvm_webserver'] == 'apache'
    aliases = get_apache_vhosts(vconfig['apache_vhosts'])
  else
    # @todo shim for `nginx_hosts`.
    aliases = get_nginx_vhosts(vconfig.fetch('nginx_hosts', vconfig['nginx_vhosts']))
  end
  aliases = aliases.uniq - [vconfig['vagrant_ip']]
  # Remove wildcard subdomains.
  aliases.delete_if { |vhost| vhost.include?('*') }
end

# Return a default post_up_message.
def get_default_post_up_message(vconfig)
  'Your Drupal VM Vagrant box is ready to use!'\
    "\n* Visit the dashboard for an overview of your site: http://dashboard.#{vconfig['vagrant_hostname']} (or http://#{vconfig['vagrant_ip']})"\
    "\n* You can SSH into your machine with `vagrant ssh`."\
    "\n* Find out more in the Drupal VM documentation at http://docs.drupalvm.com"
end