"""
Deep behavioral tests for core utilities.
Tests utc_now, generate_card_id, get_local_date_string, and utility patterns.
"""

from datetime import datetime, timezone, timedelta

from local_deep_research.news.core.utils import (
    generate_card_id,
    utc_now,
)


# --- utc_now ---


class TestUtcNow:
    """Tests for utc_now utility."""

    def test_returns_datetime(self):
        result = utc_now()
        assert isinstance(result, datetime)

    def test_has_timezone_info(self):
        result = utc_now()
        assert result.tzinfo is not None

    def test_is_utc(self):
        result = utc_now()
        assert result.tzinfo == timezone.utc

    def test_is_recent(self):
        before = datetime.now(timezone.utc)
        result = utc_now()
        after = datetime.now(timezone.utc)
        assert before <= result <= after

    def test_consecutive_calls_ordered(self):
        t1 = utc_now()
        t2 = utc_now()
        assert t2 >= t1

    def test_has_year(self):
        result = utc_now()
        assert result.year >= 2025

    def test_has_microseconds(self):
        """Should include microsecond precision."""
        result = utc_now()
        # Just verify it's a valid datetime
        assert 0 <= result.microsecond < 1000000


# --- generate_card_id ---


class TestGenerateCardIdDeep:
    """Tests for generate_card_id utility."""

    def test_returns_string(self):
        result = generate_card_id()
        assert isinstance(result, str)

    def test_non_empty(self):
        assert len(generate_card_id()) > 0

    def test_unique_across_many(self):
        ids = {generate_card_id() for _ in range(500)}
        assert len(ids) == 500

    def test_contains_only_valid_chars(self):
        result = generate_card_id()
        # UUID format: hex digits and hyphens
        valid_chars = set("0123456789abcdef-")
        assert all(c in valid_chars for c in result)

    def test_consistent_length(self):
        lengths = {len(generate_card_id()) for _ in range(50)}
        assert len(lengths) == 1  # All same length

    def test_uuid_format(self):
        """Should be a valid UUID format."""
        import uuid

        result = generate_card_id()
        # Should not raise
        uuid.UUID(result)


# --- Date string patterns ---


class TestDateStringPatterns:
    """Tests for date string formatting patterns used across the codebase."""

    def test_isoformat_includes_t(self):
        dt = datetime(2025, 6, 15, 12, 0, 0, tzinfo=timezone.utc)
        assert "T" in dt.isoformat()

    def test_isoformat_includes_timezone(self):
        dt = datetime(2025, 6, 15, 12, 0, 0, tzinfo=timezone.utc)
        iso = dt.isoformat()
        assert "+00:00" in iso

    def test_none_isoformat_pattern(self):
        """The 'x.isoformat() if x else None' pattern."""
        x = None
        result = x.isoformat() if x else None
        assert result is None

    def test_datetime_isoformat_pattern(self):
        x = datetime(2025, 6, 15, tzinfo=timezone.utc)
        result = x.isoformat() if x else None
        assert "2025-06-15" in result

    def test_z_suffix_replacement(self):
        """The .replace('Z', '+00:00') pattern."""
        ts = "2025-06-15T12:00:00Z"
        result = ts.replace("Z", "+00:00")
        assert result == "2025-06-15T12:00:00+00:00"

    def test_fromisoformat_roundtrip(self):
        dt = datetime(2025, 6, 15, 12, 0, 0, tzinfo=timezone.utc)
        iso = dt.isoformat()
        parsed = datetime.fromisoformat(iso)
        assert parsed == dt


# --- Timedelta patterns ---


class TestTimedeltaPatterns:
    """Tests for timedelta calculation patterns used in the codebase."""

    def test_minutes_to_timedelta(self):
        minutes = 60
        delta = timedelta(minutes=minutes)
        assert delta.total_seconds() == 3600

    def test_hours_to_timedelta(self):
        hours = 24
        delta = timedelta(hours=hours)
        assert delta.days == 1

    def test_seconds_to_timedelta(self):
        seconds = 1800
        delta = timedelta(seconds=seconds)
        assert delta.total_seconds() == 1800

    def test_next_refresh_calculation(self):
        now = datetime(2025, 6, 15, 12, 0, 0, tzinfo=timezone.utc)
        refresh_minutes = 30
        next_refresh = now + timedelta(minutes=refresh_minutes)
        assert next_refresh.minute == 30

    def test_retention_cutoff(self):
        now = datetime(2025, 6, 15, 12, 0, 0, tzinfo=timezone.utc)
        retention_hours = 48
        cutoff = now - timedelta(hours=retention_hours)
        assert cutoff.day == 13

    def test_jitter_range(self):
        """Jitter should be within max_jitter_seconds."""
        import random

        max_jitter = 300
        jitter = random.randint(0, max_jitter)
        assert 0 <= jitter <= 300


