# -*- coding: utf-8 -*-
"""
Tests for util.date_utils
"""

from datetime import datetime, timedelta, tzinfo
import unittest

import ddt
from mock import patch
from nose.tools import assert_equals, assert_false  # pylint: disable=no-name-in-module
from pytz import UTC

from util.date_utils import (
    get_default_time_display, get_time_display, almost_same_datetime,
    strftime_localized,
)


def test_get_default_time_display():
    assert_equals("", get_default_time_display(None))
    test_time = datetime(1992, 3, 12, 15, 3, 30, tzinfo=UTC)
    assert_equals(
        "Mar 12, 1992 at 15:03 UTC",
        get_default_time_display(test_time))


def test_get_dflt_time_disp_notz():
    test_time = datetime(1992, 3, 12, 15, 3, 30)
    assert_equals(
        "Mar 12, 1992 at 15:03 UTC",
        get_default_time_display(test_time))


def test_get_time_disp_ret_empty():
    assert_equals("", get_time_display(None))
    test_time = datetime(1992, 3, 12, 15, 3, 30, tzinfo=UTC)
    assert_equals("", get_time_display(test_time, ""))


def test_get_time_display():
    test_time = datetime(1992, 3, 12, 15, 3, 30, tzinfo=UTC)
    assert_equals("dummy text", get_time_display(test_time, 'dummy text'))
    assert_equals("Mar 12 1992", get_time_display(test_time, '%b %d %Y'))
    assert_equals("Mar 12 1992 UTC", get_time_display(test_time, '%b %d %Y %Z'))
    assert_equals("Mar 12 15:03", get_time_display(test_time, '%b %d %H:%M'))


def test_get_time_pass_through():
    test_time = datetime(1992, 3, 12, 15, 3, 30, tzinfo=UTC)
    assert_equals("Mar 12, 1992 at 15:03 UTC", get_time_display(test_time))
    assert_equals("Mar 12, 1992 at 15:03 UTC", get_time_display(test_time, None))
    assert_equals("Mar 12, 1992 at 15:03 UTC", get_time_display(test_time, "%"))


def test_get_time_display_coerce():
    test_time_standard = datetime(1992, 1, 12, 15, 3, 30, tzinfo=UTC)
    test_time_daylight = datetime(1992, 7, 12, 15, 3, 30, tzinfo=UTC)
    assert_equals("Jan 12, 1992 at 07:03 PST",
                  get_time_display(test_time_standard, None, coerce_tz="US/Pacific"))
    assert_equals("Jan 12, 1992 at 15:03 UTC",
                  get_time_display(test_time_standard, None, coerce_tz="NONEXISTENTTZ"))
    assert_equals("Jan 12 07:03",
                  get_time_display(test_time_standard, '%b %d %H:%M', coerce_tz="US/Pacific"))
    assert_equals("Jul 12, 1992 at 08:03 PDT",
                  get_time_display(test_time_daylight, None, coerce_tz="US/Pacific"))
    assert_equals("Jul 12, 1992 at 15:03 UTC",
                  get_time_display(test_time_daylight, None, coerce_tz="NONEXISTENTTZ"))
    assert_equals("Jul 12 08:03",
                  get_time_display(test_time_daylight, '%b %d %H:%M', coerce_tz="US/Pacific"))


class NamelessTZ(tzinfo):
    """Static timezone for testing"""

    def utcoffset(self, _dt):
        return timedelta(hours=-3)

    def dst(self, _dt):
        return timedelta(0)


def test_get_default_time_display_no_tzname():
    assert_equals("", get_default_time_display(None))
    test_time = datetime(1992, 3, 12, 15, 3, 30, tzinfo=NamelessTZ())
    assert_equals(
        "Mar 12, 1992 at 15:03-0300",
        get_default_time_display(test_time))


def test_almost_same_datetime():
    assert almost_same_datetime(
        datetime(2013, 5, 3, 10, 20, 30),
        datetime(2013, 5, 3, 10, 21, 29)
    )

    assert almost_same_datetime(
        datetime(2013, 5, 3, 11, 20, 30),
        datetime(2013, 5, 3, 10, 21, 29),
        timedelta(hours=1)
    )

    assert_false(
        almost_same_datetime(
            datetime(2013, 5, 3, 11, 20, 30),
            datetime(2013, 5, 3, 10, 21, 29)
        )
    )

    assert_false(
        almost_same_datetime(
            datetime(2013, 5, 3, 11, 20, 30),
            datetime(2013, 5, 3, 10, 21, 29),
            timedelta(minutes=10)
        )
    )


def fake_ugettext(translations):
    """
    Create a fake implementation of ugettext, for testing.
    """
    def _ugettext(text):                # pylint: disable=missing-docstring
        return translations.get(text, text)
    return _ugettext


