#!/usr/bin/env python

"""Tests for time humanizing."""

import datetime as dt

import humanize
import pytest
from freezegun import freeze_time
from humanize import time

ONE_DAY_DELTA = dt.timedelta(days=1)

# In seconds
ONE_MICROSECOND = 1 / 1000000
FOUR_MICROSECONDS = 4 / 1000000
ONE_MILLISECOND = 1 / 1000
FOUR_MILLISECONDS = 4 / 1000
ONE_HOUR = 3600
ONE_DAY = 24 * ONE_HOUR
ONE_YEAR = 365.25 * ONE_DAY

with freeze_time("2020-02-02"):
    NOW = dt.datetime.now()
    TODAY = dt.date.today()
    TOMORROW = TODAY + ONE_DAY_DELTA
    YESTERDAY = TODAY - ONE_DAY_DELTA


class FakeDate:
    def __init__(self, year, month, day):
        self.year, self.month, self.day = year, month, day


VALUE_ERROR_TEST = FakeDate(290149024, 2, 2)
OVERFLOW_ERROR_TEST = FakeDate(120390192341, 2, 2)


def assertEqualDatetime(dt1, dt2):
    assert (dt1 - dt2).seconds == 0


def assertEqualTimedelta(td1, td2):
    assert td1.days == td2.days
    assert td1.seconds == td2.seconds


# These are not considered "public" interfaces, but require tests anyway.


def test_date_and_delta():
    now = dt.datetime.now()
    td = dt.timedelta
    int_tests = (3, 29, 86399, 86400, 86401 * 30)
    date_tests = [now - td(seconds=x) for x in int_tests]
    td_tests = [td(seconds=x) for x in int_tests]
    results = [(now - td(seconds=x), td(seconds=x)) for x in int_tests]
    for t in (int_tests, date_tests, td_tests):
        for arg, result in zip(t, results):
            date, d = time.date_and_delta(arg)
            assertEqualDatetime(date, result[0])
            assertEqualTimedelta(d, result[1])
    assert time.date_and_delta("NaN") == (None, "NaN")


# Tests for the public interface of humanize.time


def nd_nomonths(d):
    return humanize.naturaldelta(d, months=False)


@pytest.mark.parametrize(
    "test_input, expected",
    [
        (dt.timedelta(days=7), "7 days"),
        (dt.timedelta(days=31), "31 days"),
        (dt.timedelta(days=230), "230 days"),
        (dt.timedelta(days=400), "1 year, 35 days"),
    ],
)
def test_naturaldelta_nomonths(test_input, expected):
    assert nd_nomonths(test_input) == expected


@pytest.mark.parametrize(
    "test_input, expected",
    [
        (0, "a moment"),
        (1, "a second"),
        (30, "30 seconds"),
        (dt.timedelta(minutes=1, seconds=30), "a minute"),
        (dt.timedelta(minutes=2), "2 minutes"),
        (dt.timedelta(hours=1, minutes=30, seconds=30), "an hour"),
        (dt.timedelta(hours=23, minutes=50, seconds=50), "23 hours"),
        (dt.timedelta(days=1), "a day"),
        (dt.timedelta(days=500), "1 year, 4 months"),
        (dt.timedelta(days=365 * 2 + 35), "2 years"),
        (dt.timedelta(seconds=1), "a second"),
        (dt.timedelta(seconds=30), "30 seconds"),
        (dt.timedelta(minutes=1, seconds=30), "a minute"),
        (dt.timedelta(minutes=2), "2 minutes"),
        (dt.timedelta(hours=1, minutes=30, seconds=30), "an hour"),
        (dt.timedelta(hours=23, minutes=50, seconds=50), "23 hours"),
        (dt.timedelta(days=1), "a day"),
        (dt.timedelta(days=500), "1 year, 4 months"),
        (dt.timedelta(days=365 * 2 + 35), "2 years"),
        # regression tests for bugs in post-release humanize
        (dt.timedelta(days=10000), "27 years"),
        (dt.timedelta(days=365 + 35), "1 year, 1 month"),
        (30, "30 seconds"),
        (dt.timedelta(days=365 * 2 + 65), "2 years"),
        (dt.timedelta(days=365 + 4), "1 year, 4 days"),
        (dt.timedelta(days=35), "a month"),
        (dt.timedelta(days=65), "2 months"),
        (dt.timedelta(days=9), "9 days"),
        (dt.timedelta(days=365), "a year"),
        ("NaN", "NaN"),
    ],
)
def test_naturaldelta(test_input, expected):
    assert humanize.naturaldelta(test_input) == expected


