from datetime import datetime

import pytest
import pytz
from dateutil import tz as dateutil_tz

from arrow import (
    FORMAT_ATOM,
    FORMAT_COOKIE,
    FORMAT_RFC822,
    FORMAT_RFC850,
    FORMAT_RFC1036,
    FORMAT_RFC1123,
    FORMAT_RFC2822,
    FORMAT_RFC3339,
    FORMAT_RSS,
    FORMAT_W3C,
)

from .utils import make_full_tz_list


@pytest.mark.usefixtures("arrow_formatter")
class TestFormatterFormatToken:
    def test_format(self):

        dt = datetime(2013, 2, 5, 12, 32, 51)

        result = self.formatter.format(dt, "MM-DD-YYYY hh:mm:ss a")

        assert result == "02-05-2013 12:32:51 pm"

    def test_year(self):

        dt = datetime(2013, 1, 1)
        assert self.formatter._format_token(dt, "YYYY") == "2013"
        assert self.formatter._format_token(dt, "YY") == "13"

    def test_month(self):

        dt = datetime(2013, 1, 1)
        assert self.formatter._format_token(dt, "MMMM") == "January"
        assert self.formatter._format_token(dt, "MMM") == "Jan"
        assert self.formatter._format_token(dt, "MM") == "01"
        assert self.formatter._format_token(dt, "M") == "1"

    def test_day(self):

        dt = datetime(2013, 2, 1)
        assert self.formatter._format_token(dt, "DDDD") == "032"
        assert self.formatter._format_token(dt, "DDD") == "32"
        assert self.formatter._format_token(dt, "DD") == "01"
        assert self.formatter._format_token(dt, "D") == "1"
        assert self.formatter._format_token(dt, "Do") == "1st"

        assert self.formatter._format_token(dt, "dddd") == "Friday"
        assert self.formatter._format_token(dt, "ddd") == "Fri"
        assert self.formatter._format_token(dt, "d") == "5"

    def test_hour(self):

        dt = datetime(2013, 1, 1, 2)
        assert self.formatter._format_token(dt, "HH") == "02"
        assert self.formatter._format_token(dt, "H") == "2"

        dt = datetime(2013, 1, 1, 13)
        assert self.formatter._format_token(dt, "HH") == "13"
        assert self.formatter._format_token(dt, "H") == "13"

        dt = datetime(2013, 1, 1, 2)
        assert self.formatter._format_token(dt, "hh") == "02"
        assert self.formatter._format_token(dt, "h") == "2"

        dt = datetime(2013, 1, 1, 13)
        assert self.formatter._format_token(dt, "hh") == "01"
        assert self.formatter._format_token(dt, "h") == "1"

        # test that 12-hour time converts to '12' at midnight
        dt = datetime(2013, 1, 1, 0)
        assert self.formatter._format_token(dt, "hh") == "12"
        assert self.formatter._format_token(dt, "h") == "12"

    def test_minute(self):

        dt = datetime(2013, 1, 1, 0, 1)
        assert self.formatter._format_token(dt, "mm") == "01"
        assert self.formatter._format_token(dt, "m") == "1"

    def test_second(self):

        dt = datetime(2013, 1, 1, 0, 0, 1)
        assert self.formatter._format_token(dt, "ss") == "01"
        assert self.formatter._format_token(dt, "s") == "1"

    def test_sub_second(self):

        dt = datetime(2013, 1, 1, 0, 0, 0, 123456)
        assert self.formatter._format_token(dt, "SSSSSS") == "123456"
        assert self.formatter._format_token(dt, "SSSSS") == "12345"
        assert self.formatter._format_token(dt, "SSSS") == "1234"
        assert self.formatter._format_token(dt, "SSS") == "123"
        assert self.formatter._format_token(dt, "SS") == "12"
        assert self.formatter._format_token(dt, "S") == "1"

        dt = datetime(2013, 1, 1, 0, 0, 0, 2000)
        assert self.formatter._format_token(dt, "SSSSSS") == "002000"
        assert self.formatter._format_token(dt, "SSSSS") == "00200"
        assert self.formatter._format_token(dt, "SSSS") == "0020"
        assert self.formatter._format_token(dt, "SSS") == "002"
        assert self.formatter._format_token(dt, "SS") == "00"
        assert self.formatter._format_token(dt, "S") == "0"

    def test_timestamp(self):

        dt = datetime.now(tz=dateutil_tz.UTC)
        expected = str(dt.timestamp())
        assert self.formatter._format_token(dt, "X") == expected

        # Must round because time.time() may return a float with greater
        # than 6 digits of precision
        expected = str(int(dt.timestamp() * 1000000))
        assert self.formatter._format_token(dt, "x") == expected

    def test_timezone(self):

        dt = datetime.utcnow().replace(tzinfo=dateutil_tz.gettz("US/Pacific"))

        result = self.formatter._format_token(dt, "ZZ")
        assert result == "-07:00" or result == "-08:00"

        result = self.formatter._format_token(dt, "Z")
        assert result == "-0700" or result == "-0800"

    @pytest.mark.parametrize("full_tz_name", make_full_tz_list())
    def test_timezone_formatter(self, full_tz_name):

        # This test will fail if we use "now" as date as soon as we change from/to DST
        dt = datetime(1986, 2, 14, tzinfo=pytz.timezone("UTC")).replace(
            tzinfo=dateutil_tz.gettz(full_tz_name)
        )
        abbreviation = dt.tzname()

        result = self.formatter._format_token(dt, "ZZZ")
        assert result == abbreviation

    def test_am_pm(self):

        dt = datetime(2012, 1, 1, 11)
        assert self.formatter._format_token(dt, "a") == "am"
        assert self.formatter._format_token(dt, "A") == "AM"

        dt = datetime(2012, 1, 1, 13)
        assert self.formatter._format_token(dt, "a") == "pm"
        assert self.formatter._format_token(dt, "A") == "PM"

    def test_week(self):
        dt = datetime(2017, 5, 19)
        assert self.formatter._format_token(dt, "W") == "2017-W20-5"

        # make sure week is zero padded when needed
        dt_early = datetime(2011, 1, 20)
        assert self.formatter._format_token(dt_early, "W") == "2011-W03-4"

    def test_nonsense(self):
        dt = datetime(2012, 1, 1, 11)
        assert self.formatter._format_token(dt, None) is None
        assert self.formatter._format_token(dt, "NONSENSE") is None

    def test_escape(self):

        assert (
            self.formatter.format(
                datetime(2015, 12, 10, 17, 9), "MMMM D, YYYY [at] h:mma"
            )
            == "December 10, 2015 at 5:09pm"
        )

        assert (
            self.formatter.format(
                datetime(2015, 12, 10, 17, 9), "[MMMM] M D, YYYY [at] h:mma"
            )
            == "MMMM 12 10, 2015 at 5:09pm"
        )

        assert (
            self.formatter.format(
                datetime(1990, 11, 25),
                "[It happened on] MMMM Do [in the year] YYYY [a long time ago]",
            )
            == "It happened on November 25th in the year 1990 a long time ago"
        )

        assert (
            self.formatter.format(
                datetime(1990, 11, 25),
                "[It happened on] MMMM Do [in the][ year] YYYY [a long time ago]",
            )
            == "It happened on November 25th in the year 1990 a long time ago"
        )

        assert (
            self.formatter.format(
                datetime(1, 1, 1), "[I'm][ entirely][ escaped,][ weee!]"
            )
            == "I'm entirely escaped, weee!"
        )

        # Special RegEx characters
        assert (
            self.formatter.format(
                datetime(2017, 12, 31, 2, 0), "MMM DD, YYYY |^${}().*+?<>-& h:mm A"
            )
            == "Dec 31, 2017 |^${}().*+?<>-& 2:00 AM"
        )

        # Escaping is atomic: brackets inside brackets are treated literally
        assert self.formatter.format(datetime(1, 1, 1), "[[[ ]]") == "[[ ]"


