"""Tests for memify — vitality scoring, protection check, cosine similarity, results."""

from __future__ import annotations

from datetime import UTC, datetime, timedelta
from types import SimpleNamespace

from arandu.background.memify import (
    MemifyResult,
    _cosine_similarity,
    _is_fact_protected,
    compute_vitality,
)

# ---------------------------------------------------------------------------
# compute_vitality
# ---------------------------------------------------------------------------


def test_vitality_superseded_fact():
    """Superseded fact (valid_to set) → 0.0."""
    fact = SimpleNamespace(valid_to=datetime.now(UTC))
    assert compute_vitality(fact) == 0.0


def test_vitality_fresh_high_confidence():
    """Recently retrieved, high confidence → high vitality."""
    now = datetime.now(UTC)
    fact = SimpleNamespace(
        valid_to=None,
        times_retrieved=5,
        last_retrieved_at=now - timedelta(hours=1),
        confidence=0.95,
        reinforcement_count=3,
        user_correction_count=0,
    )
    score = compute_vitality(fact, now)
    assert score > 0.5


def test_vitality_old_never_retrieved():
    """Old fact, never retrieved → low vitality."""
    now = datetime.now(UTC)
    fact = SimpleNamespace(
        valid_to=None,
        times_retrieved=0,
        last_retrieved_at=None,
        created_at=now - timedelta(days=365),
        confidence=0.3,
        reinforcement_count=0,
        user_correction_count=0,
    )
    score = compute_vitality(fact, now)
    assert score < 0.3


def test_vitality_correction_penalty():
    """Corrections should reduce vitality."""
    now = datetime.now(UTC)
    base = SimpleNamespace(
        valid_to=None,
        times_retrieved=5,
        last_retrieved_at=now,
        confidence=0.9,
        reinforcement_count=2,
        user_correction_count=0,
    )
    corrected = SimpleNamespace(
        valid_to=None,
        times_retrieved=5,
        last_retrieved_at=now,
        confidence=0.9,
        reinforcement_count=2,
        user_correction_count=3,
    )
    score_base = compute_vitality(base, now)
    score_corrected = compute_vitality(corrected, now)
    assert score_corrected < score_base


def test_vitality_bounded_0_1():
    """Vitality should always be between 0.0 and 1.0."""
    now = datetime.now(UTC)
    fact = SimpleNamespace(
        valid_to=None,
        times_retrieved=1000,
        last_retrieved_at=now,
        confidence=1.0,
        reinforcement_count=100,
        user_correction_count=0,
    )
    score = compute_vitality(fact, now)
    assert 0.0 <= score <= 1.0


def test_vitality_custom_weights():
    """Custom weights should be respected."""
    now = datetime.now(UTC)
    fact = SimpleNamespace(
        valid_to=None,
        times_retrieved=10,
        last_retrieved_at=now,
        confidence=0.9,
        reinforcement_count=5,
        user_correction_count=0,
    )
    # Only retrieval matters
    w = {"retrieval": 1.0, "recency": 0.0, "confidence": 0.0, "reinforcement": 0.0}
    score = compute_vitality(fact, now, weights=w)
    # retrieval_score = log(11)/log(11) = 1.0
    assert abs(score - 1.0) < 0.01


# ---------------------------------------------------------------------------
# _is_fact_protected
# ---------------------------------------------------------------------------


def test_protected_high_importance():
    """High importance (>=0.8) → protected."""
    fact = SimpleNamespace(importance=0.85, is_sensitive=False)
    assert _is_fact_protected(fact) is True


def test_protected_sensitive():
    """Sensitive fact → protected."""
    fact = SimpleNamespace(importance=0.1, is_sensitive=True)
    assert _is_fact_protected(fact) is True


def test_not_protected():
    """Low importance, not sensitive → not protected."""
    fact = SimpleNamespace(importance=0.3, is_sensitive=False)
    assert _is_fact_protected(fact) is False


# ---------------------------------------------------------------------------
# _cosine_similarity
# ---------------------------------------------------------------------------


def test_cosine_identical():
    """Identical vectors → 1.0."""
    v = [0.5, 0.5, 0.5]
    assert abs(_cosine_similarity(v, v) - 1.0) < 0.001


def test_cosine_orthogonal():
    """Orthogonal vectors → 0.0."""
    assert abs(_cosine_similarity([1.0, 0.0], [0.0, 1.0])) < 0.001


def test_cosine_empty():
    """Empty vectors → 0.0."""
    assert _cosine_similarity([], []) == 0.0


def test_cosine_zero_vector():
    """Zero vector → 0.0."""
    assert _cosine_similarity([0.0, 0.0], [1.0, 1.0]) == 0.0


# ---------------------------------------------------------------------------
# MemifyResult defaults
# ---------------------------------------------------------------------------


def test_memify_result_defaults():
    """MemifyResult should have sane defaults."""
    r = MemifyResult()
    assert r.facts_scored == 0
    assert r.facts_marked_stale == 0
    assert r.edges_reinforced == 0
    assert r.merges_executed == 0