def fake_pgettext(translations):
    """
    Create a fake implementation of pgettext, for testing.
    """
    def _pgettext(context, text):       # pylint: disable=missing-docstring
        return translations.get((context, text), text)
    return _pgettext


@ddt.ddt
class StrftimeLocalizedTest(unittest.TestCase):
    """
    Tests for strftime_localized.
    """
    @ddt.data(
        ("%Y", "2013"),
        ("%m/%d/%y", "02/14/13"),
        ("hello", "hello"),
        (u'%Y년 %m월 %d일', u"2013년 02월 14일"),
        ("%a, %b %d, %Y", "Thu, Feb 14, 2013"),
        ("%I:%M:%S %p", "04:41:17 PM"),
    )
    def test_usual_strftime_behavior(self, (fmt, expected)):
        dtime = datetime(2013, 02, 14, 16, 41, 17)
        self.assertEqual(expected, strftime_localized(dtime, fmt))
        # strftime doesn't like Unicode, so do the work in UTF8.
        self.assertEqual(expected, dtime.strftime(fmt.encode('utf8')).decode('utf8'))

    @ddt.data(
        ("SHORT_DATE", "Feb 14, 2013"),
        ("LONG_DATE", "Thursday, February 14, 2013"),
        ("TIME", "04:41:17 PM"),
        ("%x %X!", "Feb 14, 2013 04:41:17 PM!"),
    )
    def test_shortcuts(self, (fmt, expected)):
        dtime = datetime(2013, 02, 14, 16, 41, 17)
        self.assertEqual(expected, strftime_localized(dtime, fmt))

    @patch('util.date_utils.pgettext', fake_pgettext(translations={
        ("abbreviated month name", "Feb"): "XXfebXX",
        ("month name", "February"): "XXfebruaryXX",
        ("abbreviated weekday name", "Thu"): "XXthuXX",
        ("weekday name", "Thursday"): "XXthursdayXX",
        ("am/pm indicator", "PM"): "XXpmXX",
    }))
    @ddt.data(
        ("SHORT_DATE", "XXfebXX 14, 2013"),
        ("LONG_DATE", "XXthursdayXX, XXfebruaryXX 14, 2013"),
        ("DATE_TIME", "XXfebXX 14, 2013 at 16:41"),
        ("TIME", "04:41:17 XXpmXX"),
        ("%x %X!", "XXfebXX 14, 2013 04:41:17 XXpmXX!"),
    )
    def test_translated_words(self, (fmt, expected)):
        dtime = datetime(2013, 02, 14, 16, 41, 17)
        self.assertEqual(expected, strftime_localized(dtime, fmt))

    @patch('util.date_utils.ugettext', fake_ugettext(translations={
        "SHORT_DATE_FORMAT": "date(%Y.%m.%d)",
        "LONG_DATE_FORMAT": "date(%A.%Y.%B.%d)",
        "DATE_TIME_FORMAT": "date(%Y.%m.%d@%H.%M)",
        "TIME_FORMAT": "%Hh.%Mm.%Ss",
    }))
    @ddt.data(
        ("SHORT_DATE", "date(2013.02.14)"),
        ("Look: %x", "Look: date(2013.02.14)"),
        ("LONG_DATE", "date(Thursday.2013.February.14)"),
        ("DATE_TIME", "date(2013.02.14@16.41)"),
        ("TIME", "16h.41m.17s"),
        ("The time is: %X", "The time is: 16h.41m.17s"),
        ("%x %X", "date(2013.02.14) 16h.41m.17s"),
    )
    def test_translated_formats(self, (fmt, expected)):
        dtime = datetime(2013, 02, 14, 16, 41, 17)
        self.assertEqual(expected, strftime_localized(dtime, fmt))

    @patch('util.date_utils.ugettext', fake_ugettext(translations={
        "SHORT_DATE_FORMAT": "oops date(%Y.%x.%d)",
        "TIME_FORMAT": "oops %Hh.%Xm.%Ss",
    }))
    @ddt.data(
        ("SHORT_DATE", "Feb 14, 2013"),
        ("TIME", "04:41:17 PM"),
    )
    def test_recursion_protection(self, (fmt, expected)):
        dtime = datetime(2013, 02, 14, 16, 41, 17)
        self.assertEqual(expected, strftime_localized(dtime, fmt))

    @ddt.data(
        "%",
        "Hello%"
        "%Y/%m/%d%",
    )
    def test_invalid_format_strings(self, fmt):
        dtime = datetime(2013, 02, 14, 16, 41, 17)
        with self.assertRaises(ValueError):
            strftime_localized(dtime, fmt)
