defmodule Countries.Loader do
  @moduledoc false

  import Countries.Utils, only: [to_map: 1]
  alias Countries.Country

  # loads all country data from our yaml files
  # :yamerl returns a really terrible data structure
  #    [[{'name', 'Germany'}, {'code', 'DE'}], [{'nam:e', 'Austria'}, {'code', 'AT'}]]
  # We need to transform that to maps:
  #    [%{name: "Germany', code: "DE"}, %{name: 'Austria', code: "AT"}]
  def load do
    data_path(["countries.yaml"])
    |> :yamerl.decode_file()
    |> List.first()
    |> Enum.flat_map(fn code ->
      :yamerl.decode_file(data_path(["countries", "#{code}.yaml"]))
    end)
    |> Enum.map(&convert_country/1)
  end

  defp data_path(path) do
    Path.join([:code.priv_dir(:countries), "data"] ++ path)
  end

  defp convert_country([{_, country_data}]) do
    Enum.reduce(country_data, %Country{}, fn {attribute, value}, country ->
      with attribute = List.to_atom(attribute) do
        Map.put(country, attribute, convert_value(attribute, value))
      end
    end)
  end

  @do_not_convert ~w[national_number_lengths national_destination_code_lengths]a

  defp convert_value(_, :null),
    do: nil

  defp convert_value(:vat_rates, vat_rates) when not is_nil(vat_rates) do
    Map.new(vat_rates, fn
      {key, :null} -> {List.to_atom(key), nil}
      {key, value} -> {List.to_atom(key), value}
    end)
  end

  defp convert_value(:unofficial_names, names),
    do: Enum.map(names, &to_string/1)

  defp convert_value(:geo, values),
    do: to_map(values)

  defp convert_value(attribute, value)
       when is_list(value) and not (attribute in @do_not_convert),
       do: to_string(value)

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