lib/radixir/config.ex

defmodule Radixir.Config do
  @moduledoc """
  Configuration helpers.
  """

  @type url :: String.t()
  @type username :: String.t()
  @type password :: String.t()
  @type error_message :: String.t()

  @doc """
  Fetches gateway api url.
  """
  @spec gateway_api_url() :: url | nil
  def gateway_api_url do
    :radixir
    |> Application.get_env(__MODULE__)
    |> Keyword.get(:gateway_api_url)
  end

  @doc """
  Fetches core api url.
  """
  @spec core_api_url() :: url | nil
  def core_api_url do
    :radixir
    |> Application.get_env(__MODULE__)
    |> Keyword.get(:core_api_url)
  end

  @doc """
  Fetches system api url.
  """
  @spec system_api_url() :: url | nil
  def system_api_url do
    :radixir
    |> Application.get_env(__MODULE__)
    |> Keyword.get(:system_api_url)
  end

  @doc """
  Fetches usernames.

  ## Note
  See `.envrc.example` for required format of usernames string.
  """
  @spec usernames() :: {:ok, list(username)} | {:error, error_message}
  def usernames() do
    :radixir
    |> Application.get_env(__MODULE__)
    |> Keyword.get(:usernames)
    |> process_usernames_passwords("no usernames")
  end

  @doc """
  Fetches passwords.

  ## Note
  See `.envrc.example` for required format of passwords string.
  """
  @spec passwords() :: {:ok, list(password)} | {:error, error_message}
  def passwords() do
    :radixir
    |> Application.get_env(__MODULE__)
    |> Keyword.get(:passwords)
    |> process_usernames_passwords("no passwords")
  end

  @doc """
  Specifies whether or not the testnet is being used.
  """
  @spec testnet?() :: boolean()
  def testnet? do
    :radixir
    |> Application.get_env(__MODULE__)
    |> Keyword.get(:testnet)
    |> case do
      "true" -> true
      _ -> false
    end
  end

  @doc """
  Fetches the username / password at specified index.

  ## Example

  If the following usernames and passwords are exported as follows:
  ```
  export USERNAMES='admin, superadmin, metrics'
  export PASSWORDS='funny cats very Jack 21!, harry Kack love h39! LW, monitor Kat darrel 23 Jack!'
  ```
  then `auth(0)` would return `{:ok, "admin", "funny cats very Jack 21!"}`
  """
  @spec auth(non_neg_integer()) :: {:ok, username, password} | {:error, error_message}
  def auth(index) do
    with {:ok, usernames} <- usernames(),
         {:ok, passwords} <- passwords(),
         {:ok, length} <- valid_length(usernames, passwords),
         {:ok, index} <- valid_index(length, index) do
      {:ok, Enum.at(usernames, index), Enum.at(passwords, index)}
    end
  end

  @doc """
  Fetches the network being used.

  ## Note
  The two possibilities are `stokenet` or `mainnet`.
  """
  @spec network() :: String.t()
  def network do
    if testnet?() do
      "stokenet"
    else
      "mainnet"
    end
  end

  defp process_usernames_passwords(nil, error_message), do: {:error, error_message}

  defp process_usernames_passwords(content, error_message) do
    String.split(content, ", ")
    |> case do
      [""] -> {:error, error_message}
      items -> {:ok, items}
    end
  end

  defp valid_length(usernames, passwords) do
    usernames_length = Enum.count(usernames)
    passwords_length = Enum.count(passwords)

    if usernames_length == passwords_length do
      {:ok, passwords_length}
    else
      {:error, "usernames and passwords length mismatch"}
    end
  end

  defp valid_index(length, index) do
    if index < length && index >= 0 do
      {:ok, index}
    else
      {:error, "invalid index for accessing usernames and passwords"}
    end
  end
end