defmodule Slime.Parser.AttributesKeyword do
  @moduledoc "
  Utilities for handling element attributes.
  "

  @doc """
  Merges multiply attributes values for keys specified in merge_rules.
  Attribute value may be given by string, list, or {:eex, args} node
  `merge_rules` should me an `%{attribute_name: joining_character}` map

  ## Examples
      iex> Slime.Parser.AttributesKeyword.merge(
      ...>   [class: "a", class: ["b", "c"], class: "d"],
      ...>   %{class: " "}
      ...> )
      [class: "a b c d"]

      iex> Slime.Parser.AttributesKeyword.merge(
      ...>   [class: "a", class: ["b", "c"], class: {:eex, "d"}],
      ...>   %{class: " "}
      ...> )
      [class: {:eex, ~S("a b c \#{d}")}]
  """
  def merge(keyword_list, merge_rules) do
    Enum.reduce(merge_rules, keyword_list, fn {attr, join}, result ->
      values =
        result
        |> Enum.filter(fn {key, _value} -> key == attr end)
        |> Enum.map(fn {_key, value} -> value end)

      if values == [] do
        result
      else
        values = merge_attribute_values(values, join)
        result = Enum.filter(result, &key_not_attr?(&1, attr))
        [{attr, values} | result]
      end
    end)
  end

  defp key_not_attr?({key, _value}, attr), do: key != attr

  defp merge_attribute_values(values, join_by) do
    result = join_attribute_values(values, join_by)

    if Enum.any?(values, &dynamic_value?/1) do
      {:eex, ~s("#{result}")}
    else
      result
    end
  end

  defp dynamic_value?({:eex, _}), do: true
  defp dynamic_value?(_), do: false

  defp join_attribute_values(values, join_by) do
    values |> Enum.map(&attribute_val(&1, join_by)) |> List.flatten() |> Enum.join(join_by)
  end

  defp attribute_val({:eex, content}, join_by) do
    case Code.string_to_quoted!(content) do
      list when is_list(list) ->
        list |> List.flatten() |> Enum.join(join_by)

      _ ->
        "\#{" <> content <> "}"
    end
  end

  defp attribute_val(value, _), do: value
end
