defmodule Serum.HeaderParser.ValueTransformer do
  @moduledoc false

  _moduledocp = """
  A module for parsing header strings and transforming into appropriate types.
  """

  alias Serum.HeaderParser

  @date_format1 "{YYYY}-{0M}-{0D} {h24}:{m}:{s}"
  @date_format2 "{YYYY}-{0M}-{0D}"

  @doc false
  @spec transform_value(binary(), binary(), HeaderParser.value_type()) ::
          HeaderParser.value() | {:error, binary()}
  def transform_value(key, valstr, type)
  def transform_value(_key, valstr, :string), do: valstr

  def transform_value(key, valstr, :integer) do
    case Integer.parse(valstr) do
      {value, ""} -> value
      _ -> {:error, "`#{key}`: invalid integer"}
    end
  end

  def transform_value(key, valstr, :datetime) do
    case Timex.parse(valstr, @date_format1) do
      {:ok, dt} ->
        dt |> Timex.to_erl() |> Timex.to_datetime(:local)

      {:error, _msg} ->
        case Timex.parse(valstr, @date_format2) do
          {:ok, dt} ->
            dt |> Timex.to_erl() |> Timex.to_datetime(:local)

          {:error, msg} ->
            {:error, "`#{key}`: " <> msg}
        end
    end
  end

  def transform_value(key, _valstr, {:list, {:list, _type}}) do
    {:error, "`#{key}`: \"list of lists\" type is not supported"}
  end

  def transform_value(key, valstr, {:list, type}) when is_atom(type) do
    list =
      valstr
      |> String.split(",")
      |> Stream.map(&String.trim/1)
      |> Stream.reject(&(&1 == ""))
      |> Stream.map(&transform_value(key, &1, type))

    case Enum.filter(list, &error?/1) do
      [] -> Enum.to_list(list)
      [{:error, _} = error | _] -> error
    end
  end

  def transform_value(key, _valstr, _type) do
    {:error, "`#{key}`: invalid value type"}
  end

  @spec error?(term) :: boolean
  defp error?({:error, _}), do: true
  defp error?(_), do: false
end
