"""Tests for speaker provenance in upsert pipeline."""

from __future__ import annotations

from datetime import UTC, datetime
from unittest.mock import AsyncMock, MagicMock, patch

import pytest

from arandu.client import FactDetail, ScoredFact


class TestSpeakerInFactDetail:
    """Speaker field in FactDetail dataclass."""

    def test_fact_detail_has_speaker_field(self) -> None:
        detail = FactDetail(fact_id="abc", speaker="Pedro")
        assert detail.speaker == "Pedro"

    def test_fact_detail_speaker_defaults_to_none(self) -> None:
        detail = FactDetail(fact_id="abc")
        assert detail.speaker is None


class TestSpeakerInScoredFact:
    """Speaker field in ScoredFact dataclass."""

    def test_scored_fact_has_speaker_field(self) -> None:
        sf = ScoredFact(fact_id="abc", speaker="Assistente")
        assert sf.speaker == "Assistente"

    def test_scored_fact_speaker_defaults_to_none(self) -> None:
        sf = ScoredFact(fact_id="abc")
        assert sf.speaker is None


class TestSpeakerInAddFact:
    """_add_fact() propagates speaker to MemoryFact."""

    @pytest.mark.asyncio
    async def test_add_fact_sets_speaker(self) -> None:
        from arandu.write.upsert import _add_fact

        session = AsyncMock()
        decision = MagicMock()
        decision.fact_text = "Lives in São Paulo"
        decision.confidence = 0.9
        decision.importance = 0.5

        entity = MagicMock()
        entity.display_name = "Pedro"
        entity.entity_type = "person"
        entity.entity_key = "person:pedro"

        embeddings = MagicMock()
        embeddings.embed_one = AsyncMock(return_value=[0.0] * 1536)

        with patch("arandu.write.upsert.MemoryFact") as MockFact:
            mock_instance = MagicMock()
            MockFact.return_value = mock_instance

            await _add_fact(
                session,
                "agent-1",
                None,
                decision,
                entity,
                datetime.now(UTC),
                embeddings,
                speaker="Pedro",
            )

            call_kwargs = MockFact.call_args[1]
            assert call_kwargs["speaker"] == "Pedro"

    @pytest.mark.asyncio
    async def test_add_fact_speaker_none_by_default(self) -> None:
        from arandu.write.upsert import _add_fact

        session = AsyncMock()
        decision = MagicMock()
        decision.fact_text = "Likes coffee"
        decision.confidence = 0.9
        decision.importance = 0.5

        entity = MagicMock()
        entity.display_name = "User"
        entity.entity_type = "person"
        entity.entity_key = "person:user"

        embeddings = MagicMock()
        embeddings.embed_one = AsyncMock(return_value=[0.0] * 1536)

        with patch("arandu.write.upsert.MemoryFact") as MockFact:
            mock_instance = MagicMock()
            MockFact.return_value = mock_instance

            await _add_fact(
                session,
                "agent-1",
                None,
                decision,
                entity,
                datetime.now(UTC),
                embeddings,
            )

            call_kwargs = MockFact.call_args[1]
            assert call_kwargs["speaker"] is None


class TestSpeakerInExecuteUpsert:
    """execute_upsert() propagates speaker to _add_fact and _update_fact."""

    @pytest.mark.asyncio
    async def test_execute_upsert_propagates_speaker(self) -> None:
        from arandu.write.upsert import execute_upsert

        session = AsyncMock()
        session.begin_nested = MagicMock(return_value=AsyncMock())

        decision = MagicMock()
        decision.action = "ADD"
        decision.subject = "pedro"
        decision.fact_text = "Lives in SP"
        decision.confidence = 0.9
        decision.importance = 0.5

        entity = MagicMock()
        entity.display_name = "Pedro"
        entity.entity_type = "person"
        entity.entity_key = "person:pedro"

        embeddings = MagicMock()
        embeddings.embed_one = AsyncMock(return_value=[0.0] * 1536)

        with (
            patch("arandu.write.upsert._add_fact", new_callable=AsyncMock) as mock_add,
            patch("arandu.write.upsert._create_entity_links", new_callable=AsyncMock),
        ):
            mock_add.return_value = MagicMock(entity_key="person:pedro")

            await execute_upsert(
                session,
                "agent-1",
                None,
                [decision],
                {"pedro": entity},
                embeddings,
                speaker="Pedro",
            )

            mock_add.assert_called_once()
            assert mock_add.call_args[1]["speaker"] == "Pedro"
