defmodule Kanta.Registry do
  @moduledoc """
  Kanta Registry
  """

  def child_spec(_arg) do
    [keys: :unique, name: __MODULE__]
    |> Registry.child_spec()
    |> Supervisor.child_spec(id: __MODULE__)
  end

  @doc """
  Fetch the config for an Kanta supervisor instance.

  ## Example

  Get the default instance config:

      Kanta.Registry.config(Kanta)

  Get config for a custom named instance:

      Kanta.Registry.config(MyApp.Kanta)
  """
  @spec config(atom()) :: Kanta.Config.t()
  def config(kanta_name) do
    case lookup(kanta_name) do
      {_pid, config} ->
        config

      _ ->
        raise RuntimeError, """
        No Kanta instance named `#{inspect(kanta_name)}` is running and config isn't available.
        """
    end
  end

  @doc """
  Find the `{pid, value}` pair for a registered Kanta process.

  ## Example

  Get the default instance config:

      Kanta.Registry.lookup(Kanta)

  Get a supervised module's pid:

      Kanta.Registry.lookup(Kanta, Kanta.Notifier)
  """
  def lookup(kanta_name, role \\ nil) do
    __MODULE__
    |> Registry.lookup(key(kanta_name, role))
    |> List.first()
  end

  @doc """
  Returns the pid of a supervised Kanta process, or `nil` if the process can't be found.

  ## Example

  Get the Kanta supervisor's pid:

      Kanta.Registry.whereis(Kanta)

  Get the pid for a plugin:

      Kanta.Registry.whereis(Kanta, {:plugin, MyApp.Kanta.Plugin})
  """
  def whereis(kanta_name, role \\ nil) do
    kanta_name
    |> via(role)
    |> GenServer.whereis()
  end

  @doc """
  Build a via tuple suitable for calls to a supervised Kanta process.

  ## Example

  For an Kanta supervisor:

      Kanta.Registry.via(Kanta)

  For a plugin:

      Kanta.Registry.via(Kanta, {:plugin, Kanta.DeepL.Plugin})
  """
  def via(kanta_name, role \\ nil, value \\ nil)
  def via(kanta_name, role, nil), do: {:via, Registry, {__MODULE__, key(kanta_name, role)}}

  def via(kanta_name, role, value),
    do: {:via, Registry, {__MODULE__, key(kanta_name, role), value}}

  defp key(kanta_name, nil), do: kanta_name
  defp key(kanta_name, role), do: {kanta_name, role}
end
