defmodule JsonSchema.Parser.ArrayParser do
  @behaviour JsonSchema.Parser.ParserBehaviour
  @moduledoc ~S"""
  Parses a JSON schema array type:
      {
        "type": "array",
        "items": {
          "$ref": "#/definitions/rectangle"
        }
      }
  Into an `JsonSchema.Types.ArrayType`.
  """
  require Logger
  alias JsonSchema.{Parser, Types}
  alias Parser.{ErrorUtil, ParserResult, Util}
  alias Types.ArrayType
  @doc ~S"""
  Returns true if the json subschema represents an array type.
  ## Examples
  iex> type?(%{})
  false
  iex> type?(%{"items" => %{"$ref" => "#foo"}})
  true
  """
  @impl JsonSchema.Parser.ParserBehaviour
  @spec type?(Types.schemaNode()) :: boolean
  def type?(%{"items" => items}) when is_map(items), do: true
  def type?(_schema_node), do: false
  @doc """
  Parses a JSON schema array type into an `JsonSchema.Types.ArrayType`.
  """
  @impl JsonSchema.Parser.ParserBehaviour
  @spec parse(
          Types.schemaNode(),
          URI.t(),
          URI.t() | nil,
          URI.t(),
          String.t()
        ) :: ParserResult.t()
  def parse(%{"items" => items} = schema_node, parent_id, id, path, name) do
    description = Map.get(schema_node, "description")
    default = Map.get(schema_node, "default")
    errors =
      if default != nil && not is_list(default) do
        [ErrorUtil.invalid_type(path, "default", "array", default)]
      else
        []
      end
    items_abs_path =
      path
      |> Util.add_fragment_child("items")
    items_result =
      items
      |> Util.parse_type(parent_id, items_abs_path, :anonymous)
    array_type = %ArrayType{
      name: name,
      description: description,
      default: default,
      path: path,
      items: items_abs_path
    }
    array_type
    |> Util.create_type_dict(path, id)
    |> ParserResult.new([], errors)
    |> ParserResult.merge(items_result)
  end
end