require 'open-uri'
require 'socket'
require 'yaml'

class Runpuppet
  def self.with_lock(lock_file)
    recently_locked = (File.exists?(lock_file) and File.mtime(lock_file) > Time.now - 15*60)

    if recently_locked
      puts "can not run, lockfile #{lock_file} exists"
    else
      begin
        `touch #{lock_file}`
        yield
      ensure
        `rm #{lock_file}`
      end
    end
  end

  # simplified copy of rake`s sh
  def self.sh(cmd)
    puts cmd
    IO.popen(cmd) do |pipe|
      while str = pipe.gets
        puts str
      end
    end
    $?.success?
  end


  def self.get(path)
    path += (path.include?('?') ? '&' : '?')
    path += "hostname=#{Socket.gethostname}&ip=#{Config.local_ip}"
    open(Config.puppet_controller_url + path, Config.puppet_controller_auth) rescue puts "WARNING: no connection to puppet controller!"
  end


  def self.run(options={})
    with_lock(Config.lock_file) do
      time = Time.now.utc.strftime("%Y-%m-%d %H:%M:%S")

      todo, branch = get("/puppet/run").read.split('-') rescue []
      if todo == 'run' or !options[:try] # try allows not running
        get '/puppet/status?status=started'
        branch ||= Config.default_branch
        cmd = Config.command.gsub('@@branch@@', branch)
        puts "#{time}: run #{branch}"

        if sh(cmd)
          get '/puppet/status?status=finished'
        else
          get '/puppet/status?status=error'
          exit 2
        end
      else
        puts "#{time}: nothing to do"
      end
    end
  end


  class Config
    def self.config
      home = File.expand_path('~')
      ["#{home}/.runpuppet.yml", '/etc/runpuppet.yml'].each do |file|
        return YAML.load(File.read(file)) if File.exist?(file)
      end
      raise "No runpuppet.yml found in /etc or #{home}"
    end

    def self.puppet_controller_url
      config['puppet_controller_url'].sub(%r{/$},'')
    end

    def self.puppet_controller_auth
      extract_auth_from_url!(config['puppet_controller_url'])
    end

    def self.extract_auth_from_url!(url)
      url.sub!(%r{//(.*?):(.*?)@}, '//')
      auth = [$1, $2].compact
      auth.empty? ? nil : auth
    end

    def self.lock_file
      config['lock_file'] || '/tmp/runpuppet.lock'
    end

    def self.command
      config['command'] ||  "cd /etc/puppet/repo && git fetch && git checkout -f origin/@@branch@@ && puppet -v --debug --logdest console --modulepath /etc/puppet/repo/modules /etc/puppet/repo/manifests/site.pp"
    end

    def self.default_branch
      config['defaulf_branch'] || 'master'
    end

    def self.local_ip
      %x(ifconfig)[/192.168.\d+.\d+/] || config['local_ip']
    end
  end
end