"""Tests for consolidation — emotion tagging, cosine similarity, result types, constants."""

from __future__ import annotations

import asyncio

from arandu._helpers import cosine_similarity as _cosine_similarity
from arandu.background.consolidation import (
    ConsolidationResult,
    tag_event_emotion,
)
from arandu.constants import VALID_EMOTIONS, VALID_ENERGIES, VALID_OBSERVATION_TYPES
from arandu.protocols import LLMResult


def run_async(coro):
    return asyncio.run(coro)


# ---------------------------------------------------------------------------
# Constants validation
# ---------------------------------------------------------------------------


def test_valid_emotions_populated():
    """VALID_EMOTIONS should have expected entries."""
    assert "neutral" in VALID_EMOTIONS
    assert "anxious" in VALID_EMOTIONS
    assert "satisfied" in VALID_EMOTIONS
    assert len(VALID_EMOTIONS) >= 10


def test_valid_energies_populated():
    """VALID_ENERGIES should have high/medium/low."""
    assert {"high", "medium", "low"} == VALID_ENERGIES


def test_valid_observation_types_populated():
    """VALID_OBSERVATION_TYPES should have expected types."""
    assert "pattern" in VALID_OBSERVATION_TYPES
    assert "insight" in VALID_OBSERVATION_TYPES
    assert "trend" in VALID_OBSERVATION_TYPES
    assert "behavioral_preference" in VALID_OBSERVATION_TYPES


# ---------------------------------------------------------------------------
# Cosine similarity
# ---------------------------------------------------------------------------


def test_cosine_similarity_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_similarity_zero_vectors():
    """Zero vectors → 0.0."""
    assert _cosine_similarity([0.0, 0.0], [0.0, 0.0]) == 0.0


# ---------------------------------------------------------------------------
# Emotion tagging
# ---------------------------------------------------------------------------


class FakeLLMForEmotion:
    """Returns a valid emotion JSON."""

    def __init__(self, response: str) -> None:
        self._response = response

    async def complete(self, messages, **kwargs):
        return LLMResult(text=self._response)


def test_tag_event_emotion_valid():
    """Valid LLM response should parse correctly."""
    llm = FakeLLMForEmotion('{"emotion": "anxious", "intensity": 0.7, "energy": "high"}')
    result = run_async(tag_event_emotion("Estou muito preocupado", llm))
    assert result is not None
    assert result["emotion"] == "anxious"
    assert result["intensity"] == 0.7
    assert result["energy"] == "high"


def test_tag_event_emotion_invalid_emotion():
    """Invalid emotion should return None."""
    llm = FakeLLMForEmotion('{"emotion": "invalid_emotion", "intensity": 0.5, "energy": "medium"}')
    result = run_async(tag_event_emotion("teste", llm))
    assert result is None


def test_tag_event_emotion_empty_text():
    """Empty text should return None without calling LLM."""
    llm = FakeLLMForEmotion("should not be called")
    result = run_async(tag_event_emotion("", llm))
    assert result is None


def test_tag_event_emotion_bad_json():
    """Invalid JSON should return None."""
    llm = FakeLLMForEmotion("not valid json")
    result = run_async(tag_event_emotion("teste", llm))
    assert result is None


def test_tag_event_emotion_code_block():
    """LLM response wrapped in code block should be parsed."""
    llm = FakeLLMForEmotion('```json\n{"emotion": "neutral", "intensity": 0.3, "energy": "low"}\n```')
    result = run_async(tag_event_emotion("Dia normal", llm))
    assert result is not None
    assert result["emotion"] == "neutral"


# ---------------------------------------------------------------------------
# ConsolidationResult defaults
# ---------------------------------------------------------------------------


def test_consolidation_result_defaults():
    """ConsolidationResult should have sane defaults."""
    r = ConsolidationResult()
    assert r.events_processed == 0
    assert r.observations_created == 0
    assert r.observations_reinforced == 0
    assert r.skipped is False
    assert r.skip_reason is None
