"""Comprehensive test suite for the date/time utility module.

This test suite covers all classes and functions in the datetimeutil module,
including edge cases, error handling, and timezone behavior.
"""

import datetime
import json
import re
from typing import Any
from unittest.mock import MagicMock, patch

import babel
import pendulum
import pytest
from pendulum.tz.timezone import Timezone
from pydantic import ValidationError

from akkudoktoreos.core.pydantic import PydanticBaseModel
from akkudoktoreos.utils.datetimeutil import (
    MAX_DURATION_STRING_LENGTH,
    Date,
    DateTime,
    Duration,
    Time,
    _parse_time_string,
    compare_datetimes,
    hours_in_day,
    to_datetime,
    to_duration,
    to_time,
    to_timezone,
)

# ----------
# Time Class
# ----------


class TestTimeParsing:
    """Comprehensive tests for string → pendulum.Time conversion and roundtrip correctness."""

    # -------------------------------
    # VALID FORMATS
    # -------------------------------
    @pytest.mark.parametrize(
        "input_str, expected",
        [
            # 24-hour basic formats
            ("00:00", (0, 0, 0)),
            ("23:59:59", (23, 59, 59)),
            ("14:30", (14, 30, 0)),
            ("14:30:45", (14, 30, 45)),
            ("14:30:45.123456", (14, 30, 45)),
            ("1430", (14, 30, 0)),
            ("143045", (14, 30, 45)),
            ("930", (9, 30, 0)),
            ("14", (14, 0, 0)),
            ("14.5", (14, 30, 0)),
            ("14.25", (14, 15, 0)),
            ("14h30", (14, 30, 0)),
            ("14-30", (14, 30, 0)),
            ("14 30", (14, 30, 0)),

            # 12-hour AM/PM formats
            ("12:00 AM", (0, 0, 0)),
            ("12:00 PM", (12, 0, 0)),
            ("2:30 PM", (14, 30, 0)),
            ("2:30:45 PM", (14, 30, 45)),
            ("2PM", (14, 0, 0)),
            ("11AM", (11, 0, 0)),

            # Compact & decimal
            ("3", (3, 0, 0)),
            ("23.75", (23, 45, 0)),

            # Offset-based / ISO-like
            ("08:00:00.000000+01:00", (8, 0, 0)),
            ("14:30 +05:30", (14, 30, 0)),
            ("14:30 -03:00", (14, 30, 0)),
            ("22:15 -0800", (22, 15, 0)),

            # Alternative separators
            ("14-30", (14, 30, 0)),
            ("14 30", (14, 30, 0)),
            ("14-30-45", (14, 30, 45)),
            ("14 30 45", (14, 30, 45)),

            # Timezones by abbreviation
            ("14:30 UTC", (14, 30, 0)),
            ("14:30 GMT", (14, 30, 0)),
            ("2:30 PM EST", (14, 30, 0)),
            ("9:15 CST", (9, 15, 0)),
            ("23:59 PST", (23, 59, 0)),

            # Named timezones
            ("14h30 Europe/Berlin", (14, 30, 0)),
            ("14:30 America/New_York", (14, 30, 0)),
            ("08:15 Asia/Tokyo", (8, 15, 0)),
            ("23:45 Australia/Sydney", (23, 45, 0)),
        ],
    )
    def test_parse_time_string_valid(self, input_str, expected):
        """Ensure various valid time strings parse correctly."""
        result = _parse_time_string(input_str)
        assert isinstance(result, pendulum.Time)
        assert (result.hour, result.minute, result.second) == expected[:3]

    # -------------------------------
    # INVALID INPUTS
    # -------------------------------
    @pytest.mark.parametrize(
        "input_str",
        [
            "",              # empty
            "25:00",         # invalid hour
            "14:61",         # invalid minute
            "2:30 XM",       # bad AM/PM
            "noonish",       # nonsense
            "2400",          # invalid compact
            "24.999",        # beyond 23.999
            "14:30 Mars/Terra",  # invalid tz
        ],
    )
    def test_parse_time_string_invalid(self, input_str):
        """Invalid inputs should raise ValueError."""
        with pytest.raises(ValueError):
            _parse_time_string(input_str)

    # -------------------------------
    # TIMEZONE HANDLING
    # -------------------------------
    @pytest.mark.parametrize(
        "input_str, tz_name",
        [
            ("14:30 UTC", "UTC"),
            ("14:30 Europe/Berlin", "Europe/Berlin"),
            ("2:30 PM PST", "America/Los_Angeles"),
            ("08:00:00.000000+01:00", "+01:00"),
            ("14:30 +05:30", "+05:30"),
            ("22:00 -04:00", "-04:00"),
        ],
    )
    def test_parse_time_string_with_timezone(self, input_str, tz_name):
        """Test timezone-aware parsing results in a Time with tzinfo."""
        t = _parse_time_string(input_str)
        assert isinstance(t, pendulum.Time)
        assert t.tzinfo is not None
        # compare normalized zone name
        assert tz_name.split("/")[-1] in str(t.tzinfo) or tz_name in str(t.tzinfo), f"{str(t.tzinfo)} vs. expected {tz_name}"

    @pytest.mark.parametrize(
        "time_str",
        [
            "08:00:00.000000+01:00",
            "14:30 UTC",
            "2:30 PM PST",
            "14h30 Europe/Berlin",
            "23:45 America/New_York",
        ],
    )
    def test_roundtrip_to_string(self, time_str):
        """Test that parsing and serializing preserves hour, minute, offset."""
        t = _parse_time_string(time_str)
        s = t.isoformat()
        reparsed = _parse_time_string(s)
        assert t.hour == reparsed.hour
        assert t.minute == reparsed.minute
        assert t.second == reparsed.second
        assert t.utcoffset() == reparsed.utcoffset()

    def test_microsecond_precision_and_offset(self):
        """Ensure microseconds and offset are exact."""
        t = _parse_time_string("08:00:00.000001+01:00")
        assert t.microsecond == 1
        assert t.strftime("%z") in ("+0100", "+01:00")

    def test_parse_edge_cases(self):
        """Test parsing edge cases."""
        # Test with whitespace
        result = _parse_time_string("  14:30  ")
        assert result.hour == 14
        assert result.minute == 30

        # Test case insensitivity
        result = _parse_time_string("2:30 pm")
        assert result.hour == 14
        assert result.minute == 30

        # Test mixed case
        result = _parse_time_string("14H30")
        assert result.hour == 14
        assert result.minute == 30


