lib/tx_build/time_bounds.ex

defmodule Stellar.TxBuild.TimeBounds do
  @moduledoc """
  `TimeBounds` struct definition.
  """
  alias StellarBase.XDR.{TimePoint, TimeBounds, OptionalTimeBounds}

  @behaviour Stellar.TxBuild.XDR

  @type unix_date_time :: non_neg_integer()
  @type date_time :: DateTime.t() | unix_date_time()
  @type validation :: {:ok, unix_date_time()} | {:error, :atom}

  @type t :: %__MODULE__{min_time: unix_date_time(), max_time: unix_date_time()}

  defstruct [:min_time, :max_time]

  @impl true
  def new(time_bounds \\ [min_time: 0, max_time: 0], opts \\ [])

  def new(:none, _opts), do: new(min_time: 0, max_time: 0)

  def new([min_time: min_time, max_time: max_time], _opts) do
    with {:ok, min_time} <- validate_date_time(min_time),
         {:ok, max_time} <- validate_date_time(max_time) do
      %__MODULE__{min_time: min_time, max_time: max_time}
    end
  end

  @impl true
  def to_xdr(%__MODULE__{min_time: 0, max_time: 0}) do
    OptionalTimeBounds.new()
  end

  def to_xdr(%__MODULE__{min_time: min_time, max_time: max_time}) do
    min_time = TimePoint.new(min_time)
    max_time = TimePoint.new(max_time)

    min_time
    |> TimeBounds.new(max_time)
    |> OptionalTimeBounds.new()
  end

  @spec set_timeout(max_time :: date_time()) :: t() | {:error, atom()}
  def set_timeout(max_time), do: new(min_time: 0, max_time: max_time)

  @spec validate_date_time(date_time :: date_time()) :: validation()
  defp validate_date_time(%DateTime{} = date_time) do
    {:ok, DateTime.to_unix(date_time)}
  end

  defp validate_date_time(date_time) when is_integer(date_time) and date_time >= 0,
    do: {:ok, date_time}

  defp validate_date_time(_date_time), do: {:error, :invalid_time_bounds}
end