"""Shared test fixtures and helpers for the arandu SDK test suite."""

from __future__ import annotations

import asyncio
import uuid
from datetime import UTC, datetime
from unittest.mock import MagicMock

from arandu.models import MemoryFact
from arandu.protocols import LLMResult

# ---------------------------------------------------------------------------
# Fake providers — used across all test modules
# ---------------------------------------------------------------------------


class FakeLLMProvider:
    """Fake LLM that returns canned JSON responses in sequence.

    Args:
        responses: Ordered list of JSON strings to return. If exhausted,
            the last response is repeated. If empty, returns a default
            empty extraction JSON.
    """

    def __init__(self, responses: list[str] | None = None) -> None:
        self._responses = list(responses or [])
        self._call_count = 0

    async def complete(
        self,
        messages: list[dict],
        temperature: float = 0,
        response_format: dict | None = None,
        max_tokens: int | None = None,
    ) -> LLMResult:
        if self._responses:
            resp = self._responses[min(self._call_count, len(self._responses) - 1)]
        else:
            resp = '{"entities": [], "facts": [], "relations": []}'
        self._call_count += 1
        return LLMResult(text=resp)


class FakeEmbeddingProvider:
    """Fake embedding provider that returns deterministic zero vectors."""

    async def embed(self, texts: list[str]) -> list[list[float]]:
        return [[0.0] * 1536 for _ in texts]

    async def embed_one(self, text: str) -> list[float] | None:
        return [0.0] * 1536


class FailingEmbeddingProvider:
    """Embedding provider that simulates failures (returns empty/None)."""

    async def embed(self, texts: list[str]) -> list[list[float]]:
        return []

    async def embed_one(self, text: str) -> list[float] | None:
        return None


# ---------------------------------------------------------------------------
# Async helper
# ---------------------------------------------------------------------------


def run_async(coro):
    """Run an async coroutine synchronously for tests."""
    return asyncio.run(coro)


# ---------------------------------------------------------------------------
# Factory helpers
# ---------------------------------------------------------------------------


def make_fact(**overrides) -> MagicMock:
    """Create a MagicMock MemoryFact with sensible defaults.

    Returns a MagicMock that behaves like a MemoryFact ORM object.
    Override any field via keyword arguments.

    Args:
        **overrides: Fields to override on the mock fact.

    Returns:
        MagicMock with MemoryFact-like attributes.
    """
    defaults = {
        "id": uuid.uuid4(),
        "user_id": "test-user",
        "entity_type": "person",
        "entity_key": "person::test_entity",
        "entity_name": "Test Entity",
        "attribute_key": "preference",
        "value_json": None,
        "value_text": "likes coffee",
        "fact_text": "Test Entity likes coffee",
        "category": "preference",
        "confidence": 0.9,
        "importance": 0.5,
        "is_private": False,
        "valid_from": datetime.now(tz=UTC),
        "valid_to": None,
        "superseded_by": None,
        "source_event_id": uuid.uuid4(),
        "embedding": [0.0] * 1536,
        "created_at": datetime.now(tz=UTC),
        "updated_at": datetime.now(tz=UTC),
    }
    defaults.update(overrides)
    fact = MagicMock(spec=MemoryFact)
    for key, value in defaults.items():
        setattr(fact, key, value)
    return fact