class TestTime:
    """Test suite for the custom Time class."""

    def test_time_creation_basic(self):
        """Test basic Time object creation."""
        t = Time(14, 30, 45, 123456)
        assert t.hour == 14
        assert t.minute == 30
        assert t.second == 45
        assert t.microsecond == 123456
        assert t.tzinfo is None

    def test_time_creation_with_timezone(self):
        """Test Time object creation with timezone."""
        berlin_tz = pendulum.timezone("Europe/Berlin")
        t = Time(14, 30, 0, tzinfo=berlin_tz)
        assert t.hour == 14
        assert t.minute == 30
        assert t.second == 0
        assert t.tzinfo == berlin_tz

    def test_pydantic_validation_valid_time(self):
        """Test pydantic validation with valid Time object."""
        berlin_tz = pendulum.timezone("Europe/Berlin")
        pend_time = pendulum.time(14, 30, 0).replace(tzinfo=berlin_tz)

        # This should not raise an exception
        validated = Time._validate(pend_time)
        assert isinstance(validated, Time)
        assert validated.hour == 14
        assert validated.minute == 30
        assert validated.tzinfo == berlin_tz

    def test_pydantic_validation_valid_time_in_other_timezone(self, set_other_timezone):
        """Test pydantic validation with valid Time object running in different timezone."""
        timezone = set_other_timezone()

        berlin_tz = pendulum.timezone("Europe/Berlin")
        pend_time = pendulum.time(14, 30, 0).replace(tzinfo=berlin_tz)
        assert isinstance(pend_time.tzinfo, Timezone)
        assert pend_time.tzinfo == berlin_tz

        # This should not raise an exception
        validated = Time._validate(pend_time)
        assert isinstance(validated, Time)
        assert validated.hour == 14
        assert validated.minute == 30
        assert validated.tzinfo == berlin_tz

    def test_pydantic_validation_string_input(self):
        """Test pydantic validation with string input."""
        time_str = "14:30:45"
        validated = Time._validate(time_str)
        assert isinstance(validated, Time)
        assert validated.hour == 14
        assert validated.minute == 30
        assert validated.second == 45

    def test_pydantic_validation_none_input(self):
        """Test pydantic validation with None input raises ValueError."""
        with pytest.raises(ValueError, match="Time value cannot be None"):
            Time._validate(None)

    def test_pydantic_validation_invalid_input(self):
        """Test pydantic validation with invalid input."""
        with pytest.raises(ValueError, match="Invalid time value"):
            Time._validate("invalid_time")

    def test_serialization_naive_time(self):
        """Test serialization of naive Time object."""
        t = Time(14, 30, 45, 123456)
        serialized = Time._serialize(t)
        assert serialized == "14:30:45.123456"

    def test_serialization_timezone_aware_time(self):
        """Test serialization of timezone-aware Time object."""
        berlin_tz = pendulum.timezone("Europe/Berlin")
        t = Time(14, 30, 45, 123456, tzinfo=berlin_tz)
        serialized = Time._serialize(t)
        assert "14:30:45.123456" in serialized
        assert "Europe/Berlin" in serialized or "+0" in serialized

    def test_serialization_none_value(self):
        """Test serialization of None value."""
        serialized = Time._serialize(None)
        assert serialized == ""

    def test_repr_naive_time(self):
        """Test __repr__ for naive Time."""
        t = Time(14, 30, 45, 123456)
        repr_str = repr(t)
        assert "Time(14, 30, 45, 123456)" in repr_str
        assert "tzinfo" not in repr_str

    def test_repr_timezone_aware_time(self):
        """Test __repr__ for timezone-aware Time."""
        berlin_tz = pendulum.timezone("Europe/Berlin")
        t = Time(14, 30, 45, 123456, tzinfo=berlin_tz)
        repr_str = repr(t)
        assert "Time(14, 30, 45, 123456, tzinfo=" in repr_str
        assert "Europe/Berlin" in repr_str

    def test_str_representation(self):
        """Test __str__ method."""
        t = Time(14, 30, 45, 123456)
        str_repr = str(t)
        assert str_repr == "14:30:45.123456"

    def test_equality_naive_times(self):
        """Test equality comparison for naive times."""
        t1 = Time(14, 30, 45)
        t2 = Time(14, 30, 45)
        t3 = Time(14, 30, 46)

        assert t1 == t2
        assert t1 != t3

    def test_equality_timezone_aware_times(self):
        """Test equality comparison for timezone-aware times."""
        berlin_tz = pendulum.timezone("Europe/Berlin")
        utc_tz = pendulum.timezone("UTC")

        t1 = Time(14, 30, 0, tzinfo=berlin_tz)
        t2 = Time(14, 30, 0, tzinfo=berlin_tz)
        t3 = Time(14, 30, 0, tzinfo=utc_tz)

        assert t1 == t2

    def test_equality_mixed_timezone_naive(self):
        """Test equality comparison between timezone-aware and naive times."""
        berlin_tz = pendulum.timezone("Europe/Berlin")
        t1 = Time(14, 30, 0, tzinfo=berlin_tz)
        t2 = Time(14, 30, 0)  # naive

        # Mixed comparison should use direct comparison
        assert t1 == t2

    def test_hash_naive_time(self):
        """Test hash function for naive time."""
        t1 = Time(14, 30, 45)
        t2 = Time(14, 30, 45)

        assert hash(t1) == hash(t2)

        # Test that times can be used in sets
        time_set = {t1, t2}
        assert len(time_set) == 1

    def test_hash_timezone_aware_time(self):
        """Test hash function for timezone-aware time."""
        berlin_tz = pendulum.timezone("Europe/Berlin")
        t1 = Time(14, 30, 0, tzinfo=berlin_tz)
        t2 = Time(14, 30, 0, tzinfo=berlin_tz)

        assert hash(t1) == hash(t2)

    def test_is_naive(self):
        """Test is_naive method."""
        t_naive = Time(14, 30, 0)
        t_aware = Time(14, 30, 0, tzinfo=pendulum.timezone("UTC"))

        assert t_naive.is_naive() is True
        assert t_aware.is_naive() is False

    def test_is_aware(self):
        """Test is_aware method."""
        t_naive = Time(14, 30, 0)
        t_aware = Time(14, 30, 0, tzinfo=pendulum.timezone("UTC"))

        assert t_naive.is_aware() is False
        assert t_aware.is_aware() is True

    def test_replace_timezone(self):
        """Test replace_timezone method."""
        t = Time(14, 30, 0)
        berlin_tz = pendulum.timezone("Europe/Berlin")

        t_with_tz = t.replace_timezone(berlin_tz)
        assert t_with_tz.tzinfo == berlin_tz
        assert t_with_tz.hour == 14  # Time should not change

        # Test with string timezone
        t_with_str_tz = t.replace_timezone("UTC")
        assert t_with_str_tz.tzinfo == pendulum.timezone("UTC")

    def test_replace_timezone_none(self):
        """Test replace_timezone with None removes timezone."""
        berlin_tz = pendulum.timezone("Europe/Berlin")
        t = Time(14, 30, 0, tzinfo=berlin_tz)

        t_naive = t.replace_timezone(None)
        assert t_naive.tzinfo is None

    def test_format_user_friendly_basic(self):
        """Test format_user_friendly with basic options."""
        t = Time(14, 30, 45)

        # Without seconds
        formatted = t.format_user_friendly(include_seconds=False)
        assert formatted == "14:30"

        # With seconds
        formatted = t.format_user_friendly(include_seconds=True)
        assert formatted == "14:30:45"

    def test_format_user_friendly_with_timezone(self):
        """Test format_user_friendly with timezone."""
        berlin_tz = pendulum.timezone("Europe/Berlin")
        t = Time(14, 30, 45, tzinfo=berlin_tz)

        # Auto-include timezone
        formatted = t.format_user_friendly()
        assert "14:30" in formatted
        assert any(tz_indicator in formatted for tz_indicator in ["+", "-", "Z"])

    def test_now_classmethod(self):
        """Test now() class method."""
        now = Time.now()
        assert isinstance(now, Time)
        assert now.tzinfo is not None  # Should have timezone info

        # Test with specific timezone
        utc_now = Time.now("UTC")
        assert isinstance(utc_now, Time)
        assert utc_now.tzinfo == pendulum.timezone("UTC")

    def test_parse_classmethod(self):
        """Test parse() class method."""
        time_str = "14:30:45"
        parsed = Time.parse(time_str)
        assert isinstance(parsed, Time)
        assert parsed.hour == 14
        assert parsed.minute == 30
        assert parsed.second == 45

    def test_in_timezone_conversion(self):
        """Test in_timezone method for actual timezone conversion."""
        utc_tz = pendulum.timezone("UTC")
        berlin_tz = pendulum.timezone("Europe/Berlin")

        # Create UTC time
        utc_time = Time(12, 0, 0, tzinfo=utc_tz)

        # Convert to Berlin time
        berlin_time = utc_time.in_timezone(berlin_tz)
        assert isinstance(berlin_time, Time)
        assert berlin_time.tzinfo == berlin_tz
        # The actual hour will depend on DST, but it should be different from 12
        # This is a simplified test - you may need to adjust based on actual conversion logic

    def test_in_timezone_naive_time(self):
        """Test in_timezone with naive time."""
        t = Time(14, 30, 0)  # naive
        berlin_tz = pendulum.timezone("Europe/Berlin")

        result = t.in_timezone(berlin_tz)
        assert isinstance(result, Time)
        # Should assume local timezone and convert

    def test_to_local(self):
        """Test to_local method."""
        utc_tz = pendulum.timezone("UTC")
        t = Time(12, 0, 0, tzinfo=utc_tz)

        local_time = t.to_local()
        assert isinstance(local_time, Time)
        assert local_time.tzinfo == pendulum.local_timezone()

    def test_to_utc(self):
        """Test to_utc method."""
        berlin_tz = pendulum.timezone("Europe/Berlin")
        t = Time(14, 0, 0, tzinfo=berlin_tz)

        utc_time = t.to_utc()
        assert isinstance(utc_time, Time)
        assert utc_time.tzinfo == pendulum.timezone("UTC")

    def test_create_from_pendulum_time(self):
        """Test _create_from_pendulum_time class method."""
        berlin_tz = pendulum.timezone("Europe/Berlin")
        pend_time = pendulum.time(14, 30, 45, 123456).replace(tzinfo=berlin_tz)

        custom_time = Time._create_from_pendulum_time(pend_time)
        assert isinstance(custom_time, Time)
        assert custom_time.hour == 14
        assert custom_time.minute == 30
        assert custom_time.second == 45
        assert custom_time.microsecond == 123456
        assert custom_time.tzinfo == berlin_tz


