require 'rest-client'
require 'addressable/uri'
require 'json'
require 'silver_spurs/knife_interface'
require 'silver_spurs/client/exceptions'
require 'silver_spurs/client/bootstrap_run'
require 'silver_spurs/client/chef_output'

module SilverSpurs
  class Client
    def initialize(host_url, options={})
      @host_url = host_url
      @timeout = options[:timeout] || 2 * 60
    end

    def start_bootstrap(ip, node_name, options = {})
      params = extract_extra_params(options).merge({:node_name => node_name})
      payload = parameterize_hash params
      headers = {:accept => :json, :content_type=> 'application/x-www-form-urlencoded'}

      response = gracefully_handle_rest_call do
        spur_host["bootstrap/#{ip}"].put(payload, headers, &method(:dont_redirect_for_303))
      end
      
      throw ClientException.new("unexpected response", response) unless response.code == 303

      BootstrapRun.new(response.headers[:location], :timeout => @timeout)
    end

    def start_chef_run(host_name, runlist = [])
      response = gracefully_handle_rest_call do
        spur_host["kick/#{host_name}"].post({ :run => runlist })
      end
      ChefOutput.new JSON.parse(response)
    end

    def set_node_attributes(host_name, attributes = {})
      headers = { :accept => :json, :content_type => 'application/json' }
      gracefully_handle_rest_call do
        spur_host["attributes/#{host_name}"].put({ :attributes => attributes }.to_json, headers)
      end
    end

    private

    def gracefully_handle_rest_call(&rest_call)
      begin
        response = rest_call.call
      rescue RestClient::ResourceNotFound => ex
        raise ClientException.new("the host name was not found", ex.response)
      rescue Exception => ex
        raise ClientException.new("an unanticipated error occured: #{ex.message}", ex.backtrace)
      end

      response
    end
    
    def parameterize_hash(param_hash)
      uri = Addressable::URI.new
      uri.query_values = param_hash
      uri.query
    end
    
    def extract_extra_params(options)
      supported_arguments = KnifeInterface.supported_arguments
      options.select {|k,v| supported_arguments.include? k}  
    end

    def spur_host
      RestClient::Resource.new(@host_url, :timeout => @timeout)
    end

    def dont_redirect_for_303(response, origin, orig_result, &block)
      if response.code == 303
        response
      else
        response.return! &block
      end
    end
                
  end
end
