lib/ident/handle/lib.ex

defmodule Rivet.Ident.Handle.Lib do
  alias Rivet.Ident
  use Rivet.Ecto.Collection.Context, model: Ident.Handle

  @doc """
  Check if handle is open, return double-error (internal/external msg)
  """
  def available(handle, user_id \\ nil) when is_binary(handle) do
    if String.length(handle) < 4 do
      {:error, "short handle", "Your handle should be at least 4 characters long"}
    else
      case Ident.Handle.one(handle: handle) do
        {:ok, %Ident.Handle{user_id: ^user_id}} ->
          {:ok, :current}

        {:error, :not_found} ->
          {:ok, :available}

        {:ok, _} ->
          {:error, "existing handle not by user", "Sorry, that handle isn't available"}

        {:error, err} ->
          {:error, "unexpected error=#{inspect(err)}", "Sorry, that handle isn't available"}
      end
    end
  end

  defp clean_handle(handle) when is_binary(handle) do
    String.split(handle, "@", parts: 2)
    |> Enum.at(0)
    |> String.downcase()
    |> String.replace(~r/[^a-z0-9-]/, "-")
    |> String.replace(~r/-+/, "-")
    |> String.replace(~r/(^-|-$)/, "")
  end

  @chars String.graphemes("ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789")
  defp randchar(), do: Enum.random(@chars)

  def gen_good_handle(handle) do
    handle = clean_handle(handle)

    case Ident.Handle.Lib.available(handle) do
      {:error, _, _} ->
        gen_good_handle(handle <> randchar() <> randchar())

      {:ok, _available_msg} ->
        handle
    end
  end
end