# -------
# to_time
# -------


class TestToTime:
    """Test suite for the to_time function."""

    def test_to_time_string_input(self):
        """Test to_time with string input."""
        result = to_time("14:30:45")
        assert isinstance(result, Time)
        assert result.hour == 14
        assert result.minute == 30
        assert result.second == 45

    def test_to_time_time_object_input(self):
        """Test to_time with Time object input."""
        t = Time(14, 30, 45)
        result = to_time(t)
        assert isinstance(result, Time)
        assert result.hour == 14
        assert result.minute == 30
        assert result.second == 45

    def test_to_time_pendulum_time_input(self):
        """Test to_time with pendulum.Time input."""
        pend_time = pendulum.time(14, 30, 45)
        result = to_time(pend_time)
        assert isinstance(result, Time)
        assert result.hour == 14
        assert result.minute == 30
        assert result.second == 45

    def test_to_time_datetime_time_input(self):
        """Test to_time with datetime.time input."""
        dt_time = datetime.time(14, 30, 45)
        result = to_time(dt_time)
        assert isinstance(result, Time)
        assert result.hour == 14
        assert result.minute == 30
        assert result.second == 45

    def test_to_time_datetime_datetime_input(self):
        """Test to_time with datetime.datetime input."""
        dt_datetime = datetime.datetime(2023, 10, 15, 14, 30, 45)
        result = to_time(dt_datetime, in_timezone = "UTC")
        assert isinstance(result, Time)
        assert result.hour == 14
        assert result.minute == 30
        assert result.second == 45

    def test_to_time_integer_input(self):
        """Test to_time with integer input (hour only)."""
        result = to_time(14)
        assert isinstance(result, Time)
        assert result.hour == 14
        assert result.minute == 0
        assert result.second == 0

    def test_to_time_float_input(self):
        """Test to_time with float input (decimal hours)."""
        result = to_time(14.5)  # 14:30
        assert isinstance(result, Time)
        assert result.hour == 14
        assert result.minute == 30
        assert result.second == 0

    def test_to_time_tuple_input(self):
        """Test to_time with tuple input."""
        test_cases = [
            ((14,), 14, 0, 0, 0),
            ((14, 30), 14, 30, 0, 0),
            ((14, 30, 45), 14, 30, 45, 0),
            ((14, 30, 45, 123456), 14, 30, 45, 123456),
        ]

        for tuple_input, expected_hour, expected_minute, expected_second, expected_microsecond in test_cases:
            result = to_time(tuple_input)
            assert isinstance(result, Time)
            assert result.hour == expected_hour
            assert result.minute == expected_minute
            assert result.second == expected_second
            assert result.microsecond == expected_microsecond

    def test_to_time_with_timezone(self):
        """Test to_time with timezone parameter."""
        result = to_time("14:30", in_timezone="Europe/Berlin")
        assert isinstance(result, Time)
        assert result.hour == 14
        assert result.minute == 30
        assert result.tzinfo == pendulum.timezone("Europe/Berlin")

    def test_to_time_to_naive(self):
        """Test to_time with to_naive=True."""
        #result = to_time("14:30", in_timezone="Europe/Berlin", to_naive=True)
        result = to_time("14:30", to_naive=True)
        #result = to_time("14:30")
        assert isinstance(result, Time)
        assert result.hour == 14
        assert result.minute == 30
        assert result.tzinfo is None

    def test_to_time_as_string_true(self):
        """Test to_time with as_string=True."""
        result = to_time("14:30:45", as_string=True)
        assert isinstance(result, str)
        assert "14:30:45" in result

    def test_to_time_as_string_format(self):
        """Test to_time with custom format string."""
        result = to_time("14:30:45", as_string="HH:mm")
        assert isinstance(result, str)
        assert result == "14:30"

    def test_to_time_timezone_conversion(self):
        """Test to_time with timezone conversion."""
        berlin_tz = pendulum.timezone("Europe/Berlin")
        utc_tz = pendulum.timezone("UTC")

        # Create time with Berlin timezone
        berlin_time = pendulum.time(14, 30, 0).replace(tzinfo=berlin_tz)

        # Convert to UTC
        result = to_time(berlin_time, in_timezone="UTC")
        assert isinstance(result, Time)
        assert result.tzinfo == utc_tz
        # The hour should be different due to timezone conversion

    def test_to_time_invalid_timezone(self):
        """Test to_time with invalid timezone."""
        with pytest.raises(ValueError, match="Invalid timezone"):
            to_time("14:30", in_timezone="Invalid/Timezone")

    def test_to_time_invalid_input_type(self):
        """Test to_time with invalid input type."""
        with pytest.raises(ValueError, match="Unsupported type"):
            to_time({"invalid": "input"})

    def test_to_time_invalid_hour_integer(self):
        """Test to_time with invalid hour as integer."""
        with pytest.raises(ValueError, match="Hour must be between 0 and 23"):
            to_time(25)

    def test_to_time_invalid_hour_float(self):
        """Test to_time with invalid hour as float."""
        with pytest.raises(ValueError, match="Hour must be between 0 and 23"):
            to_time(25.5)

    def test_to_time_empty_tuple(self):
        """Test to_time with empty tuple."""
        with pytest.raises(ValueError, match="Empty tuple provided"):
            to_time(())

    def test_to_time_pendulum_datetime_input(self):
        """Test to_time with pendulum DateTime input."""
        dt = pendulum.datetime(2023, 10, 15, 14, 30, 45)
        result = to_time(dt, in_timezone = "UTC")
        assert isinstance(result, Time)
        assert result.hour == 14
        assert result.minute == 30
        assert result.second == 45

    def test_to_time_with_timezone_object(self):
        """Test to_time with timezone object instead of string."""
        berlin_tz = pendulum.timezone("Europe/Berlin")
        result = to_time("14:30", in_timezone=berlin_tz)
        assert isinstance(result, Time)
        assert result.tzinfo == berlin_tz

    def test_to_time_invalid_timezone_type(self):
        """Test to_time with invalid timezone type."""
        with pytest.raises(ValueError, match="Invalid timezone"):
            to_time("14:30", in_timezone=123)

    def test_to_time_microseconds_precision(self):
        """Test to_time preserves microsecond precision."""
        result = to_time("14:30:45.123456")
        assert isinstance(result, Time)
        assert result.microsecond == 123456

    def test_to_time_fallback_parsing(self):
        """Test to_time fallback parsing mechanisms."""
        # Test with a format that might not be caught by the main parser
        # This tests the fallback to pendulum.parse
        result = to_time("14:30:45")
        assert isinstance(result, Time)
        assert result.hour == 14
        assert result.minute == 30
        assert result.second == 45

    @patch('akkudoktoreos.utils.datetimeutil.logger.trace')
    def test_to_time_logging_on_parse_failures(self, mock_trace):
        """Test that parsing failures are logged appropriately."""
        # This test verifies that failed parsing attempts are logged
        with pytest.raises(ValueError):
            to_time("definitely_invalid_time_format")

        # Verify that trace logs were called for failed parsing attempts
        assert mock_trace.called

    def test_to_time_timezone_aware_datetime_input(self):
        """Test to_time with timezone-aware datetime input."""
        tz = datetime.timezone.utc
        dt = datetime.datetime(2023, 10, 15, 14, 30, 45, tzinfo=tz)
        result = to_time(dt)
        assert isinstance(result, Time)
        assert result.hour == 14
        assert result.minute == 30
        assert result.second == 45
        assert result.tzinfo is not None


