defmodule Algora.Restream do
  def authorize_url(state) do
    query =
      URI.encode_query(
        client_id: client_id(),
        state: state,
        response_type: "code",
        redirect_uri: redirect_uri()
      )

    "https://api.restream.io/login?#{query}"
  end

  def websocket_url(token), do: "wss://chat.api.restream.io/ws?accessToken=#{token}"

  def exchange_access_token(opts) do
    code = Keyword.fetch!(opts, :code)
    state = Keyword.fetch!(opts, :state)

    state
    |> fetch_exchange_response(code)
    |> fetch_user_info()
  end

  defp fetch_exchange_response(_state, code) do
    body =
      URI.encode_query(%{
        grant_type: "authorization_code",
        redirect_uri: redirect_uri(),
        code: code
      })

    headers = [
      {"Content-Type", "application/x-www-form-urlencoded"},
      {"Authorization", "Basic " <> Base.encode64("#{client_id()}:#{secret()}")}
    ]

    resp = HTTPoison.post("https://api.restream.io/oauth/token", body, headers)

    with {:ok, %HTTPoison.Response{status_code: 200, body: body}} <- resp,
         %{"access_token" => token, "refresh_token" => refresh_token} <- Jason.decode!(body) do
      {:ok, %{token: token, refresh_token: refresh_token}}
    else
      {:error, %HTTPoison.Error{reason: reason}} -> {:error, reason}
      %{} = resp -> {:error, {:bad_response, resp}}
    end
  end

  defp fetch_user_info({:error, _reason} = error), do: error

  defp fetch_user_info({:ok, %{token: token} = tokens}) do
    headers = [{"Authorization", "Bearer #{token}"}]

    case HTTPoison.get("https://api.restream.io/v2/user/profile", headers) do
      {:ok, %HTTPoison.Response{status_code: 200, body: body}} ->
        {:ok, %{info: Jason.decode!(body), tokens: tokens}}

      {:ok, %HTTPoison.Response{status_code: status_code, body: body}} ->
        {:error, {status_code, body}}

      {:error, %HTTPoison.Error{reason: reason}} ->
        {:error, reason}
    end
  end

  def refresh_access_token(refresh_token) do
    body =
      URI.encode_query(%{
        grant_type: "refresh_token",
        refresh_token: refresh_token
      })

    headers = [
      {"Content-Type", "application/x-www-form-urlencoded"},
      {"Authorization", "Basic " <> Base.encode64("#{client_id()}:#{secret()}")}
    ]

    resp = HTTPoison.post("https://api.restream.io/oauth/token", body, headers)

    with {:ok, %HTTPoison.Response{status_code: 200, body: body}} <- resp,
         %{"access_token" => token, "refresh_token" => refresh_token} <- Jason.decode!(body) do
      {:ok, %{token: token, refresh_token: refresh_token}}
    else
      {:error, %HTTPoison.Error{reason: reason}} -> {:error, reason}
      %{} = resp -> {:error, {:bad_response, resp}}
    end
  end

  defp client_id, do: Algora.config([:restream, :client_id])
  defp secret, do: Algora.config([:restream, :client_secret])
  defp redirect_uri, do: "#{AlgoraWeb.Endpoint.url()}/oauth/callbacks/restream"
end
