require "json"

class KnifeCommands

  attr_accessor :config

  def initialize config
    self.config = config
  end

  def chef_node_list
    knife("node list")[0].split.map{|c| c.strip}
  end

  def chef_client_list
    knife("client list")[0].split.map{|c| c.strip}
  end

  def chef_node_delete name
    o = knife("node delete #{name} -y")[0]
    (o.nil? ? o : o.strip)
  end

  def chef_client_delete name
    o = knife("client delete #{name} -y")[0]
    (o.nil? ? o : o.strip)
  end

  def tags_list name
    knife("tag list #{name}")[0].split.map{|c| c.strip}
  end

  def tags_create name, tagsStr
    knife("tag create #{name} #{tagsStr}")
  end

  def tags_delete name, tagsStr
    knife("tag delete #{name} #{tagsStr}")
  end

  # extracted from server_executor.rb
  def swap_tags(name, from, to)
    tags_delete(name, from)
    result = tags_create(name, to)
    !!result[1]
  end

  def create_role role_name, project, env
    file = "/tmp/new_role.json"
    File.open(file, "w") do |f|
      f.puts <<-EOH
{
  "name" : "#{role_name}",
  "description": "",
  "json_class": "Chef::Role",
  "default_attributes": {
    "project": "#{project}",
    "env": "#{env}",
    "deploy_env": "#{env}"
  },
  "override_attributes": {},
  "chef_type": "role",
  "run_list": [],
  "env_run_lists": {}
}
EOH
    end
    out, res = knife("role from file #{file}")
    raise "Cannot create role '#{role_name}': #{out}" unless res
    true
  end

  def roles(chef_env=nil)
    o, s = knife("role list --format json")
    return (s ? JSON.parse(o) : nil)
  end

  def role_name project_name, env_id, category_id
    separator = DevopsConfig.config[:role_separator] || '_'
    [project_name, env_id, category_id].join(separator)
  end

  def knife cmd
    cmd = "bundle exec knife #{cmd} -c #{self.config} 2>&1"
    DevopsLogger.logger.info("Going to invoke command: #{cmd}")
    o = `#{cmd}`
    return o, $?.success?
  end

  def ssh_options cmd, host, user, cert
    ["-m", "-x", user, "-i", cert, "--no-host-key-verify", host, "'#{(user == "root" ? cmd : "sudo #{cmd}")}'"]
  end

  def ssh_stream out, cmd, host, user, cert
    knife_cmd = "bundle exec knife ssh -c #{self.config} #{ssh_options(cmd, host, user, cert).join(" ")}"
    out << "\nExecuting '#{knife_cmd}' \n\n"
    out.flush if out.respond_to?(:flush)
    status = 2
    lline = nil
    IO.popen(knife_cmd + " 2>&1") do |o|
      while line = o.gets do
        out << line
        lline = line
        out.flush if out.respond_to?(:flush)
      end
      o.close
    end
    return lline
  end

  def knife_bootstrap out, ip, options
    knife_stream(out, "bootstrap", options + [ ip ])
  end

  def knife_stream out, cmd, options=[]
    knife_cmd = "bundle exec knife #{cmd} #{options.join(" ")} -c #{self.config}"
    out << "\nExecuting '#{knife_cmd}' \n\n"
    out.flush if out.respond_to?(:flush)
    status = nil
    IO.popen(knife_cmd + " 2>&1") do |o|
      while line = o.gets do
        out << line
        out.flush if out.respond_to?(:flush)
      end
      o.close
      status = $?.to_i
    end
    return status
  end

  def set_run_list node, list
    knife("node run_list set #{node} '#{list.join("','")}'")
  end

private
  def self.get_config
    File.join(ENV["HOME"], ".chef", "knife.rb")
  end
end