# ----------------
# to_time and Time
# ----------------


class TestTimeUtilityIntegration:
    """Integration tests for the time utility functions."""

    def test_time_roundtrip_serialization(self):
        """Test that Time objects can be serialized and deserialized."""
        original = Time(14, 30, 45, 123456, tzinfo=pendulum.timezone("Europe/Berlin"))

        # Serialize
        serialized = Time._serialize(original)
        assert serialized == "14:30:45.123456 Europe/Berlin"

        # Parse back
        parsed = Time.parse(serialized)

        assert parsed.hour == original.hour
        assert parsed.minute == original.minute
        assert parsed.second == original.second
        assert parsed.microsecond == original.microsecond

    def test_time_pydantic_integration(self):
        """Test Time class integration with Pydantic models."""
        class TestModel(PydanticBaseModel):
            test_time: Time

        # Test with string input
        model = TestModel(test_time="14:30:45")
        assert isinstance(model.test_time, Time)
        assert model.test_time.hour == 14


    def test_time_class_uses_to_time_logic(self):
        """Test that Time class validation uses the same logic as to_time."""
        # Test with various inputs that both should handle identically
        test_cases = [
            "14:30",
            14.5,
            (14, 30),
            datetime.time(14, 30),
            pendulum.time(14, 30)
        ]

        class TestModel(PydanticBaseModel):
            test_time: Time

        for case in test_cases:
            # Both should produce the same result
            direct_result = to_time(case)
            model_result = TestModel(test_time=case).test_time

            assert direct_result.hour == model_result.hour
            assert direct_result.minute == model_result.minute
            assert direct_result.second == model_result.second


