defmodule NewPassport do def get_new_passport(now, birthday, form) do with {:ok, timestamp} <- enter_building(now), {:ok, manual} <- find_counter_information(now), counter <- manual.(birthday), {:ok, checksum} <- stamp_form(timestamp, counter, form), passport_number <- get_new_passport_number(timestamp, counter, checksum) do {:ok, passport_number} else {:coffee_break, _message} -> {:retry, NaiveDateTime.add(now, 15, :minute)} {:error, _message} = error -> error end end # Do not modify the functions below defp enter_building(%NaiveDateTime{} = datetime) do day = Date.day_of_week(datetime) time = NaiveDateTime.to_time(datetime) cond do day <= 4 and time_between(time, ~T[13:00:00], ~T[15:30:00]) -> {:ok, datetime |> DateTime.from_naive!("Etc/UTC") |> DateTime.to_unix()} day == 5 and time_between(time, ~T[13:00:00], ~T[14:30:00]) -> {:ok, datetime |> DateTime.from_naive!("Etc/UTC") |> DateTime.to_unix()} true -> {:error, "city office is closed"} end end @eighteen_years 18 * 365 defp find_counter_information(%NaiveDateTime{} = datetime) do time = NaiveDateTime.to_time(datetime) if time_between(time, ~T[14:00:00], ~T[14:20:00]) do {:coffee_break, "information counter staff on coffee break, come back in 15 minutes"} else {:ok, fn %Date{} = birthday -> 1 + div(Date.diff(datetime, birthday), @eighteen_years) end} end end defp stamp_form(timestamp, counter, :blue) when rem(counter, 2) == 1 do {:ok, 3 * (timestamp + counter) + 1} end defp stamp_form(timestamp, counter, :red) when rem(counter, 2) == 0 do {:ok, div(timestamp + counter, 2)} end defp stamp_form(_timestamp, _counter, _form), do: {:error, "wrong form color"} defp get_new_passport_number(timestamp, counter, checksum) do "#{timestamp}-#{counter}-#{checksum}" end defp time_between(time, from, to) do Time.compare(from, time) != :gt and Time.compare(to, time) == :gt end end