"""Tests for utility functions including date parsing."""

from datetime import UTC, date, datetime

import pytest

from yukka.utils import DATA_API_URL, DATE_FORMATTER, METADATA_API_URL, to_datetime


class TestToDatetime:
    """Tests for to_datetime function."""

    def test_datetime_passthrough(self):
        """Datetime input should be passed through unchanged."""
        dt = datetime(2026, 1, 15, 10, 30, 45)
        result = to_datetime(dt)

        assert result == dt
        assert isinstance(result, datetime)

    def test_date_to_datetime(self):
        """Date input should be converted to datetime at midnight."""
        d = date(2026, 1, 15)
        result = to_datetime(d)

        assert isinstance(result, datetime)
        assert result.year == 2026
        assert result.month == 1
        assert result.day == 15
        assert result.hour == 0
        assert result.minute == 0
        assert result.second == 0

    def test_float_unix_timestamp(self):
        """Float input should be interpreted as Unix timestamp."""
        # 2026-01-15 00:00:00 UTC in Unix timestamp
        timestamp = 1768435200.0
        result = to_datetime(timestamp)

        assert isinstance(result, datetime)
        # Note: exact date depends on local timezone
        assert result.year >= 2026

    def test_string_date_formatter_format(self):
        """String in DATE_FORMATTER format should be parsed correctly."""
        date_str = "2026-01-15"
        result = to_datetime(date_str)

        assert isinstance(result, datetime)
        assert result.year == 2026
        assert result.month == 1
        assert result.day == 15

    def test_string_iso_format(self):
        """String in ISO format should be parsed correctly."""
        date_str = "2026-01-15T10:30:45"
        result = to_datetime(date_str)

        assert isinstance(result, datetime)
        assert result.year == 2026
        assert result.month == 1
        assert result.day == 15
        assert result.hour == 10
        assert result.minute == 30
        assert result.second == 45

    def test_string_iso_format_with_timezone(self):
        """String in ISO format with timezone should be parsed correctly."""
        date_str = "2026-01-15T10:30:45+00:00"
        result = to_datetime(date_str)

        assert isinstance(result, datetime)
        assert result.year == 2026

    def test_unsupported_type_raises_error(self):
        """Unsupported type should raise TypeError."""
        with pytest.raises(TypeError) as exc_info:
            to_datetime([2026, 1, 15])  # type: ignore[arg-type]

        assert "not supported" in str(exc_info.value).lower()

    def test_invalid_string_format_raises_error(self):
        """Invalid string format should raise ValueError."""
        with pytest.raises(ValueError, match="not-a-date"):
            to_datetime("not-a-date")

    def test_invalid_date_string_raises_error(self):
        """Invalid date values in string should raise ValueError."""
        with pytest.raises(ValueError, match="2026-13-45"):
            to_datetime("2026-13-45")  # Invalid month and day


class TestDateFormats:
    """Tests for various date format edge cases."""

    def test_leap_year_date(self):
        """Leap year date should be handled correctly."""
        # 2024 is a leap year
        date_str = "2024-02-29"
        result = to_datetime(date_str)

        assert result.month == 2
        assert result.day == 29

    def test_end_of_month_dates(self):
        """End of month dates should be handled correctly."""
        dates = [
            "2026-01-31",
            "2026-02-28",
            "2026-03-31",
            "2026-04-30",
        ]

        for date_str in dates:
            result = to_datetime(date_str)
            assert isinstance(result, datetime)

    def test_year_boundary_dates(self):
        """Year boundary dates should be handled correctly."""
        dates = [
            "2025-12-31",
            "2026-01-01",
        ]

        for date_str in dates:
            result = to_datetime(date_str)
            assert isinstance(result, datetime)

    def test_historical_date(self):
        """Historical date should be handled correctly."""
        date_str = "2000-01-01"
        result = to_datetime(date_str)

        assert result.year == 2000

    def test_future_date(self):
        """Future date should be handled correctly."""
        date_str = "2030-12-31"
        result = to_datetime(date_str)

        assert result.year == 2030


class TestAPIURLs:
    """Tests for API URL constants."""

    def test_data_api_url_format(self):
        """DATA_API_URL should be a valid URL."""
        url_str = str(DATA_API_URL)

        assert url_str.startswith("https://")
        assert "yukkalab.com" in url_str
        assert "data-dev.api" in url_str

    def test_metadata_api_url_format(self):
        """METADATA_API_URL should be a valid URL."""
        url_str = str(METADATA_API_URL)

        assert url_str.startswith("https://")
        assert "yukkalab.com" in url_str
        assert "metadata.api" in url_str

    def test_date_formatter_constant(self):
        """DATE_FORMATTER should be valid strftime format."""
        dt = datetime(2026, 1, 15)
        formatted = dt.strftime(DATE_FORMATTER)

        assert formatted == "2026-01-15"


class TestDatetimeNoneHandling:
    """Tests for None and edge case handling."""

    def test_none_input_raises_error(self):
        """None input should raise TypeError."""
        with pytest.raises(TypeError):
            to_datetime(None)  # type: ignore[arg-type]

    def test_empty_string_raises_error(self):
        """Empty string should raise ValueError."""
        with pytest.raises(ValueError, match="isoformat"):
            to_datetime("")

    def test_whitespace_string_raises_error(self):
        """Whitespace-only string should raise ValueError."""
        with pytest.raises(ValueError, match="isoformat"):
            to_datetime("   ")

    def test_integer_input_raises_error(self):
        """Integer input should raise TypeError (float expected for timestamp)."""
        with pytest.raises(TypeError):
            to_datetime(1768435200)


class TestDatetimePreservation:
    """Tests for datetime property preservation."""

    def test_datetime_microseconds_preserved(self):
        """Microseconds in datetime should be preserved."""
        dt = datetime(2026, 1, 15, 10, 30, 45, 123456)
        result = to_datetime(dt)

        assert result.microsecond == 123456

    def test_datetime_with_tzinfo_preserved(self):
        """Timezone info in datetime should be preserved."""
        dt = datetime(2026, 1, 15, 10, 30, 45, tzinfo=UTC)
        result = to_datetime(dt)

        assert result.tzinfo == UTC

    def test_date_converted_to_midnight(self):
        """Date should be converted to midnight (00:00:00)."""
        d = date(2026, 1, 15)
        result = to_datetime(d)

        assert result.hour == 0
        assert result.minute == 0
        assert result.second == 0
        assert result.microsecond == 0
