defmodule Exonerate.Type.Integer do
  @moduledoc false

  @behaviour Exonerate.Type

  alias Exonerate.Combining
  alias Exonerate.Tools

  @modules Combining.merge(%{
             "maximum" => Exonerate.Filter.Maximum,
             "minimum" => Exonerate.Filter.Minimum,
             "exclusiveMaximum" => Exonerate.Filter.ExclusiveMaximum,
             "exclusiveMinimum" => Exonerate.Filter.ExclusiveMinimum,
             "multipleOf" => Exonerate.Filter.MultipleOf
           })

  @filters Map.keys(@modules)

  defmacro filter(resource, pointer, opts) do
    __CALLER__
    |> Tools.subschema(resource, pointer)
    |> build_filter(resource, pointer, opts)
    |> Tools.maybe_dump(__CALLER__, opts)
  end

  defp build_filter(context, resource, pointer, opts) do
    filter_clauses =
      for filter <- @filters, is_map_key(context, filter) do
        filter_call = Tools.call(resource, JsonPtr.join(pointer, Combining.adjust(filter)), opts)

        quote do
          :ok <- unquote(filter_call)(integer, path)
        end
      end

    quote do
      defp unquote(Tools.call(resource, pointer, opts))(integer, path)
           when is_integer(integer) do
        with unquote_splicing(filter_clauses) do
          :ok
        end
      end
    end
  end

  defmacro accessories(resource, pointer, opts) do
    __CALLER__
    |> Tools.subschema(resource, pointer)
    |> build_accessories(resource, pointer, opts)
    |> Tools.maybe_dump(__CALLER__, opts)
  end

  defp build_accessories(context, name, pointer, opts) do
    for filter <- @filters, is_map_key(context, filter), not Combining.filter?(filter) do
      module = @modules[filter]
      pointer = JsonPtr.join(pointer, filter)

      quote do
        require unquote(module)
        unquote(module).filter(unquote(name), unquote(pointer), unquote(opts))
      end
    end
  end
end
