defmodule Growth.Calc.Age do
  @moduledoc """
  Calculate the age in months.
  """

  @type precision :: :day | :week | :month | :year

  # NOTE: (jpd): based on [WHO instructions][0]
  # [0]: https://cdn.who.int/media/docs/default-source/child-growth/child-growth-standards/indicators/instructions-en.pdf
  @day_in_month 30.4375

  @day_in_week 7.0

  @doc """
  Calculate the age with the precision of `:day`, or `:week`, or `:month`, considering the measurement date as today.

  For age in weeks or months, considers completed ones, removing decimal value.
  """
  @spec calculate(precision(), Date.t(), Date.t()) :: pos_integer()

  def calculate(precision, date_of_birth) do
    calculate(precision, date_of_birth, Date.utc_today())
  end

  @doc """
  Calculate the age with the precision of `:day`, or `:week`, or `:month`.

  For age in weeks or months, considers completed ones, removing decimal value.
  """

  def calculate(:month, date_of_birth, date_of_measurement) do
    :day
    |> calculate(date_of_birth, date_of_measurement)
    |> Kernel./(@day_in_month)
    |> floor()
  end

  def calculate(:week, date_of_birth, date_of_measurement) do
    :day
    |> calculate(date_of_birth, date_of_measurement)
    |> Kernel./(@day_in_week)
    |> floor()
  end

  def calculate(:day, date_of_birth, date_of_measurement) do
    Date.diff(date_of_measurement, date_of_birth)
  end
end