# ------------------------------------
# date and time types used in pydantic
# ------------------------------------

class ScheduleModel(PydanticBaseModel):
    start_time: Time
    run_duration: Duration
    scheduled_at: DateTime
    run_on: Date


class TestPendulumTypes:

    def test_valid_schedule_model(self):
        model = ScheduleModel(
            start_time="14:30:00",
            run_duration=to_duration("PT2H"),
            scheduled_at=to_datetime("2025-07-04T09:00:00+02:00"),
            run_on=to_datetime("2025-07-04")
        )

        assert isinstance(model.start_time, pendulum.Time)
        assert isinstance(model.run_duration, pendulum.Duration)
        assert isinstance(model.scheduled_at, pendulum.DateTime)
        assert isinstance(model.run_on, pendulum.Date)

        assert model.start_time.hour == 14
        assert model.run_duration.in_hours() == 2
        assert model.scheduled_at.to_date_string() == "2025-07-04"
        assert model.run_on.to_date_string() == "2025-07-04"

    def test_json_serialization(self):
        model = ScheduleModel(
            start_time=pendulum.time(6, 15),
            run_duration=pendulum.duration(minutes=45),
            scheduled_at=pendulum.datetime(2025, 7, 4, 6, 15, tz="Europe/Berlin"),
            run_on=pendulum.date(2025, 7, 4)
        )

        json_data = model.model_dump(mode="json")
        assert "06:15:00" in json_data["start_time"]
        assert "PT45M" in json_data["run_duration"]
        assert "2025-07-04T06:15:00" in json_data["scheduled_at"]
        assert "2025-07-04" in json_data["run_on"]

        json_str = model.model_dump_json()
        assert '"06:15:00' in json_str
        assert "45 minutes" in json_str
        assert "2025-07-04 06:15:00" in json_str
        assert '"2025-07-04"' in json_str

    def test_invalid_start_time(self):
        with pytest.raises(ValidationError):
            ScheduleModel(
                start_time="invalid",
                run_duration="PT1H",
                scheduled_at="2025-07-04T09:00:00+02:00",
                run_on="2025-07-04"
            )

    def test_invalid_duration(self):
        with pytest.raises(ValidationError):
            ScheduleModel(
                start_time="10:00:00",
                run_duration="2 hours",  # invalid ISO 8601 duration
                scheduled_at="2025-07-04T09:00:00+02:00",
                run_on="2025-07-04"
            )

    def test_type_coercion(self):
        dt = pendulum.datetime(2025, 7, 4, 12, 0)
        model = ScheduleModel(
            start_time=pendulum.time(12, 0),
            run_duration=pendulum.duration(hours=3),
            scheduled_at=dt,
            run_on=dt.date()
        )
        assert model.scheduled_at.hour == 12
        assert model.run_duration.total_minutes() == 180


# -----------------------------
# to_datetime
# -----------------------------


