defmodule XmlToMap.NaiveMap do
  @moduledoc """
  Module to recursively traverse the output of `erlsom.simple_form/1`
  and produce a map.

  `erlsom` uses character lists so this library converts them to
  Elixir binary string.

  Attributes, if present, are defined as `tag@attribute_name => attribute_value`.
  """

  # if a single list element of a tuple, ignore the list detail and parse it
  def parse([{tag, attributes, content}]) do
    parse({tag, attributes, content})
  end

  # for any other singular value, it's probably meant to be a string
  def parse([value]) do
    to_string(value) |> String.trim()
  end

  # a parse with a tuples is probably the entry point we'll hit first
  # if this node has no attributes, our work is easier
  def parse({tag, [], content}) do
    parsed_content = parse(content)
    %{to_string(tag) => parsed_content}
  end

  # this is probably the entry point we'll hit first
  # Element = {Tag, Attributes, Content},
  # Tag is a string
  # Attributes = [{AttributeName, Value}],
  # Content is a list of Elements and/or strings.
  # in this case attributes is not empty
  def parse({tag, attributes, content}) do
    attributes_map =
      Enum.reduce(attributes, %{}, fn {attribute_name, attribute_value}, acc ->
        Map.put(acc, "-#{attribute_name}", to_string(attribute_value))
      end)

    parsed_content = parse(content)
    joined_content = %{"#content" => parsed_content} |> Map.merge(attributes_map)

    %{to_string(tag) => joined_content}
  end

  def parse(list) when is_list(list) do
    parsed_list = Enum.map(list, &{to_string(elem(&1, 0)), parse(&1)})

    content =
      Enum.reduce(parsed_list, %{}, fn {k, v}, acc ->
        case Map.get(acc, k) do
          nil ->
            for({key, value} <- v, into: %{}, do: {key, value})
            |> Map.merge(acc)

          [h | t] ->
            Map.put(acc, k, [h | t] ++ [v[k]])

          prev ->
            Map.put(acc, k, [prev] ++ [v[k]])
        end
      end)

    case content == %{} do
      true -> nil
      _ -> content
    end
  end
end
