defmodule Dnsimple.Oauth do
  @moduledoc """
  Provides functions to authenticate through the
  [OAuth web application flow](https://developer.dnsimple.com/v2/oauth/).

  See:
  - https://developer.dnsimple.com/v2/oauth/
  - https://developer.dnsimple.com/v2/#authentication
  """
  @moduledoc section: :api

  alias Dnsimple.Client
  alias Dnsimple.Response
  alias Dnsimple.OauthToken

  @doc """
  Returns the URL to start the OAuth dance.

  See:
  - https://developer.dnsimple.com/v2/oauth/#step-1---authorization

  ## Examples:

      client = %Dnsimple.Client{access_token: "a1b2c3d4"}
      url = Dnsimple.Oauth.authorize_url(client, client_id = "1z2y3x", state: "12345678")

  """
  @spec authorize_url(Client.t, String.t, Keyword.t) :: String.t
  def authorize_url(client, client_id, query \\ []) do
    host  = String.replace(client.base_url, "https://api.", "")
    query = Keyword.merge([response_type: "code", client_id: client_id], query)

    URI.to_string(%URI{scheme: "https", host: host, path: "/oauth/authorize", query: URI.encode_query(query)})
  end


  @doc """
  Returns the access token for a given authorization code.

  See:
  - https://developer.dnsimple.com/v2/oauth/#step-2---access-token

  ## Examples:

      client = %Dnsimple.Client{access_token: "a1b2c3d4"}
      {:ok, response} = Dnsimple.Oauth.exchange_authorization_for_token(client, %{
        code: "authorization_code",
        state: "12345678",
        client_id: "1z2y3x",
        client_secret: "xXxXxX",
      })

  """
  @spec exchange_authorization_for_token(Client.t, map(), keyword()) :: {:ok|:error, String.t}
  def exchange_authorization_for_token(client, attributes, options \\ []) do
    url        = Client.versioned("/oauth/access_token")
    attributes = Map.merge(attributes, %{grant_type: "authorization_code"})

    Client.post(client, url, attributes, options)
    |> Response.parse(%OauthToken{})
  end

end