# Test cases for valid pendulum.duration inputs
@pytest.mark.parametrize(
    "test_case, local_timezone, date_input, as_string, in_timezone, to_naiv, to_maxtime, expected_output, expected_approximately",
    [
        # ---------------------------------------
        # from string to pendulum.datetime object
        # ---------------------------------------
        # - no timezone
        (
            "TC001",
            "Etc/UTC",
            "2024-01-01",
            None,
            None,
            None,
            False,
            pendulum.datetime(2024, 1, 1, 0, 0, 0, tz="Etc/UTC"),
            False,
        ),
        (
            "TC002",
            "Europe/Berlin",
            "2024-01-01",
            None,
            None,
            None,
            False,
            pendulum.datetime(2024, 1, 1, 0, 0, 0, tz="Europe/Berlin"),
            False,
        ),
        (
            "TC003",
            "Europe/Berlin",
            "2024-01-01",
            None,
            None,
            None,
            False,
            pendulum.datetime(2023, 12, 31, 23, 0, 0, tz="Etc/UTC"),
            False,
        ),
        (
            "TC004",
            "Europe/Paris",
            "2024-01-01 00:00:00",
            None,
            None,
            None,
            False,
            pendulum.datetime(2024, 1, 1, 0, 0, 0, tz="Europe/Paris"),
            False,
        ),
        (
            "TC005",
            "Etc/UTC",
            "2024-01-01 00:00:00",
            None,
            None,
            None,
            False,
            pendulum.datetime(2024, 1, 1, 1, 0, 0, tz="Europe/Berlin"),
            False,
        ),
        (
            "TC006",
            "Europe/Berlin",
            "2024-01-01 00:00:00",
            None,
            None,
            None,
            False,
            pendulum.datetime(2023, 12, 31, 23, 0, 0, tz="Etc/UTC"),
            False,
        ),
        (
            "TC007",
            "Atlantic/Canary",
            "2024-01-01 12:00:00",
            None,
            None,
            None,
            False,
            pendulum.datetime(
                2024,
                1,
                1,
                12,
                0,
                0,
                tz="Atlantic/Canary",
            ),
            False,
        ),
        (
            "TC008",
            "Etc/UTC",
            "2024-01-01 12:00:00",
            None,
            None,  # force local timezone
            None,
            False,
            pendulum.datetime(2024, 1, 1, 13, 0, 0, tz="Europe/Berlin"),
            False,
        ),
        (
            "TC009",
            "Europe/Berlin",
            "2024-01-01 12:00:00",
            None,
            None,
            None,
            False,
            pendulum.datetime(2024, 1, 1, 11, 0, 0, tz="Etc/UTC"),
            False,
        ),
        # - with timezone
        (
            "TC010",
            "Etc/UTC",
            "02/02/24",
            None,
            "Europe/Berlin",
            None,
            False,
            pendulum.datetime(2024, 2, 2, 0, 0, 0, tz="Europe/Berlin"),
            False,
        ),
        (
            "TC011",
            "Etc/UTC",
            "2024-03-03T10:20:30.000+01:00",  # No dalight saving time at this date
            None,
            "Europe/Berlin",
            None,
            None,
            pendulum.datetime(2024, 3, 3, 10, 20, 30, 0, tz="Europe/Berlin"),
            False,
        ),
        (
            "TC012",
            "Etc/UTC",
            "2024-04-04T10:20:30.000+02:00",
            None,
            "Europe/Berlin",
            False,
            None,
            pendulum.datetime(2024, 4, 4, 10, 20, 30, 0, tz="Europe/Berlin"),
            False,
        ),
        (
            "TC013",
            "Etc/UTC",
            "2024-05-05T10:20:30.000+02:00",
            None,
            "Europe/Berlin",
            True,
            None,
            pendulum.naive(2024, 5, 5, 10, 20, 30, 0),
            False,
        ),
        # - without local timezone as UTC
        (
            "TC014",
            "UTC",
            "2024-01-03",
            None,
            "UTC",
            None,
            False,
            pendulum.datetime(2024, 1, 3, 0, 0, 0, tz="UTC"),
            False,
        ),
        (
            "TC015",
            "Atlantic/Canary",
            "02/02/24",
            None,
            "UTC",
            None,
            False,
            pendulum.datetime(2024, 2, 2, 0, 0, 0, tz="UTC"),
            False,
        ),
        (
            "TC016",
            "Atlantic/Canary",
            "2024-03-03T10:20:30.000Z",  # No dalight saving time at this date
            None,
            None,
            None,
            None,
            pendulum.datetime(2024, 3, 3, 10, 20, 30, 0, tz="UTC"),
            False,
        ),
        # ---------------------------------------
        # from pendulum.datetime to pendulum.datetime object
        # ---------------------------------------
        (
            "TC017",
            "Atlantic/Canary",
            pendulum.datetime(2024, 4, 4, 0, 0, 0),
            None,
            None,
            None,
            False,
            pendulum.datetime(2024, 4, 4, 0, 0, 0, tz="Etc/UTC"),
            False,
        ),
        (
            "TC018",
            "Atlantic/Canary",
            pendulum.datetime(2024, 4, 4, 1, 0, 0),
            None,
            "Europe/Berlin",
            None,
            False,
            pendulum.datetime(2024, 4, 4, 3, 0, 0, tz="Europe/Berlin"),
            False,
        ),
        (
            "TC019",
            "Atlantic/Canary",
            pendulum.datetime(2024, 4, 4, 1, 0, 0, tz="Etc/UTC"),
            None,
            "Europe/Berlin",
            None,
            False,
            pendulum.datetime(2024, 4, 4, 3, 0, 0, tz="Europe/Berlin"),
            False,
        ),
        (
            "TC020",
            "Atlantic/Canary",
            pendulum.datetime(2024, 4, 4, 2, 0, 0, tz="Europe/Berlin"),
            None,
            "Etc/UTC",
            None,
            False,
            pendulum.datetime(2024, 4, 4, 0, 0, 0, tz="Etc/UTC"),
            False,
        ),
        # ---------------------------------------
        # from string to UTC string
        # ---------------------------------------
        # - no timezone
        #   local timezone UTC
        (
            "TC021",
            "Etc/UTC",
            "2023-11-06T00:00:00",
            "UTC",
            None,
            None,
            None,
            "2023-11-06T00:00:00Z",
            False,
        ),
        #    local timezone "Europe/Berlin"
        (
            "TC022",
            "Europe/Berlin",
            "2023-11-06T00:00:00",
            "UTC",
            "Europe/Berlin",
            None,
            None,
            "2023-11-05T23:00:00Z",
            False,
        ),
        # - no microseconds
        (
            "TC023",
            "Atlantic/Canary",
            "2024-10-30T00:00:00+01:00",
            "UTC",
            None,
            None,
            None,
            "2024-10-29T23:00:00Z",
            False,
        ),
        (
            "TC024",
            "Atlantic/Canary",
            "2024-10-30T01:00:00+01:00",
            "utc",
            None,
            None,
            None,
            "2024-10-30T00:00:00Z",
            False,
        ),
        # - with microseconds
        (
            "TC025",
            "Atlantic/Canary",
            "2024-10-07T10:20:30.000+02:00",
            "UTC",
            None,
            None,
            None,
            "2024-10-07T08:20:30Z",
            False,
        ),
        # ---------------------------------------
        # from None to pendulum.datetime object
        # ---------------------------------------
        # - no timezone
        #   local timezone
        (
            "TC026",
            None,
            None,
            None,
            None,
            None,
            None,
            pendulum.now(),
            True,
        ),
    ],
)
def test_to_datetime(
    set_other_timezone,
    test_case,
    local_timezone,
    date_input,
    as_string,
    in_timezone,
    to_naiv,
    to_maxtime,
    expected_output,
    expected_approximately,
):
    """Test pendulum.datetime conversion with valid inputs."""
    set_other_timezone(local_timezone)
    result = to_datetime(
        date_input,
        as_string=as_string,
        in_timezone=in_timezone,
        to_naiv=to_naiv,
        to_maxtime=to_maxtime,
    )
    # if isinstance(date_input, str):
    #    print(f"Input:    {date_input}")
    # else:
    #    print(f"Input:    {date_input} tz={date_input.timezone}")
    if isinstance(expected_output, str):
        # print(f"Expected: {expected_output}")
        # print(f"Result:   {result}")
        assert result == expected_output
    elif expected_output.timezone is None:
        # We expect an exception
        with pytest.raises(TypeError):
            assert compare_datetimes(result, expected_output).equal
    else:
        compare = compare_datetimes(result, expected_output)
        # print(f"---- Testcase:  {test_case} ----")
        # print(f"Expected: {expected_output} tz={expected_output.timezone}")
        # print(f"Result:   {result} tz={result.timezone}")
        # print(f"Compare:  {compare}")
        if expected_approximately:
            # Compare using Unix timestamps to avoid pendulum timezone state
            # issues and to prevent staleness from parametrize collection time.
            import time

            assert abs(result.timestamp() - time.time()) < 300
        else:
            assert compare.equal == True


# -----------------------------
# to_duration
# -----------------------------

