defmodule JsonSchema.Parser.EnumParser do
  @behaviour JsonSchema.Parser.ParserBehaviour
  @moduledoc ~S"""
  Parse a JSON schema enum type:
      {
        "type": "string",
        "enum": ["none", "green", "orange", "blue", "yellow", "red"]
      }
  Into an `JsonSchema.Types.EnumType`.
  """
  require Logger
  alias JsonSchema.{Parser, Types}
  alias Parser.{ErrorUtil, ParserResult, Util}
  alias Types.EnumType
  @doc ~S"""
  Returns true if the json subschema represents an enum type.
  ## Examples
  iex> type?(%{})
  false
  iex> type?(%{"enum" => ["red", "yellow", "green"]})
  true
  """
  @impl JsonSchema.Parser.ParserBehaviour
  @spec type?(Types.schemaNode()) :: boolean
  def type?(%{"enum" => values}) when is_list(values) and length(values) > 0, do: true
  def type?(_schema_node), do: false
  @doc """
  Parses a JSON schema enum type into an `JsonSchema.Types.EnumType`.
  """
  @impl JsonSchema.Parser.ParserBehaviour
  @spec parse(
          Types.schemaNode(),
          URI.t(),
          URI.t() | nil,
          URI.t(),
          String.t()
        ) :: ParserResult.t()
  def parse(%{"enum" => values} = schema_node, _parent_id, id, path, name) do
    description = Map.get(schema_node, "description")
    default = Map.get(schema_node, "default")
    type = parse_enum_type(values)
    errors =
      if default != nil && not Enum.member?(values, default) do
        [ErrorUtil.invalid_enum(path, "default", values, default)]
      else
        []
      end
    enum_type = %EnumType{
      name: name,
      description: description,
      default: default,
      path: path,
      type: type,
      values: values
    }
    enum_type
    |> Util.create_type_dict(path, id)
    |> ParserResult.new([], errors)
  end
  @spec parse_enum_type(nonempty_list(term)) :: EnumType.value_type_name()
  defp parse_enum_type(values) do
    first_value = hd(values)
    cond do
      is_binary(first_value) -> :string
      is_integer(first_value) -> :integer
      is_number(first_value) -> :number
    end
  end
end