"""Tests for calendar date helper functions."""
from __future__ import annotations

from datetime import date
from unittest.mock import patch, MagicMock

from shared.utils.calendar_helpers import add_months, build_calendar_weeks


# ---------------------------------------------------------------------------
# add_months
# ---------------------------------------------------------------------------

class TestAddMonths:
    def test_same_year(self):
        assert add_months(2025, 3, 2) == (2025, 5)

    def test_next_year(self):
        assert add_months(2025, 11, 2) == (2026, 1)

    def test_subtract(self):
        assert add_months(2025, 3, -2) == (2025, 1)

    def test_subtract_prev_year(self):
        assert add_months(2025, 1, -1) == (2024, 12)

    def test_add_twelve(self):
        assert add_months(2025, 6, 12) == (2026, 6)

    def test_subtract_twelve(self):
        assert add_months(2025, 6, -12) == (2024, 6)

    def test_large_delta(self):
        assert add_months(2025, 1, 25) == (2027, 2)

    def test_zero_delta(self):
        assert add_months(2025, 7, 0) == (2025, 7)

    def test_december_to_january(self):
        assert add_months(2025, 12, 1) == (2026, 1)

    def test_january_to_december(self):
        assert add_months(2025, 1, -1) == (2024, 12)

    def test_subtract_large(self):
        assert add_months(2025, 3, -15) == (2023, 12)

    def test_month_boundaries(self):
        # Every month +1
        for m in range(1, 12):
            y, nm = add_months(2025, m, 1)
            assert nm == m + 1
            assert y == 2025
        y, nm = add_months(2025, 12, 1)
        assert nm == 1
        assert y == 2026


# ---------------------------------------------------------------------------
# build_calendar_weeks
# ---------------------------------------------------------------------------

class TestBuildCalendarWeeks:
    def test_returns_list_of_weeks(self):
        weeks = build_calendar_weeks(2025, 6)
        assert isinstance(weeks, list)
        assert len(weeks) >= 4  # at least 4 weeks in any month
        assert len(weeks) <= 6  # at most 6 weeks

    def test_each_week_has_7_days(self):
        weeks = build_calendar_weeks(2025, 6)
        for week in weeks:
            assert len(week) == 7

    def test_day_dict_structure(self):
        weeks = build_calendar_weeks(2025, 6)
        day = weeks[0][0]
        assert "date" in day
        assert "in_month" in day
        assert "is_today" in day
        assert isinstance(day["date"], date)
        assert isinstance(day["in_month"], bool)
        assert isinstance(day["is_today"], bool)

    def test_in_month_flag(self):
        weeks = build_calendar_weeks(2025, 6)
        june_days = [d for w in weeks for d in w if d["in_month"]]
        assert len(june_days) == 30  # June has 30 days

    def test_february_leap_year(self):
        weeks = build_calendar_weeks(2024, 2)
        feb_days = [d for w in weeks for d in w if d["in_month"]]
        assert len(feb_days) == 29

    def test_february_non_leap_year(self):
        weeks = build_calendar_weeks(2025, 2)
        feb_days = [d for w in weeks for d in w if d["in_month"]]
        assert len(feb_days) == 28

    def test_starts_on_monday(self):
        """Calendar should start on Monday (firstweekday=0)."""
        weeks = build_calendar_weeks(2025, 6)
        first_day = weeks[0][0]["date"]
        assert first_day.weekday() == 0  # Monday

    def test_is_today_flag(self):
        """The today flag should be True for exactly one day (or zero if not in month range)."""
        # Use a fixed known date - mock datetime.now
        from datetime import datetime, timezone
        fixed_now = datetime(2025, 6, 15, tzinfo=timezone.utc)
        with patch("shared.utils.calendar_helpers.datetime") as mock_dt:
            mock_dt.now.return_value = fixed_now
            mock_dt.side_effect = lambda *a, **kw: datetime(*a, **kw)
            weeks = build_calendar_weeks(2025, 6)
            today_days = [d for w in weeks for d in w if d["is_today"]]
            assert len(today_days) == 1
            assert today_days[0]["date"] == date(2025, 6, 15)
