defmodule AprsmeWeb.TimeUtilsTest do
  use ExUnit.Case, async: true

  alias AprsmeWeb.TimeUtils

  # Allow a few seconds of slack between sequential `DateTime.utc_now/0` calls.
  @slack_seconds 5

  defp seconds_between(dt1, dt2) do
    abs(DateTime.diff(dt1, dt2, :second))
  end

  describe "hours_ago/1" do
    test "returns a DateTime in the past" do
      dt = TimeUtils.hours_ago(3)
      now = DateTime.utc_now()
      assert DateTime.before?(dt, now)
      assert_in_delta DateTime.diff(now, dt, :second), 3 * 3600, @slack_seconds
    end

    test "hours_ago(0) returns approximately now" do
      dt = TimeUtils.hours_ago(0)
      assert seconds_between(dt, DateTime.utc_now()) < @slack_seconds
    end
  end

  describe "hours_ago/1 with common values" do
    test "hours_ago(1) is about 3600 seconds before now" do
      dt = TimeUtils.hours_ago(1)
      assert_in_delta DateTime.diff(DateTime.utc_now(), dt, :second), 3600, @slack_seconds
    end

    test "hours_ago(24) is about 86400 seconds before now" do
      dt = TimeUtils.hours_ago(24)
      assert_in_delta DateTime.diff(DateTime.utc_now(), dt, :second), 86_400, @slack_seconds
    end

    test "hours_ago(48) is about 172800 seconds before now" do
      dt = TimeUtils.hours_ago(48)
      assert_in_delta DateTime.diff(DateTime.utc_now(), dt, :second), 172_800, @slack_seconds
    end

    test "hours_ago(168) is about 604800 seconds before now" do
      dt = TimeUtils.hours_ago(168)
      assert_in_delta DateTime.diff(DateTime.utc_now(), dt, :second), 604_800, @slack_seconds
    end
  end

  describe "days_ago/1" do
    test "returns N days before now" do
      dt = TimeUtils.days_ago(3)
      assert_in_delta DateTime.diff(DateTime.utc_now(), dt, :second), 3 * 86_400, @slack_seconds
    end

    test "accepts 0" do
      dt = TimeUtils.days_ago(0)
      assert seconds_between(dt, DateTime.utc_now()) < @slack_seconds
    end
  end

  describe "time_range/1" do
    test "returns {past, now} for :last_hour" do
      {start_t, end_t} = TimeUtils.time_range(:last_hour)
      assert DateTime.before?(start_t, end_t)
      assert_in_delta DateTime.diff(end_t, start_t, :second), 3600, @slack_seconds
    end

    test "returns {past, now} for :last_day" do
      {start_t, end_t} = TimeUtils.time_range(:last_day)
      assert_in_delta DateTime.diff(end_t, start_t, :second), 86_400, @slack_seconds
    end

    test "returns {past, now} for :last_two_days" do
      {start_t, end_t} = TimeUtils.time_range(:last_two_days)
      assert_in_delta DateTime.diff(end_t, start_t, :second), 172_800, @slack_seconds
    end

    test "returns {past, now} for :last_week" do
      {start_t, end_t} = TimeUtils.time_range(:last_week)
      assert_in_delta DateTime.diff(end_t, start_t, :second), 604_800, @slack_seconds
    end
  end

  describe "default_time_range/0" do
    test "matches :last_two_days" do
      {start_t, end_t} = TimeUtils.default_time_range()
      assert_in_delta DateTime.diff(end_t, start_t, :second), 172_800, @slack_seconds
    end
  end
end
