defmodule Exnumerator do
  alias Exnumerator.{StringListEnum, AtomListEnum, KeywordListEnum}

  defmacro __using__(opts) do
    quote location: :keep, bind_quoted: [values: opts[:values]] do
      @behaviour Ecto.Type

      @doc """
      Returns all possible values for the enum.
      """
      def values, do: unquote(values)

      @doc """
      Returns a random value for the enum.
      """
      def sample, do: Enum.random(values())

      @doc """
      Returns the first value for the enum.
      """
      def first, do: List.first(values())

      @impl true
      def type, do: :string

      @impl true
      def cast(term), do: Exnumerator.cast(values(), term)

      @impl true
      def dump(term), do: Exnumerator.dump(values(), term)

      @impl true
      def load(term), do: Exnumerator.load(values(), term)

      @impl true
      def embed_as(_format), do: :self

      @impl true
      def equal?(term, term), do: true
      @impl true
      def equal?(_term1, _term2), do: false
    end
  end

  def cast(values, term), do: run(:cast, values, term)
  def load(values, term), do: run(:load, values, term)
  def dump(values, term), do: run(:dump, values, term)

  defp run(action, values, term) do
    cond do
      Keyword.keyword?(values) -> apply(KeywordListEnum, action, [values, term])
      Enum.all?(values, &is_atom(&1)) -> apply(AtomListEnum, action, [values, term])
      is_list(values) -> apply(StringListEnum, action, [values, term])
      true -> nil
    end
  end
end