@pytest.mark.usefixtures("arrow_formatter", "time_1975_12_25")
class TestFormatterBuiltinFormats:
    def test_atom(self):
        assert (
            self.formatter.format(self.datetime, FORMAT_ATOM)
            == "1975-12-25 14:15:16-05:00"
        )

    def test_cookie(self):
        assert (
            self.formatter.format(self.datetime, FORMAT_COOKIE)
            == "Thursday, 25-Dec-1975 14:15:16 EST"
        )

    def test_rfc_822(self):
        assert (
            self.formatter.format(self.datetime, FORMAT_RFC822)
            == "Thu, 25 Dec 75 14:15:16 -0500"
        )

    def test_rfc_850(self):
        assert (
            self.formatter.format(self.datetime, FORMAT_RFC850)
            == "Thursday, 25-Dec-75 14:15:16 EST"
        )

    def test_rfc_1036(self):
        assert (
            self.formatter.format(self.datetime, FORMAT_RFC1036)
            == "Thu, 25 Dec 75 14:15:16 -0500"
        )

    def test_rfc_1123(self):
        assert (
            self.formatter.format(self.datetime, FORMAT_RFC1123)
            == "Thu, 25 Dec 1975 14:15:16 -0500"
        )

    def test_rfc_2822(self):
        assert (
            self.formatter.format(self.datetime, FORMAT_RFC2822)
            == "Thu, 25 Dec 1975 14:15:16 -0500"
        )

    def test_rfc3339(self):
        assert (
            self.formatter.format(self.datetime, FORMAT_RFC3339)
            == "1975-12-25 14:15:16-05:00"
        )

    def test_rss(self):
        assert (
            self.formatter.format(self.datetime, FORMAT_RSS)
            == "Thu, 25 Dec 1975 14:15:16 -0500"
        )

    def test_w3c(self):
        assert (
            self.formatter.format(self.datetime, FORMAT_W3C)
            == "1975-12-25 14:15:16-05:00"
        )
