#!/usr/bin/env python

import calendar
import datetime
import time

from saml2.time_util import add_duration
from saml2.time_util import after
from saml2.time_util import before
from saml2.time_util import f_quotient
from saml2.time_util import in_a_while
from saml2.time_util import instant
from saml2.time_util import modulo
from saml2.time_util import not_before
from saml2.time_util import not_on_or_after
from saml2.time_util import parse_duration
from saml2.time_util import str_to_time
from saml2.time_util import valid


def test_f_quotient():
    assert f_quotient(0, 3) == 0
    assert f_quotient(1, 3) == 0
    assert f_quotient(2, 3) == 0
    assert f_quotient(3, 3) == 1
    assert f_quotient(3.123, 3) == 1


def test_modulo():
    assert modulo(-1, 3) == 2
    assert modulo(0, 3) == 0
    assert modulo(1, 3) == 1
    assert modulo(2, 3) == 2
    assert modulo(3, 3) == 0
    x = 3.123
    assert modulo(3.123, 3) == x - 3


def test_f_quotient_2():
    for i in range(1, 13):
        assert f_quotient(i, 1, 13) == 0
    assert f_quotient(13, 1, 13) == 1
    assert f_quotient(13.123, 1, 13) == 1


def test_modulo_2():
    assert modulo(0, 1, 13) == 12
    for i in range(1, 13):
        assert modulo(i, 1, 13) == i
    assert modulo(13, 1, 13) == 1
    # x = 0.123
    # assert modulo(13+x, 1, 13) == 1+x


def test_parse_duration():
    (sign, d) = parse_duration("P1Y3M5DT7H10M3.3S")
    assert sign == "+"
    assert d["tm_sec"] == 3.3
    assert d["tm_mon"] == 3
    assert d["tm_hour"] == 7
    assert d["tm_mday"] == 5
    assert d["tm_year"] == 1
    assert d["tm_min"] == 10


def test_parse_duration2():
    (sign, d) = parse_duration("PT30M")
    assert sign == "+"
    assert d["tm_sec"] == 0
    assert d["tm_mon"] == 0
    assert d["tm_hour"] == 0
    assert d["tm_mday"] == 0
    assert d["tm_year"] == 0
    assert d["tm_min"] == 30


PATTERNS = {
    "P3Y6M4DT12H30M5S": {"tm_sec": 5, "tm_hour": 12, "tm_mday": 4, "tm_year": 3, "tm_mon": 6, "tm_min": 30},
    "P23DT23H": {"tm_sec": 0, "tm_hour": 23, "tm_mday": 23, "tm_year": 0, "tm_mon": 0, "tm_min": 0},
    "P4Y": {"tm_sec": 0, "tm_hour": 0, "tm_mday": 0, "tm_year": 4, "tm_mon": 0, "tm_min": 0},
    "P1M": {"tm_sec": 0, "tm_hour": 0, "tm_mday": 0, "tm_year": 0, "tm_mon": 1, "tm_min": 0},
    "PT1M": {"tm_sec": 0, "tm_hour": 0, "tm_mday": 0, "tm_year": 0, "tm_mon": 0, "tm_min": 1},
    "P0.5Y": {"tm_sec": 0, "tm_hour": 0, "tm_mday": 0, "tm_year": 0.5, "tm_mon": 0, "tm_min": 0},
    "P0,5Y": {"tm_sec": 0, "tm_hour": 0, "tm_mday": 0, "tm_year": 0.5, "tm_mon": 0, "tm_min": 0},
    "PT36H": {"tm_sec": 0, "tm_hour": 36, "tm_mday": 0, "tm_year": 0, "tm_mon": 0, "tm_min": 0},
    "P1DT12H": {"tm_sec": 0, "tm_hour": 12, "tm_mday": 1, "tm_year": 0, "tm_mon": 0, "tm_min": 0},
}


def test_parse_duration_n():
    for dur, _val in PATTERNS.items():
        (sign, d) = parse_duration(dur)
        assert d == _val


def test_add_duration_1():
    # 2000-01-12T12:13:14Z	P1Y3M5DT7H10M3S	2001-04-17T19:23:17Z
    t = add_duration(str_to_time("2000-01-12T12:13:14Z"), "P1Y3M5DT7H10M3S")
    assert t.tm_year == 2001
    assert t.tm_mon == 4
    assert t.tm_mday == 17
    assert t.tm_hour == 19
    assert t.tm_min == 23
    assert t.tm_sec == 17


def test_add_duration_2():
    # 2000-01-12 PT33H   2000-01-13
    t = add_duration(str_to_time("2000-01-12T00:00:00Z"), "PT33H")
    assert t.tm_year == 2000
    assert t.tm_mon == 1
    assert t.tm_mday == 13
    assert t.tm_hour == 9
    assert t.tm_min == 0
    assert t.tm_sec == 0


def test_str_to_time():
    t = calendar.timegm(str_to_time("2000-01-12T00:00:00Z"))
    # TODO: Find all instances of time.mktime(.....)
    # t = time.mktime(str_to_time("2000-01-12T00:00:00Z"))
    # assert t == 947631600.0
    # TODO: add something to show how this time was arrived at
    # do this as an external method in the
    assert t == 947635200
    # some IdPs omit the trailing Z, and SAML spec is unclear if it is actually required
    t = calendar.timegm(str_to_time("2000-01-12T00:00:00"))
    assert t == 947635200


def test_instant():
    inst = str_to_time(instant())
    now = time.gmtime()

    assert now >= inst


def test_valid():
    assert valid("2000-01-12T00:00:00Z") == False
    current_year = datetime.datetime.today().year
    assert valid(f"{int(current_year + 1)}-01-12T00:00:00Z") == True
    this_instance = instant()
    time.sleep(1)
    assert valid(this_instance) is False  # unless on a very fast machine :-)
    soon = in_a_while(seconds=10)
    assert valid(soon) == True


def test_timeout():
    soon = in_a_while(seconds=1)
    time.sleep(2)
    assert valid(soon) == False


def test_before():
    current_year = datetime.datetime.today().year
    assert before(f"{int(current_year - 1)}-01-01T00:00:00Z") == False
    assert before(f"{int(current_year + 1)}-01-01T00:00:00Z") == True


def test_after():
    current_year = datetime.datetime.today().year
    assert after(f"{int(current_year + 1)}-01-01T00:00:00Z") == False
    assert after(f"{int(current_year - 1)}-01-01T00:00:00Z") == True


def test_not_before():
    current_year = datetime.datetime.today().year
    assert not_before(f"{int(current_year + 1)}-01-01T00:00:00Z") == False
    assert not_before(f"{int(current_year - 1)}-01-01T00:00:00Z") == True


def test_not_on_or_after():
    current_year = datetime.datetime.today().year
    assert not_on_or_after(f"{int(current_year + 1)}-01-01T00:00:00Z") == True
    assert not_on_or_after(f"{int(current_year - 1)}-01-01T00:00:00Z") == False


if __name__ == "__main__":
    test_str_to_time()
