#encoding: utf-8
require 'vagabond/vagabond'
require 'mixlib/cli'
require 'digest/md5'

module Vagabond

  class Server < Vagabond

    class << self
      def basename
        'vagabond server'
      end
    end

    self.class_exec(false, &Vagabond::COMMANDS)

    def initialize(*args)
      super
      @name = 'server'
      @base_template = 'ubuntu_1204' # TODO: Make this dynamic
      setup('status')
    end

    desc 'server stop', 'Stops the local Chef server'
    def stop
      if(lxc.exists?)
        if(lxc.running?)
          ui.info 'Shutting down Chef server container...'
          lxc.shutdown
          ui.info 'Chef server container shut down!'
        else
          ui.error 'Chef server container not currently running'
        end
      else
        ui.error 'Chef server container has not been created'
      end
    end

    desc 'auto_upload', 'Uploads all assets'
    def auto_upload
      ui.info 'Auto uploading all assets to local Chef server...'
      upload_roles
      upload_databags
      upload_environments
      upload_cookbooks
      ui.info ui.color('  -> All assets uploaded!', :green)
    end

    desc 'upload_roles', 'Upload all roles'
    def upload_roles
      am_uploading('roles') do
        if(File.directory?(File.join(base_dir, 'roles')))
          %w(rb json js).each do |ext|
            next if Dir.glob(File.join(base_dir, "roles", "*.#{ext}")).size == 0
            cmd = knife_command("role from file #{File.join(base_dir, "roles/*.#{ext}")}")
            cmd.run_command
            cmd.error!
          end
        end
      end
    end

    desc 'upload_databags', 'Upload all data bags'
    def upload_databags
      am_uploading('data bags') do
        if(File.directory?(File.join(base_dir, 'data_bags')))
          Dir.glob(File.join(base_dir, "data_bags/*")).each do |b|
            next if %w(. ..).include?(b) || !File.directory?(b)
            coms = [
              "data bag create #{File.basename(b)}",
              "data bag from file #{File.basename(b)} --all"
            ].each do |com|
              cmd = knife_command(com)
              cmd.run_command
              cmd.error!
            end
          end
        end
      end
    end

    desc 'upload_environments', 'Upload all environments'
    def upload_environments
      am_uploading('environments') do
        if(File.directory?(File.join(base_dir, 'environments')))
          %w(rb json js).each do |ext|
            next if Dir.glob(File.join(base_dir, "environments", "*.#{ext}")).size == 0
            cmd = knife_command("environment from file #{File.join(base_dir, "environments/*.#{ext}")}")
            cmd.run_command
            cmd.error!
          end
        end
      end
    end

    desc 'upload_cookbooks', 'Upload all cookbooks'
    def upload_cookbooks
      am_uploading('cookbooks') do
        if(vagabondfile[:local_chef_server][:librarian])
          librarian_upload
        elsif(vagabondfile[:local_chef_server][:berkshelf])
          berks_upload
        else
          if(File.exists?(File.join(vagabondfile.directory, 'Cheffile')))
            librarian_upload
          elsif(File.exists?(File.join(vagabondfile.directory, 'Berksfile')))
            berks_upload
          else
            raw_upload
          end
        end
      end
    end

    private

    def validate!
    end

    def setup(action, name=nil, *args)
      super(action, 'server', *args)
    end

    def am_uploading(thing)
      ui.info "#{ui.color('Local chef server:', :bold)} Uploading #{ui.color(thing, :green)}"
      yield
      ui.info ui.color("  -> UPLOADED #{thing.upcase}", :green)
    end

    def server_base
      if(vagabondfile[:local_chef_server][:zero] || options[:force_zero])
        base = 'vb-zero-server'
      else
        version = vagabondfile[:local_chef_server][:version]
        unless(version)
          # TODO: Bad magic. Make configurable!
          matches = Dir.new('/var/lib/lxc').find_all{|i| i.match(/^vb-server-\d+_\d+_\d+$/)}
          matches.map!{|i| i.sub('vb-server-', '').gsub('_', '.')}
          version = matches.sort{|x,y| Gem::Version.new(x) <=> Gem::Version.new(y)}.last
        end
        base = "vb-server-#{version.gsub('.', '_')}"
        unless(Lxc.new(base).exists?)
          raise VagabondError::ErchefBaseMissing.new("Required base container is missing: #{base}")
        end
      end
      base
    end

    def do_create
      config = Mash.new
      # TODO: Pull custom IP option if provided
      config[:daemon] = true
      config[:original] = server_base

      ephemeral = Lxc::Ephemeral.new(config)
      e_name = ephemeral.name
      internal_config[mappings_key][name] = e_name
      ephemeral.start!(:fork)
      @lxc = Lxc.new(e_name)
      @lxc.wait_for_state(:running)
    end

    def do_provision
      if(vagabondfile[:local_chef_server][:zero] || options[:force_zero])
        ui.info ui.color('  -> Bootstrapping chef-zero...', :cyan)
        tem_file = File.expand_path(File.join(File.dirname(__FILE__), 'bootstraps/server-zero.erb'))
        knife_config :server_url => "http://#{lxc.container_ip(20, true)}"
      else
        ui.info ui.color('  -> Bootstrapping erchef...', :cyan)
        tem_file = File.expand_path(File.join(File.dirname(__FILE__), 'bootstraps/server.erb'))
        knife_config :server_url => "https://#{lxc.container_ip(20, true)}"
      end
      # Scrub before bootstrap
      direct_container_command('rm -rf /var/chef-host/cookbooks')
      # And bootstrap
      cmd = knife_command(
        "bootstrap #{lxc.container_ip(10, true)} --sync-directory " <<
        "\"#{internal_config.cookbook_path}:/var/chef-host/cookbooks\" --template-file " <<
        "#{tem_file} -i #{Settings[:ssh_key]}"
      )
      cmd.run_command
      cmd.error!
      ui.info ui.color('  -> COMPLETE', :green)
      auto_upload if vagabondfile[:local_chef_server][:auto_upload]
    end

    def berks_upload
      ui.info 'Cookbooks being uploaded via berks'
      if(vagabondfile[:local_chef_server][:berkshelf].is_a?(Hash))
        berks_opts = vagabondfile[:local_chef_server][:berkshelf][:options]
        berks_path = vagabondfile[:local_chef_server][:berkshelf][:path]
      end
      berk_uploader = Uploader::Berkshelf.new(
        vagabondfile, vagabondfile.build_private_store, options.merge(
          :ui => ui,
          :berksfile => File.join(vagabondfile.directory, berks_path || 'Berksfile'),
          :chef_server_url => options[:knife_opts].to_s.split(' ').last,
          :berks_opts => berks_opts
        )
      )
      berk_uploader.upload
    end

    def librarian_upload
      ui.info 'Cookbooks being uploaded with librarian'
      librarian_uploader = Uploader::Librarian.new(
        vagabondfile, vagabondfile.build_private_store, options.merge(
          :ui => ui,
          :cheffile => File.join(vagabondfile.directory, 'Cheffile')
        )
      )
      librarian_uploader.upload
    end

    def raw_upload
      ui.info 'Cookbooks being uploaded via knife'
      knife_uploader = Uploader::Knife.new(vagabondfile, vagabondfile.directory, options.merge(:ui => ui))
      knife_uploader.upload
    end

  end
end
