defmodule Wireguardex do
  @moduledoc """
  Wireguardex is an Elixir library for configuring [WireGuard](https://wireguard.com) interfaces. It uses
  [Rust](https://rust-lang.org) NIFs for performance and safety.

  This is the main module, providing the API for interface configuration and
  utilities such as key generation.
  """
  mix_config = Mix.Project.config()
  version = mix_config[:version]
  github_url = mix_config[:package][:links]["GitHub"]

  use RustlerPrecompiled,
    otp_app: :wireguardex,
    crate: "wireguard_nif",
    base_url: "#{github_url}/releases/download/v#{version}",
    force_build: System.get_env("WIREGUARDNIF_BUILD") in ["1", "true"],
    version: version,
    targets: [
      "aarch64-unknown-linux-gnu",
      "x86_64-unknown-linux-gnu",
      "aarch64-unknown-linux-musl",
      "x86_64-unknown-linux-musl",
      "aarch64-apple-darwin",
      "x86_64-apple-darwin"
    ]

  @type device :: Wireguardex.Device.t()
  @type device_config :: Wireguardex.DeviceConfig.t()
  @type peer_config :: Wireguardex.PeerConfig.t()
  @type peer_stats :: Wireguardex.PeerStats.t()

  @type name :: String.t()
  @type key :: String.t()

  @doc """
  Get a list of interface names of WireGuard devices.

  Returns `{:ok, [...]}` if successful. `{:error, error_info}` will be returned
  if listing device interface names fails.
  """
  @spec list_devices() :: {:ok, [device()]} | {:error, String.t()}
  def list_devices, do: error()

  @doc """
  Get a `Device` by its interface name.

  Returns `{:ok, Device}` if successful. `{:error, error_info}` will be returned
  if getting the device fails.
  """
  @spec get_device(name()) :: {:ok, device()} | {:error, String.t()}
  def get_device(_name), do: error()

  @doc """
  Set a `Device` by its interface name with a config. The config can be generated
  using a `DeviceConfigBuilder`.

  Note if no device is present, a new one will be created for the given interface name.

  Returns `:ok` if successful. `{:error, error_info}` will be returned if setting
  the device fails.
  """
  @spec set_device(device_config(), name()) :: :ok | {:error, String.t()}
  def set_device(_device_config, _name), do: error()

  @doc """
  Delete a `Device` by its interface name.

  Returns `:ok` if successful. `{:error, error_info}` will be returned if deleting
  the device fails.
  """
  @spec delete_device(name()) :: :ok | {:error, String.t()}
  def delete_device(_name), do: error()

  @doc """
  Remove a peer from a `Device` by the peer's public key.

  Returns `:ok` if successful. `{:error, error_info}` will be returned if removing
  the peer from the device fails.
  """
  @spec remove_peer(name(), key()) :: :ok | {:error, String.t()}
  def remove_peer(_name, _public_key), do: error()

  @doc """
  Add a peer to a `Device` using a config for the peer. A config can be generated
  using a `PeerConfigBuilder`.

  Returns `:ok` if successful. `{:error, error_info}` will be returned if adding
  the peer to the device fails.
  """
  @spec add_peer(name(), peer_config()) :: :ok | {:error, String.t()}
  def add_peer(_name, _peer), do: error()

  @doc """
  Generates a random private key. It is returned as a base64 `string`.
  """
  @spec generate_private_key() :: key()
  def generate_private_key, do: error()

  @doc """
  Generates a random preshared key. It is returned as a base64 `string`.
  """
  @spec generate_preshared_key() :: key()
  def generate_preshared_key, do: error()

  @doc """
  Return a private key's public key as a base64 `string`.

  Returns `{:ok, public_key}` if successful. `{:error, error_info}` will be returned if
  if getting the public key fails.
  """
  @spec get_public_key(key()) :: {:ok, key()} | {:error, String.t()}
  def get_public_key(_private_key), do: error()

  defp error, do: :erlang.nif_error(:nif_not_loaded)
end
