"""Tests for spreading activation — hop 1-2 context expansion."""

from __future__ import annotations

import uuid
from datetime import UTC, datetime

from conftest import make_fact

from arandu.read._helpers import is_fact_active, is_key_allowed
from arandu.read.spreading_activation import (
    SpreadingActivationResult,
    _compute_avg_seed_score,
    _entity_from_fact,
    _parse_uuids,
    _score_fact,
)

# ---------------------------------------------------------------------------
# Helper function tests
# ---------------------------------------------------------------------------


def test_entity_from_fact_with_colon():
    """Fact with colon in entity_key returns it directly."""
    fact = make_fact(entity_key="person:kevin", entity_type="person")
    assert _entity_from_fact(fact) == "person:kevin"


def test_entity_from_fact_without_colon():
    """Fact without colon gets entity_type prefix."""
    fact = make_fact(entity_key="kevin", entity_type="person")
    assert _entity_from_fact(fact) == "person:kevin"


def test_parse_uuids_valid():
    """Valid UUID strings should be parsed."""
    uid = uuid.uuid4()
    result = _parse_uuids([str(uid)])
    assert len(result) == 1
    assert result[0] == uid


def test_parse_uuids_invalid():
    """Invalid UUID strings should be skipped."""
    result = _parse_uuids(["not-a-uuid", "also-invalid"])
    assert result == []


def test_parse_uuids_mixed():
    """Mix of valid and invalid UUIDs."""
    uid = uuid.uuid4()
    result = _parse_uuids([str(uid), "bad", str(uuid.uuid4())])
    assert len(result) == 2


def test_compute_avg_seed_score_with_scores():
    """Average seed score from provided dict."""
    facts = [make_fact(id=uuid.UUID("00000000-0000-0000-0000-000000000001"))]
    scores = {"00000000-0000-0000-0000-000000000001": 0.8}
    avg = _compute_avg_seed_score(facts, scores)
    assert avg == 0.8


def test_compute_avg_seed_score_none():
    """Default 0.5 when no scores provided."""
    facts = [make_fact()]
    avg = _compute_avg_seed_score(facts, None)
    assert avg == 0.5


def test_score_fact_with_decay():
    """Scored fact should incorporate decay and avg seed score."""
    now = datetime.now(UTC)
    fact = make_fact(importance=0.5, times_retrieved=0, last_retrieved_at=None, user_correction_count=0)
    score = _score_fact(fact, decay=0.6, avg_seed_score=0.8, now=now)
    assert 0 < score < 1.0


# ---------------------------------------------------------------------------
# is_fact_active tests
# ---------------------------------------------------------------------------


def test_is_fact_active_no_expiry():
    """Active fact with no valid_to."""
    fact = make_fact(valid_to=None, is_stale=False)
    assert is_fact_active(fact) is True


def test_is_fact_active_stale():
    """Stale fact should not be active."""
    fact = make_fact(valid_to=None, is_stale=True)
    assert is_fact_active(fact) is False


def test_is_fact_active_expired():
    """Expired fact should not be active."""
    past = datetime(2020, 1, 1, tzinfo=UTC)
    fact = make_fact(valid_to=past, is_stale=False)
    assert is_fact_active(fact) is False


# ---------------------------------------------------------------------------
# is_key_allowed tests
# ---------------------------------------------------------------------------


def test_is_key_allowed_open_mode():
    """All keys allowed when allowed_keys is None."""
    assert is_key_allowed("any.key", None) is True


def test_is_key_allowed_empty_set():
    """All keys allowed when allowed_keys is empty set."""
    assert is_key_allowed("any.key", set()) is True


def test_is_key_allowed_in_set():
    """Key in allowed set."""
    assert is_key_allowed("preference", {"preference", "name"}) is True


def test_is_key_allowed_not_in_set():
    """Key not in allowed set."""
    assert is_key_allowed("secret", {"preference", "name"}) is False


def test_is_key_allowed_none_key():
    """None key is always allowed."""
    assert is_key_allowed(None, {"preference"}) is True


# ---------------------------------------------------------------------------
# SpreadingActivationResult dataclass
# ---------------------------------------------------------------------------


def test_spreading_result_defaults():
    """SpreadingActivationResult should have sane defaults."""
    result = SpreadingActivationResult()
    assert result.candidates == []
    assert result.entities_explored == []
    assert result.hop1_count == 0
    assert result.hop2_count == 0
