import datetime
from zoneinfo import ZoneInfo

from app import format_date, format_date_range, format_datetime, format_datetime_range
from app.common.filters import format_date_range_short, format_datetime_short, iso_utc


class TestFormatDate:
    def test_date(self):
        assert format_date(datetime.date(2025, 1, 1)) == "Wednesday 1 January 2025"

    def test_datetime(self):
        assert format_date(datetime.datetime(2025, 1, 1, 12, 0, 0)) == "Wednesday 1 January 2025"

    def test_datetime_crosses_midnight_in_bst(self):
        assert format_date(datetime.datetime(2025, 6, 1, 23, 30, 0)) == "Monday 2 June 2025"

    def test_datetime_tz_none_keeps_utc(self):
        assert format_date(datetime.datetime(2025, 6, 1, 23, 30, 0), tz=None) == "Sunday 1 June 2025"

    def test_date_ignores_tz(self):
        assert format_date(datetime.date(2025, 6, 1), tz=None) == "Sunday 1 June 2025"


class TestFormatDatetime:
    def test_datetime_am(self):
        assert format_datetime(datetime.datetime(2025, 1, 1, 1, 0, 0)) == "1am on Wednesday 1 January 2025"

    def test_datetime_pm(self):
        assert format_datetime(datetime.datetime(2025, 1, 1, 13, 0, 0)) == "1pm on Wednesday 1 January 2025"

    def test_datetime_minutes(self):
        assert format_datetime(datetime.datetime(2025, 1, 1, 13, 37, 0)) == "1:37pm on Wednesday 1 January 2025"

    def test_datetime_noon(self):
        assert format_datetime(datetime.datetime(2025, 1, 1, 12, 0, 0)) == "12pm on Wednesday 1 January 2025"

    def test_datetime_midnight(self):
        assert format_datetime(datetime.datetime(2025, 1, 1, 0, 0, 0)) == "12am on Wednesday 1 January 2025"

    def test_naive_utc_in_bst_shifts_one_hour(self):
        assert format_datetime(datetime.datetime(2025, 6, 1, 23, 30, 0)) == "12:30am on Monday 2 June 2025"

    def test_naive_utc_in_gmt_unchanged(self):
        assert format_datetime(datetime.datetime(2025, 1, 1, 9, 0, 0)) == "9am on Wednesday 1 January 2025"

    def test_tz_none_keeps_raw_utc(self):
        assert format_datetime(datetime.datetime(2025, 6, 1, 23, 30, 0), tz=None) == "11:30pm on Sunday 1 June 2025"

    def test_aware_non_utc_input_converted(self):
        eastern = datetime.datetime(2025, 6, 1, 19, 30, 0, tzinfo=ZoneInfo("America/New_York"))
        assert format_datetime(eastern) == "12:30am on Monday 2 June 2025"


class TestFormatDateRange:
    def test_dates(self):
        assert (
            format_date_range(datetime.date(2025, 1, 1), datetime.date(2025, 2, 1))
            == "Wednesday 1 January 2025 to Saturday 1 February 2025"
        )


class TestFormatDateRangeShort:
    def test_dates(self):
        assert (
            format_date_range_short(datetime.date(2025, 1, 1), datetime.date(2025, 2, 1))
            == "1 January 2025 to 1 February 2025"
        )


class TestFormatDatetimeRange:
    def test_datetimes(self):
        assert (
            format_datetime_range(datetime.datetime(2025, 1, 1, 9, 0, 0), datetime.datetime(2025, 2, 1, 17, 0, 0))
            == "9am on Wednesday 1 January 2025 to 5pm on Saturday 1 February 2025"
        )

    def test_datetimes_minutes(self):
        assert (
            format_datetime_range(datetime.datetime(2025, 1, 1, 9, 15, 0), datetime.datetime(2025, 2, 1, 17, 45, 0))
            == "9:15am on Wednesday 1 January 2025 to 5:45pm on Saturday 1 February 2025"
        )


class TestFormatDatetimeShort:
    def test_datetime_am(self):
        assert format_datetime_short(datetime.datetime(2025, 1, 1, 1, 0, 0)) == "1 Jan 2025 at 1am"

    def test_datetime_pm(self):
        assert format_datetime_short(datetime.datetime(2025, 1, 1, 13, 0, 0)) == "1 Jan 2025 at 1pm"

    def test_datetime_minutes(self):
        assert format_datetime_short(datetime.datetime(2025, 1, 1, 13, 37, 0)) == "1 Jan 2025 at 1:37pm"

    def test_datetime_noon(self):
        assert format_datetime_short(datetime.datetime(2025, 1, 1, 12, 0, 0)) == "1 Jan 2025 at 12pm"

    def test_datetime_midnight(self):
        assert format_datetime_short(datetime.datetime(2025, 1, 1, 0, 0, 0)) == "1 Jan 2025 at 12am"

    def test_naive_utc_in_bst_shifts_one_hour(self):
        assert format_datetime_short(datetime.datetime(2025, 6, 1, 23, 30, 0)) == "2 Jun 2025 at 12:30am"


class TestIsoUtc:
    def test_naive_input_treated_as_utc(self):
        assert iso_utc(datetime.datetime(2025, 6, 1, 23, 30, 0)) == "2025-06-01T23:30:00+00:00"

    def test_aware_non_utc_input_converted(self):
        eastern = datetime.datetime(2025, 6, 1, 18, 30, 0, tzinfo=ZoneInfo("America/New_York"))
        assert iso_utc(eastern) == "2025-06-01T22:30:00+00:00"

    def test_aware_utc_input_unchanged(self):
        utc = datetime.datetime(2025, 1, 1, 9, 0, 0, tzinfo=datetime.UTC)
        assert iso_utc(utc) == "2025-01-01T09:00:00+00:00"
