# #region Test.AppTimezone [C:3] [TYPE Module] [SEMANTICS test,timezone,datetime,utilities]
# @BRIEF Tests for core/timezone.py — all timezone utility functions.
# @RELATION BINDS_TO -> [AppTimezone]

from pathlib import Path
import sys

sys.path.insert(0, str(Path(__file__).parent.parent.parent / "src"))

from datetime import datetime, timezone, timedelta
from unittest.mock import patch
import pytest

from zoneinfo import ZoneInfo


# #region test_get_default_tz_name [C:2] [TYPE Function]
# @BRIEF Test _get_default_tz_name reads env var or default.
class TestGetDefaultTzName:
    def test_returns_env_value(self):
        from src.core.timezone import _get_default_tz_name
        with patch("src.core.timezone.os.getenv", return_value="America/New_York"):
            assert _get_default_tz_name() == "America/New_York"

    def test_returns_default_when_not_set(self):
        from src.core.timezone import _get_default_tz_name
        with patch("src.core.timezone.os.getenv", return_value=None):
            # os.getenv returns None if not set, but _get_default_tz_name uses getenv with default
            pass
        # The function uses os.getenv("APP_TIMEZONE", "Europe/Moscow"), so we can't test unset easily
        # Let's test directly
        with patch("src.core.timezone.os.getenv", return_value="Europe/Moscow"):
            assert _get_default_tz_name() == "Europe/Moscow"
# #endregion test_get_default_tz_name


# #region test_get_app_timezone [C:2] [TYPE Function]
# @BRIEF Test get_app_timezone returns cached ZoneInfo.
class TestGetAppTimezone:
    def test_returns_zoneinfo(self):
        from src.core.timezone import get_app_timezone, invalidate_timezone_cache
        invalidate_timezone_cache()
        tz = get_app_timezone()
        assert isinstance(tz, ZoneInfo)

    def test_caches_result(self):
        from src.core.timezone import get_app_timezone, invalidate_timezone_cache
        invalidate_timezone_cache()
        tz1 = get_app_timezone()
        tz2 = get_app_timezone()
        assert tz1 is tz2  # Same cached object
# #endregion test_get_app_timezone


# #region test_invalidate_timezone_cache [C:2] [TYPE Function]
# @BRIEF Test invalidate_timezone_cache resets the cache.
class TestInvalidateTimezoneCache:
    def test_invalidates_cache(self):
        import src.core.timezone as tz_mod
        tz_mod._APP_TZ_CACHE = None  # Reset
        tz_mod.invalidate_timezone_cache()
        assert tz_mod._APP_TZ_CACHE is None  # Cache cleared
        tz1 = tz_mod.get_app_timezone()
        assert tz_mod._APP_TZ_CACHE is not None  # Cache populated
        tz_mod.invalidate_timezone_cache()
        assert tz_mod._APP_TZ_CACHE is None  # Cache cleared again
        tz_mod._APP_TZ_CACHE = None  # Reset for other tests
# #endregion test_invalidate_timezone_cache


# #region test_validate_timezone [C:2] [TYPE Function]
# @BRIEF Test validate_timezone for valid/invalid IANA names.
class TestValidateTimezone:
    def test_valid_timezone(self):
        from src.core.timezone import validate_timezone
        assert validate_timezone("Europe/Moscow") is True
        assert validate_timezone("UTC") is True
        assert validate_timezone("America/New_York") is True

    def test_invalid_timezone(self):
        from src.core.timezone import validate_timezone
        assert validate_timezone("Invalid/Zone") is False

    def test_none_raises_type_error(self):
        from src.core.timezone import validate_timezone
        # This should return False when TypeError is caught
        assert validate_timezone(None) is False  # type: ignore
# #endregion test_validate_timezone


# #region test_localize [C:2] [TYPE Function]
# @BRIEF Test localize converts UTC to app timezone.
class TestLocalize:
    def test_none_returns_none(self):
        from src.core.timezone import localize
        assert localize(None) is None

    def test_converts_naive_utc_to_tz(self):
        from src.core.timezone import localize, invalidate_timezone_cache
        from src.core.timezone import _get_default_tz_name
        invalidate_timezone_cache()
        dt = datetime(2024, 1, 15, 12, 0, 0)  # Naive UTC
        result = localize(dt)
        assert result is not None
        assert result.tzinfo is not None
        # Should be in Europe/Moscow (+03:00)
        assert result.hour == 15  # Moscow is UTC+3

    def test_converts_aware_utc_to_tz(self):
        from src.core.timezone import localize, invalidate_timezone_cache
        invalidate_timezone_cache()
        dt = datetime(2024, 1, 15, 12, 0, 0, tzinfo=timezone.utc)
        result = localize(dt)
        assert result is not None
        assert result.tzinfo is not None
        offset = result.utcoffset()
        assert offset is not None
        assert offset.total_seconds() == 3 * 3600  # Moscow is UTC+3

    def test_preserves_non_utc_aware(self):
        from src.core.timezone import localize, invalidate_timezone_cache
        invalidate_timezone_cache()
        dt = datetime(2024, 1, 15, 12, 0, 0, tzinfo=ZoneInfo("America/New_York"))
        result = localize(dt)
        assert result is not None
        # Should convert to Moscow time
        assert result.tzinfo is not None
        assert "Europe/Moscow" in str(result.tzinfo) or "+03" in result.isoformat()
# #endregion test_localize


# #region test_now [C:2] [TYPE Function]
# @BRIEF Test now returns timezone-aware datetime.
class TestNow:
    def test_returns_aware_datetime(self):
        from src.core.timezone import now
        result = now()
        assert result.tzinfo is not None

    def test_in_app_timezone(self):
        from src.core.timezone import now, invalidate_timezone_cache
        invalidate_timezone_cache()
        result = now()
        offset = result.utcoffset()
        assert offset is not None
        assert offset.total_seconds() == 3 * 3600  # Europe/Moscow
# #endregion test_now


# #region test_format_timezone_offset [C:2] [TYPE Function]
# @BRIEF Test format_timezone_offset returns formatted offset string.
class TestFormatTimezoneOffset:
    def test_returns_formatted_offset(self):
        from src.core.timezone import format_timezone_offset
        offset = format_timezone_offset()
        assert offset.startswith("+") or offset.startswith("-")
        assert ":" in offset
        assert len(offset) == 6  # e.g. "+03:00"

    def test_offset_is_moscow(self):
        from src.core.timezone import format_timezone_offset, invalidate_timezone_cache
        invalidate_timezone_cache()
        result = format_timezone_offset()
        assert result == "+03:00"
# #endregion test_format_timezone_offset
# #endregion Test.AppTimezone