# --- Dict merge patterns ---


class TestDictMergePatterns:
    """Tests for dict merge patterns used in card/subscription construction."""

    def test_update_existing(self):
        base = {"a": 1, "b": 2}
        extra = {"b": 3, "c": 4}
        base.update(extra)
        assert base == {"a": 1, "b": 3, "c": 4}

    def test_get_with_default(self):
        d = {"key": "value"}
        assert d.get("key", "default") == "value"
        assert d.get("missing", "default") == "default"

    def test_none_or_default(self):
        """The 'x or default' pattern."""
        assert (None or "default") == "default"
        assert ("" or "default") == "default"
        assert (0 or "default") == "default"
        assert ("value" or "default") == "value"

    def test_nested_dict_get(self):
        d = {"extra_data": {"key": "val"}}
        extra = d.get("extra_data", {})
        assert extra.get("key") == "val"

    def test_empty_extra_data(self):
        d = {}
        extra = d.get("extra_data", {}) or {}
        assert extra == {}

    def test_none_extra_data(self):
        d = {"extra_data": None}
        extra = d.get("extra_data", {}) or {}
        assert extra == {}


# --- JSON handling patterns ---


class TestJsonHandlingPatterns:
    """Tests for JSON handling patterns used in the codebase."""

    def test_json_dumps_loads(self):
        import json

        data = {"key": "value", "count": 42}
        serialized = json.dumps(data)
        deserialized = json.loads(serialized)
        assert deserialized == data

    def test_json_loads_string(self):
        import json

        result = json.loads('{"key": "val"}')
        assert result == {"key": "val"}

    def test_json_loads_invalid(self):
        import json

        with __import__("pytest").raises(json.JSONDecodeError):
            json.loads("not json")

    def test_isinstance_dict_check(self):
        """Pattern: isinstance(x, dict) before json.loads."""
        x = {"key": "val"}
        if isinstance(x, dict):
            result = x
        else:
            import json

            result = json.loads(x)
        assert result == {"key": "val"}

    def test_isinstance_string_check(self):
        import json

        x = '{"key": "val"}'
        if isinstance(x, dict):
            result = x
        else:
            result = json.loads(x)
        assert result == {"key": "val"}


# --- String manipulation patterns ---


class TestStringManipulationPatterns:
    """Tests for string manipulation patterns used in the codebase."""

    def test_title_truncation_with_ellipsis(self):
        title = "A" * 60
        result = title[:50] + "..." if len(title) > 50 else title
        assert len(result) == 53
        assert result.endswith("...")

    def test_title_no_truncation(self):
        title = "Short title"
        result = title[:50] + "..." if len(title) > 50 else title
        assert result == "Short title"

    def test_query_lower(self):
        query = "Breaking News About AI"
        assert query.lower() == "breaking news about ai"

    def test_domain_extraction(self):
        url = "https://www.example.com/path/to/page"
        domain = url.split("//")[-1].split("/")[0]
        assert domain == "www.example.com"

    def test_www_removal(self):
        domain = "www.example.com"
        clean = domain.replace("www.", "")
        assert clean == "example.com"

    def test_citation_regex(self):
        import re

        title = "[12, 26, 19] Article Title"
        clean = re.sub(r"^\[[^\]]+\]\s*", "", title).strip()
        assert clean == "Article Title"

    def test_citation_regex_no_match(self):
        import re

        title = "Regular Title"
        clean = re.sub(r"^\[[^\]]+\]\s*", "", title).strip()
        assert clean == "Regular Title"

    def test_split_url_prefix(self):
        line = "URL: https://example.com/article"
        url = line.split("URL:", 1)[1].strip()
        assert url == "https://example.com/article"

    def test_startswith_http(self):
        assert "https://example.com".startswith("http")
        assert "http://example.com".startswith("http")
        assert not "ftp://example.com".startswith("http")