@freeze_time("2020-02-02")
@pytest.mark.parametrize(
    "test_input, expected",
    [
        (NOW, "now"),
        (NOW - dt.timedelta(seconds=1), "a second ago"),
        (NOW - dt.timedelta(seconds=30), "30 seconds ago"),
        (NOW - dt.timedelta(minutes=1, seconds=30), "a minute ago"),
        (NOW - dt.timedelta(minutes=2), "2 minutes ago"),
        (NOW - dt.timedelta(hours=1, minutes=30, seconds=30), "an hour ago"),
        (NOW - dt.timedelta(hours=23, minutes=50, seconds=50), "23 hours ago"),
        (NOW - dt.timedelta(days=1), "a day ago"),
        (NOW - dt.timedelta(days=500), "1 year, 4 months ago"),
        (NOW - dt.timedelta(days=365 * 2 + 35), "2 years ago"),
        (NOW + dt.timedelta(seconds=1), "a second from now"),
        (NOW + dt.timedelta(seconds=30), "30 seconds from now"),
        (NOW + dt.timedelta(minutes=1, seconds=30), "a minute from now"),
        (NOW + dt.timedelta(minutes=2), "2 minutes from now"),
        (NOW + dt.timedelta(hours=1, minutes=30, seconds=30), "an hour from now"),
        (NOW + dt.timedelta(hours=23, minutes=50, seconds=50), "23 hours from now"),
        (NOW + dt.timedelta(days=1), "a day from now"),
        (NOW + dt.timedelta(days=500), "1 year, 4 months from now"),
        (NOW + dt.timedelta(days=365 * 2 + 35), "2 years from now"),
        # regression tests for bugs in post-release humanize
        (NOW + dt.timedelta(days=10000), "27 years from now"),
        (NOW - dt.timedelta(days=365 + 35), "1 year, 1 month ago"),
        (30, "30 seconds ago"),
        (NOW - dt.timedelta(days=365 * 2 + 65), "2 years ago"),
        (NOW - dt.timedelta(days=365 + 4), "1 year, 4 days ago"),
        ("NaN", "NaN"),
    ],
)
def test_naturaltime(test_input, expected):
    assert humanize.naturaltime(test_input) == expected


def nt_nomonths(d):
    return humanize.naturaltime(d, months=False)


@freeze_time("2020-02-02")
@pytest.mark.parametrize(
    "test_input, expected",
    [
        (NOW, "now"),
        (NOW - dt.timedelta(seconds=1), "a second ago"),
        (NOW - dt.timedelta(seconds=30), "30 seconds ago"),
        (NOW - dt.timedelta(minutes=1, seconds=30), "a minute ago"),
        (NOW - dt.timedelta(minutes=2), "2 minutes ago"),
        (NOW - dt.timedelta(hours=1, minutes=30, seconds=30), "an hour ago"),
        (NOW - dt.timedelta(hours=23, minutes=50, seconds=50), "23 hours ago"),
        (NOW - dt.timedelta(days=1), "a day ago"),
        (NOW - dt.timedelta(days=17), "17 days ago"),
        (NOW - dt.timedelta(days=47), "47 days ago"),
        (NOW - dt.timedelta(days=500), "1 year, 135 days ago"),
        (NOW - dt.timedelta(days=365 * 2 + 35), "2 years ago"),
        (NOW + dt.timedelta(seconds=1), "a second from now"),
        (NOW + dt.timedelta(seconds=30), "30 seconds from now"),
        (NOW + dt.timedelta(minutes=1, seconds=30), "a minute from now"),
        (NOW + dt.timedelta(minutes=2), "2 minutes from now"),
        (NOW + dt.timedelta(hours=1, minutes=30, seconds=30), "an hour from now"),
        (NOW + dt.timedelta(hours=23, minutes=50, seconds=50), "23 hours from now"),
        (NOW + dt.timedelta(days=1), "a day from now"),
        (NOW + dt.timedelta(days=500), "1 year, 135 days from now"),
        (NOW + dt.timedelta(days=365 * 2 + 35), "2 years from now"),
        # regression tests for bugs in post-release humanize
        (NOW + dt.timedelta(days=10000), "27 years from now"),
        (NOW - dt.timedelta(days=365 + 35), "1 year, 35 days ago"),
        (30, "30 seconds ago"),
        (NOW - dt.timedelta(days=365 * 2 + 65), "2 years ago"),
        (NOW - dt.timedelta(days=365 + 4), "1 year, 4 days ago"),
        ("NaN", "NaN"),
    ],
)
def test_naturaltime_nomonths(test_input, expected):
    assert nt_nomonths(test_input) == expected


