require "rest_client"
require "json"

module Shelly
  class Client
    class UnauthorizedException < Exception; end
    class UnsupportedResponseException < Exception; end
    class APIError < Exception
      def initialize(response)
        @response = response
      end

      def message
        @response["message"]
      end

      def errors
        @response["errors"]
      end
    end

    def initialize(email = nil, password = nil, options = {})
      @email = email
      @password = password
      @options = options
    end

    def api_url
      @options["shelly_url"] || ENV["SHELLY_URL"] || "https://admin.winniecloud.com/apiv2"
    end

    def register_user(email, password, ssh_key)
      post("/users", :user => {:email => email, :password => password, :ssh_key => ssh_key})
    end

    def token
      get("/token")
    end

    def create_app(attributes)
      post("/apps", :app => attributes)
    end

    def post(path, params = {})
      request(path, :post, params)
    end

    def get(path)
      request(path, :get)
    end

    def request(path, method, params = {})
      options = request_parameters(path, method, params)
      RestClient::Request.execute(options) do |response, request|
        process_response(response)
      end
    end

    def headers
      {:accept          => :json,
       :content_type    => :json,
       "shelly-version" => Shelly::VERSION}
    end

    def http_basic_auth_options
      @email ? {:user => @email, :password => @password} : {}
    end

    def request_parameters(path, method, params = {})
      {:method   => method,
       :url      => "#{api_url}#{path}",
       :headers  => headers,
       :payload  => params.to_json
      }.merge(http_basic_auth_options)
    end

    def process_response(response)
      raise UnauthorizedException.new if response.code == 302
      if [404, 422, 500].include?(response.code)
        error_details = JSON.parse(response.body)
        raise APIError.new(error_details)
      end

      begin
        response.return!
        JSON.parse(response.body)
      rescue RestClient::RequestFailed => e
        raise UnauthorizedException.new if e.http_code == 406
        raise UnsupportedResponseException.new(e)
      end
    end
  end
end