Skip to main content

lib/jido/chat/emoji.ex

defmodule Jido.Chat.Emoji do
  @moduledoc """
  Cross-platform emoji helper with a small default registry and custom overrides.
  """

  @defaults %{
    thumbs_up: "👍",
    thumbsup: "👍",
    white_check_mark: "✅",
    check: "✅",
    x: "❌",
    warning: "⚠️",
    eyes: "👀",
    rocket: "🚀",
    tada: "🎉",
    hourglass: "⏳"
  }

  @type registry :: %{optional(atom()) => String.t()}

  @doc "Returns the built-in emoji registry."
  @spec registry() :: registry()
  def registry, do: @defaults

  @doc "Adds or overrides a custom emoji entry in a registry."
  @spec put_custom(registry(), atom() | String.t(), String.t()) :: registry()
  def put_custom(custom, name, rendered) when is_binary(rendered) do
    Map.put(custom || %{}, normalize_name(name), rendered)
  end

  @doc "Renders an emoji token using built-ins plus optional custom overrides."
  @spec render(String.t() | atom(), keyword()) :: String.t()
  def render(value, opts \\ [])

  def render(value, opts) when is_binary(value) do
    if value != "" and not named_emoji?(value) do
      value
    else
      lookup_emoji(value, opts)
    end
  end

  def render(value, opts) do
    lookup_emoji(value, opts)
  end

  defp lookup_emoji(value, opts) do
    custom =
      opts[:custom]
      |> normalize_custom()

    name = normalize_name(value)
    Map.get(custom, name) || Map.get(@defaults, name) || fallback_token(value)
  end

  defp normalize_custom(nil), do: %{}

  defp normalize_custom(custom) when is_map(custom) do
    custom
    |> Enum.map(fn {key, value} -> {normalize_name(key), value} end)
    |> Map.new()
  end

  defp normalize_name(value) when is_atom(value), do: value

  defp normalize_name(value) when is_binary(value) do
    value
    |> String.trim()
    |> String.trim_leading(":")
    |> String.trim_trailing(":")
    |> String.replace("-", "_")
    |> String.to_atom()
  end

  defp fallback_token(value) when is_binary(value), do: value
  defp fallback_token(value) when is_atom(value), do: ":" <> Atom.to_string(value) <> ":"

  defp named_emoji?(value) when is_binary(value) do
    String.starts_with?(value, ":") and String.ends_with?(value, ":")
  end
end