@freeze_time("2020-02-02")
@pytest.mark.parametrize(
    "test_args, expected",
    [
        ([TODAY], "today"),
        ([TOMORROW], "tomorrow"),
        ([YESTERDAY], "yesterday"),
        ([dt.date(TODAY.year, 3, 5)], "Mar 05"),
        (["02/26/1984"], "02/26/1984"),
        ([dt.date(1982, 6, 27), "%Y.%m.%d"], "1982.06.27"),
        ([None], None),
        (["Not a date at all."], "Not a date at all."),
        ([VALUE_ERROR_TEST], VALUE_ERROR_TEST),
        ([OVERFLOW_ERROR_TEST], OVERFLOW_ERROR_TEST),
    ],
)
def test_naturalday(test_args, expected):
    assert humanize.naturalday(*test_args) == expected


@freeze_time("2020-02-02")
@pytest.mark.parametrize(
    "test_input, expected",
    [
        (TODAY, "today"),
        (TOMORROW, "tomorrow"),
        (YESTERDAY, "yesterday"),
        (dt.date(TODAY.year, 3, 5), "Mar 05"),
        (dt.date(1982, 6, 27), "Jun 27 1982"),
        (None, None),
        ("Not a date at all.", "Not a date at all."),
        (VALUE_ERROR_TEST, VALUE_ERROR_TEST),
        (OVERFLOW_ERROR_TEST, OVERFLOW_ERROR_TEST),
        (dt.date(2019, 2, 2), "Feb 02 2019"),
        (dt.date(2019, 3, 2), "Mar 02 2019"),
        (dt.date(2019, 4, 2), "Apr 02 2019"),
        (dt.date(2019, 5, 2), "May 02 2019"),
        (dt.date(2019, 6, 2), "Jun 02 2019"),
        (dt.date(2019, 7, 2), "Jul 02 2019"),
        (dt.date(2019, 8, 2), "Aug 02 2019"),
        (dt.date(2019, 9, 2), "Sep 02 2019"),
        (dt.date(2019, 10, 2), "Oct 02"),
        (dt.date(2019, 11, 2), "Nov 02"),
        (dt.date(2019, 12, 2), "Dec 02"),
        (dt.date(2020, 1, 2), "Jan 02"),
        (dt.date(2020, 2, 2), "today"),
        (dt.date(2020, 3, 2), "Mar 02"),
        (dt.date(2020, 4, 2), "Apr 02"),
        (dt.date(2020, 5, 2), "May 02"),
        (dt.date(2020, 6, 2), "Jun 02"),
        (dt.date(2020, 7, 2), "Jul 02"),
        (dt.date(2020, 8, 2), "Aug 02 2020"),
        (dt.date(2020, 9, 2), "Sep 02 2020"),
        (dt.date(2020, 10, 2), "Oct 02 2020"),
        (dt.date(2020, 11, 2), "Nov 02 2020"),
        (dt.date(2020, 12, 2), "Dec 02 2020"),
        (dt.date(2021, 1, 2), "Jan 02 2021"),
        (dt.date(2021, 2, 2), "Feb 02 2021"),
    ],
)
def test_naturaldate(test_input, expected):
    assert humanize.naturaldate(test_input) == expected


@pytest.mark.parametrize(
    "seconds, expected",
    [
        (ONE_MICROSECOND, "a moment"),
        (FOUR_MICROSECONDS, "a moment"),
        (ONE_MILLISECOND, "a moment"),
        (FOUR_MILLISECONDS, "a moment"),
        (2, "2 seconds"),
        (4, "4 seconds"),
        (ONE_HOUR + FOUR_MILLISECONDS, "an hour"),
        (ONE_DAY + FOUR_MILLISECONDS, "a day"),
        (ONE_YEAR + FOUR_MICROSECONDS, "a year"),
    ],
)
def test_naturaldelta_minimum_unit_default(seconds, expected):
    # Arrange
    delta = dt.timedelta(seconds=seconds)

    # Act / Assert
    assert humanize.naturaldelta(delta) == expected