class TestToDuration:
    # ------------------------------------------------------------------
    # Valid input conversions (no formatting)
    # ------------------------------------------------------------------
    @pytest.mark.parametrize(
        "input_value, expected_output",
        [
            # duration input
            (pendulum.duration(days=1), pendulum.duration(days=1)),

            # String input
            ("1 hour", pendulum.duration(hours=1)),
            ("2 days", pendulum.duration(days=2)),
            ("5 hours", pendulum.duration(hours=5)),
            ("47 hours", pendulum.duration(hours=47)),
            ("48 hours", pendulum.duration(seconds=48 * 3600)),
            ("30 minutes", pendulum.duration(minutes=30)),
            ("45 seconds", pendulum.duration(seconds=45)),
            (
                "1 day 2 hours 30 minutes 15 seconds",
                pendulum.duration(days=1, hours=2, minutes=30, seconds=15),
            ),
            ("3 days 4 hours", pendulum.duration(days=3, hours=4)),

            # Integer / Float
            (3600, pendulum.duration(seconds=3600)),
            (86400, pendulum.duration(days=1)),
            (1800.5, pendulum.duration(seconds=1800.5)),

            # Tuple / List
            ((1, 2, 30, 15), pendulum.duration(days=1, hours=2, minutes=30, seconds=15)),
            ([0, 10, 0, 0], pendulum.duration(hours=10)),
        ],
    )
    def test_to_duration_valid(self, input_value, expected_output):
        """Test that valid inputs convert to correct Duration objects."""
        assert to_duration(input_value) == expected_output

    # ------------------------------------------------------------------
    # ISO-8601 output (`as_string=True`)
    # ------------------------------------------------------------------
    @pytest.mark.parametrize(
        "input_value, expected",
        [
            ("15 minutes", "PT15M"),
            ("1 hour 30 minutes", "PT1H30M"),
            ("45 seconds", "PT45S"),
            ("1 hour 5 seconds", "PT1H5S"),
            ("2 days", "P2D"),
            ("2 days 3 hours 4 minutes 5 seconds", "P2DT3H4M5S"),
            ("0 seconds", "PT0S"),
        ]
    )
    def test_as_string_true_iso8601(self, input_value, expected):
        """Test ISO-8601 duration strings for various inputs."""
        assert to_duration(input_value, as_string=True) == expected

    # ------------------------------------------------------------------
    # Human readable (`as_string="human"`)
    # ------------------------------------------------------------------
    def test_as_string_human(self):
        assert to_duration("90 seconds", as_string="human") == "1 minute 30 seconds"

    # ------------------------------------------------------------------
    # Pandas frequency (`as_string="pandas"`)
    # ------------------------------------------------------------------
    @pytest.mark.parametrize(
        "input_value, expected",
        [
            ("1 hour", "1h"),
            ("2 hours", "2h"),
            ("15 minutes", "15min"),
            ("90 minutes", "90min"),
            ("30 seconds", "30s"),
            ("900 seconds", "15min"),
        ],
    )
    def test_as_string_pandas(self, input_value, expected):
        assert to_duration(input_value, as_string="pandas") == expected

    # ------------------------------------------------------------------
    # Custom format strings
    # ------------------------------------------------------------------
    def test_as_string_custom_seconds(self):
        assert to_duration("75 seconds", as_string="Total: {S}s") == "Total: 75s"

    def test_as_string_custom_minutes(self):
        assert to_duration("15 minutes", as_string="{M}m total") == "15m total"

    def test_as_string_custom_hours(self):
        assert to_duration("7200 seconds", as_string="{H} hours") == "2 hours"

    def test_as_string_custom_human_alias(self):
        assert to_duration("30 minutes", as_string="{f}") == "30 minutes"

    # ------------------------------------------------------------------
    # Invalid input handling
    # ------------------------------------------------------------------
    @pytest.mark.parametrize(
        "input_value",
        [
            "not a duration",
            "5 lightyears",
            (1, 2, 3),             # wrong tuple size
            {"a": 1},              # unsupported type
            None,
        ],
    )
    def test_invalid_inputs_raise(self, input_value):
        with pytest.raises(ValueError):
            to_duration(input_value)

    # ------------------------------------------------------------------
    # Invalid as_string values
    # ------------------------------------------------------------------
    def test_invalid_as_string_raises(self):
        with pytest.raises(ValueError):
            to_duration("5 minutes", as_string=123) # type: ignore

    def test_summation(self):
        start_datetime = to_datetime("2028-01-11 00:00:00")
        index_datetime = start_datetime
        for i in range(48):
            expected_datetime = start_datetime + to_duration(f"{i} hours")
            assert index_datetime == expected_datetime
            index_datetime += to_duration("1 hour")
        assert index_datetime == to_datetime("2028-01-13 00:00:00")

    def test_excessive_length_raises_valueerror(self):
        """Test that to_duration raises ValueError for strings exceeding max length.

        This test covers the fix for the ReDoS vulnerability.
        Related to: #494
        """
        # String exceeds limits
        long_string = "a" * (MAX_DURATION_STRING_LENGTH + 50)

        # Expected Errormessage – ESCAPED für Regex
        expected_error_message = re.escape(
            f"Input string exceeds maximum allowed length ({MAX_DURATION_STRING_LENGTH})."
        )

        # Check if error was raised
        with pytest.raises(ValueError, match=expected_error_message):
            to_duration(long_string)

        # Optional: String exactly at the limit should NOT trigger the length check.
        at_limit_string = "b" * MAX_DURATION_STRING_LENGTH
        try:
            to_duration(at_limit_string)
        except ValueError as e:
            if str(e) == f"Input string exceeds maximum allowed length ({MAX_DURATION_STRING_LENGTH}).":
                pytest.fail(
                    f"to_duration raised length ValueError unexpectedly for string at limit: {at_limit_string}"
                )
            pass


# -----------------------------
# to_timezone
# -----------------------------


def test_to_timezone_string():
    """Test to_timezone function returns correct timezone as a string."""
    location = (40.7128, -74.0060)  # New York City coordinates
    result = to_timezone(location=location, as_string=True)
    assert result == "America/New_York", "Expected timezone string 'America/New_York'"


def test_to_timezone_timezone():
    """Test to_timezone function returns correct timezone as a Timezone object."""
    location = (40.7128, -74.0060)  # New York City coordinates
    result = to_timezone(location=location)
    assert isinstance(result, Timezone), "Expected a Timezone object"
    assert result.name == "America/New_York", "Expected Timezone name 'America/New_York'"


def test_to_timezone_invalid_coordinates():
    """Test to_timezone function handles invalid coordinates gracefully."""
    location = (100.0, 200.0)  # Invalid coordinates outside Earth range
    with pytest.raises(ValueError, match="Invalid latitude/longitude"):
        to_timezone(location=location, as_string=True)


# -----------------------------
# hours_in_day
# -----------------------------


@pytest.mark.parametrize(
    "local_timezone, date, in_timezone, expected_hours",
    [
        ("Etc/UTC", "2024-11-10 00:00:00", "Europe/Berlin", 24),  # No DST in Germany
        ("Etc/UTC", "2024-08-10 00:00:00", "Europe/Berlin", 24),  # DST in Germany
        ("Etc/UTC", "2024-03-31 00:00:00", "Europe/Berlin", 23),  # DST change (23 hours/ day)
        ("Etc/UTC", "2024-10-27 00:00:00", "Europe/Berlin", 25),  # DST change (25 hours/ day)
        ("Europe/Berlin", "2024-11-10 00:00:00", "Europe/Berlin", 24),  # No DST in Germany
        ("Europe/Berlin", "2024-08-10 00:00:00", "Europe/Berlin", 24),  # DST in Germany
        ("Europe/Berlin", "2024-03-31 00:00:00", "Europe/Berlin", 23),  # DST change (23 hours/ day)
        ("Europe/Berlin", "2024-10-27 00:00:00", "Europe/Berlin", 25),  # DST change (25 hours/ day)
    ],
)
def test_hours_in_day(set_other_timezone, local_timezone, date, in_timezone, expected_hours):
    """Test the `test_hours_in_day` function."""
    set_other_timezone(local_timezone)
    date_input = to_datetime(date, in_timezone=in_timezone)
    assert date_input.timezone.name == in_timezone
    assert hours_in_day(date_input) == expected_hours


