require "rest_client"
require "json"
require "cgi"
require "netrc"

module Shelly
  class Client
    require 'shelly/client/errors'
    require 'shelly/client/shellyapp'
    require 'shelly/client/tunnels'
    require 'shelly/client/users'
    require 'shelly/client/apps'
    require 'shelly/client/configs'
    require 'shelly/client/deployment_logs'
    require 'shelly/client/application_logs'
    require 'shelly/client/database_backups'
    require 'shelly/client/deploys'
    require 'shelly/client/ssh_keys'
    require 'shelly/client/organizations'
    require 'shelly/client/auth'
    require 'shelly/client/cert'

    def api_url
      ENV["SHELLY_URL"] || "https://api.shellycloud.com/apiv2"
    end

    def api_host
      URI.parse(api_url).host
    end

    def query(options = {})
      "?" + options.map { |k, v|
        URI.escape(k.to_s) + "=" + URI.escape(v.to_s) }.join("&")
    end

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

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

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

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

    def download_file(cloud, filename, url, progress_callback = nil)
      File.open(filename, "wb") do |out|
        process_response = lambda do |response|
          response.read_body do |chunk|
            out.write(chunk)
            progress_callback.call(chunk.size) if progress_callback
          end
        end

        options = {
          :url            => url,
          :method         => :get,
          :block_response => process_response,
          :headers => {:accept => "application/x-gzip"}
        }.merge(http_basic_auth_options)

        RestClient::Request.execute(options)
      end
    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
      if @email && @password
        {:user => @email, :password => @password}
      else
        basic_auth_from_netrc
      end
    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)
      body = JSON.parse(response.body) rescue JSON::ParserError && {}
      code = response.code
      if (400..599).include?(code)
        exception_class = case response.code
        when 401; UnauthorizedException
        when 403; ForbiddenException
        when 404; NotFoundException
        when 409; ConflictException
        when 412; GemVersionException
        when 422; ValidationException
        when 423; LockedException
        when 504; GatewayTimeoutException
        else; APIException
        end
        raise exception_class.new(body, code, response.headers[:x_request_id])
      end
      response.return!
      body
    end
  end
end