@pytest.mark.parametrize(
    "minimum_unit, seconds, expected",
    [
        ("seconds", ONE_MICROSECOND, "a moment"),
        ("seconds", FOUR_MICROSECONDS, "a moment"),
        ("seconds", ONE_MILLISECOND, "a moment"),
        ("seconds", FOUR_MILLISECONDS, "a moment"),
        ("seconds", 2, "2 seconds"),
        ("seconds", 4, "4 seconds"),
        ("seconds", ONE_HOUR + FOUR_MILLISECONDS, "an hour"),
        ("seconds", ONE_DAY + FOUR_MILLISECONDS, "a day"),
        ("seconds", ONE_YEAR + FOUR_MICROSECONDS, "a year"),
        ("microseconds", ONE_MICROSECOND, "1 microsecond"),
        ("microseconds", FOUR_MICROSECONDS, "4 microseconds"),
        ("microseconds", 2, "2 seconds"),
        ("microseconds", 4, "4 seconds"),
        ("microseconds", ONE_HOUR + FOUR_MILLISECONDS, "an hour"),
        ("microseconds", ONE_DAY + FOUR_MILLISECONDS, "a day"),
        ("microseconds", ONE_YEAR + FOUR_MICROSECONDS, "a year"),
        ("milliseconds", ONE_MILLISECOND, "1 millisecond"),
        ("milliseconds", FOUR_MILLISECONDS, "4 milliseconds"),
        ("milliseconds", 2, "2 seconds"),
        ("milliseconds", 4, "4 seconds"),
        ("milliseconds", ONE_HOUR + FOUR_MILLISECONDS, "an hour"),
        ("milliseconds", ONE_YEAR + FOUR_MICROSECONDS, "a year"),
    ],
)
def test_naturaldelta_minimum_unit_explicit(minimum_unit, seconds, expected):
    # Arrange
    delta = dt.timedelta(seconds=seconds)

    # Act / Assert
    assert humanize.naturaldelta(delta, minimum_unit=minimum_unit) == expected


@pytest.mark.parametrize(
    "seconds, expected",
    [
        (ONE_MICROSECOND, "now"),
        (FOUR_MICROSECONDS, "now"),
        (ONE_MILLISECOND, "now"),
        (FOUR_MILLISECONDS, "now"),
        (2, "2 seconds ago"),
        (4, "4 seconds ago"),
        (ONE_HOUR + FOUR_MILLISECONDS, "an hour ago"),
        (ONE_DAY + FOUR_MILLISECONDS, "a day ago"),
        (ONE_YEAR + FOUR_MICROSECONDS, "a year ago"),
    ],
)
def test_naturaltime_minimum_unit_default(seconds, expected):
    # Arrange
    delta = dt.timedelta(seconds=seconds)

    # Act / Assert
    assert humanize.naturaltime(delta) == expected


@pytest.mark.parametrize(
    "minimum_unit, seconds, expected",
    [
        ("seconds", ONE_MICROSECOND, "now"),
        ("seconds", FOUR_MICROSECONDS, "now"),
        ("seconds", ONE_MILLISECOND, "now"),
        ("seconds", FOUR_MILLISECONDS, "now"),
        ("seconds", 2, "2 seconds ago"),
        ("seconds", 4, "4 seconds ago"),
        ("seconds", ONE_HOUR + FOUR_MILLISECONDS, "an hour ago"),
        ("seconds", ONE_DAY + FOUR_MILLISECONDS, "a day ago"),
        ("seconds", ONE_YEAR + FOUR_MICROSECONDS, "a year ago"),
        ("microseconds", ONE_MICROSECOND, "1 microsecond ago"),
        ("microseconds", FOUR_MICROSECONDS, "4 microseconds ago"),
        ("microseconds", 2, "2 seconds ago"),
        ("microseconds", 4, "4 seconds ago"),
        ("microseconds", ONE_HOUR + FOUR_MILLISECONDS, "an hour ago"),
        ("microseconds", ONE_DAY + FOUR_MILLISECONDS, "a day ago"),
        ("microseconds", ONE_YEAR + FOUR_MICROSECONDS, "a year ago"),
        ("milliseconds", ONE_MILLISECOND, "1 millisecond ago"),
        ("milliseconds", FOUR_MILLISECONDS, "4 milliseconds ago"),
        ("milliseconds", 2, "2 seconds ago"),
        ("milliseconds", 4, "4 seconds ago"),
        ("milliseconds", ONE_HOUR + FOUR_MILLISECONDS, "an hour ago"),
        ("milliseconds", ONE_YEAR + FOUR_MICROSECONDS, "a year ago"),
    ],
)
def test_naturaltime_minimum_unit_explicit(minimum_unit, seconds, expected):
    # Arrange
    delta = dt.timedelta(seconds=seconds)

    # Act / Assert
    assert humanize.naturaltime(delta, minimum_unit=minimum_unit) == expected