# -----------------------------
# compare_datetimes
# -----------------------------


@pytest.mark.parametrize(
    "dt1, dt2, equal, ge, gt, le, lt",
    [
        # Same time in the same timezone
        (
            pendulum.datetime(2024, 3, 15, 12, 0, 0, tz="UTC"),
            pendulum.datetime(2024, 3, 15, 12, 0, 0, tz="UTC"),
            True,
            True,
            False,
            True,
            False,
        ),
        (
            pendulum.datetime(2024, 4, 4, 0, 0, 0, tz="Europe/Berlin"),
            pendulum.datetime(2024, 4, 4, 0, 0, 0, tz="Europe/Berlin"),
            True,
            True,
            False,
            True,
            False,
        ),
        # Same instant in different timezones (converted to UTC)
        (
            pendulum.datetime(2024, 3, 15, 8, 0, 0, tz="Europe/Berlin"),
            pendulum.datetime(2024, 3, 15, 7, 0, 0, tz="UTC"),
            True,
            True,
            False,
            True,
            False,
        ),
        # Different times across timezones (converted to UTC)
        (
            pendulum.datetime(2024, 3, 15, 8, 0, 0, tz="America/New_York"),
            pendulum.datetime(2024, 3, 15, 12, 0, 0, tz="UTC"),
            True,
            True,
            False,
            True,
            False,
        ),
    ],
)
def test_compare_datetimes_equal(dt1, dt2, equal, ge, gt, le, lt):
    # requal = compare_datetimes(dt1, dt2).equal
    # rgt = compare_datetimes(dt1, dt2).gt
    # rge = compare_datetimes(dt1, dt2).ge
    # rlt = compare_datetimes(dt1, dt2).lt
    # rle = compare_datetimes(dt1, dt2).le
    # print(f"{dt1} vs. {dt2}: expected equal={equal}, ge={ge}, gt={gt}, le={le}, lt={lt}")
    # print(f"{dt1} vs. {dt2}: result   equal={requal}, ge={rge}, gt={rgt}, le={rle}, lt={rlt}")
    assert compare_datetimes(dt1, dt2).equal == equal
    assert compare_datetimes(dt1, dt2).ge == ge
    assert compare_datetimes(dt1, dt2).gt == gt
    assert compare_datetimes(dt1, dt2).le == le
    assert compare_datetimes(dt1, dt2).lt == lt


@pytest.mark.parametrize(
    "dt1, dt2, equal, ge, gt, le, lt",
    [
        # Different times in the same timezone
        (
            pendulum.datetime(2024, 3, 15, 11, 0, 0, tz="UTC"),
            pendulum.datetime(2024, 3, 15, 12, 0, 0, tz="UTC"),
            False,
            False,
            False,
            True,
            True,
        ),
        # Different times across timezones (converted to UTC)
        (
            pendulum.datetime(2024, 3, 15, 6, 0, 0, tz="America/New_York"),
            pendulum.datetime(2024, 3, 15, 12, 0, 0, tz="UTC"),
            False,
            False,
            False,
            True,
            True,
        ),
        # DST changes: spring forward
        (
            pendulum.datetime(2024, 3, 10, 1, 59, 0, tz="America/New_York"),
            pendulum.datetime(2024, 3, 10, 3, 0, 0, tz="America/New_York"),
            False,
            False,
            False,
            True,
            True,
        ),
        # DST changes: fall back
        (
            pendulum.datetime(2024, 11, 3, 1, 0, 0, tz="America/New_York"),
            pendulum.datetime(2024, 11, 3, 1, 30, 0, tz="America/New_York"),
            False,
            False,
            False,
            True,
            True,
        ),
    ],
)
def test_compare_datetimes_lt(dt1, dt2, equal, ge, gt, le, lt):
    # requal = compare_datetimes(dt1, dt2).equal
    # rgt = compare_datetimes(dt1, dt2).gt
    # rge = compare_datetimes(dt1, dt2).ge
    # rlt = compare_datetimes(dt1, dt2).lt
    # rle = compare_datetimes(dt1, dt2).le
    # print(f"{dt1} vs. {dt2}: expected equal={equal}, ge={ge}, gt={gt}, le={le}, lt={lt}")
    # print(f"{dt1} vs. {dt2}: result   equal={requal}, ge={rge}, gt={rgt}, le={rle}, lt={rlt}")
    assert compare_datetimes(dt1, dt2).equal == equal
    assert compare_datetimes(dt1, dt2).ge == ge
    assert compare_datetimes(dt1, dt2).gt == gt
    assert compare_datetimes(dt1, dt2).le == le
    assert compare_datetimes(dt1, dt2).lt == lt


@pytest.mark.parametrize(
    "dt1, dt2",
    [
        # Different times in the same timezone
        (
            pendulum.datetime(2024, 3, 15, 13, 0, 0, tz="UTC"),
            pendulum.datetime(2024, 3, 15, 12, 0, 0, tz="UTC"),
        ),
    ],
)
def test_compare_datetimes_gt(dt1, dt2):
    # requal = compare_datetimes(dt1, dt2).equal
    # rgt = compare_datetimes(dt1, dt2).gt
    # rge = compare_datetimes(dt1, dt2).ge
    # rlt = compare_datetimes(dt1, dt2).lt
    # rle = compare_datetimes(dt1, dt2).le
    # print(f"{dt1} vs. {dt2}: expected equal={equal}, ge={ge}, gt={gt}, le={le}, lt={lt}")
    # print(f"{dt1} vs. {dt2}: result   equal={requal}, ge={rge}, gt={rgt}, le={rle}, lt={rlt}")
    assert compare_datetimes(dt1, dt2).equal == False
    assert compare_datetimes(dt1, dt2).ge
    assert compare_datetimes(dt1, dt2).gt
    assert compare_datetimes(dt1, dt2).le == False
    assert compare_datetimes(dt1, dt2).lt == False
