"""Integration tests for relation extraction through the encoder pipeline."""

import pytest

from neural_memory.core.brain import Brain, BrainConfig
from neural_memory.core.synapse import SynapseType
from neural_memory.engine.encoder import MemoryEncoder
from neural_memory.storage.memory_store import InMemoryStorage


@pytest.fixture
async def encoder() -> tuple[MemoryEncoder, InMemoryStorage]:
    """Create an encoder with in-memory storage."""
    storage = InMemoryStorage()
    config = BrainConfig()
    brain = Brain.create(name="test", config=config)
    await storage.save_brain(brain)
    storage.set_brain(brain.id)
    return MemoryEncoder(storage, brain.config), storage


class TestRelationEncoding:
    """Test relation extraction creates proper synapses during encoding."""

    @pytest.mark.asyncio
    async def test_causal_creates_caused_by_synapse(self, encoder: tuple) -> None:
        """Content with 'because' should create CAUSED_BY synapse."""
        enc, storage = encoder

        result = await enc.encode(
            "The deployment failed because the database connection timed out.",
        )

        _caused_by = [s for s in result.synapses_created if s.type == SynapseType.CAUSED_BY]
        # May or may not match depending on entity extraction
        # but the relation extractor should have found the pattern
        # The synapse is only created if both spans match neurons

    @pytest.mark.asyncio
    async def test_sequential_creates_before_synapse(self, encoder: tuple) -> None:
        """Content with 'first...then' should create BEFORE synapse."""
        enc, storage = encoder

        result = await enc.encode(
            "First backup the database, then apply the schema migration.",
        )

        _before = [s for s in result.synapses_created if s.type == SynapseType.BEFORE]
        # Synapse creation depends on span-to-neuron matching

    @pytest.mark.asyncio
    async def test_no_relation_simple_content(self, encoder: tuple) -> None:
        """Simple content without relation markers should not create relation synapses."""
        enc, storage = encoder

        result = await enc.encode(
            "Meeting with Alice at the coffee shop.",
        )

        relation_metadata_synapses = [
            s for s in result.synapses_created if s.metadata.get("relation_type") is not None
        ]
        assert len(relation_metadata_synapses) == 0

    @pytest.mark.asyncio
    async def test_suggest_memory_type_fallback(self, encoder: tuple) -> None:
        """Encoding without explicit type should auto-detect via suggest_memory_type."""
        enc, storage = encoder

        result = await enc.encode(
            "Decided to use PostgreSQL instead of MySQL for the project.",
        )

        # The anchor neuron should have auto-detected type
        anchor = next(n for n in result.neurons_created if n.metadata.get("is_anchor"))
        assert "type" in anchor.metadata
        assert anchor.metadata["type"] == "decision"

    @pytest.mark.asyncio
    async def test_explicit_type_not_overridden(self, encoder: tuple) -> None:
        """When metadata includes explicit type, it should not be overridden."""
        enc, storage = encoder

        result = await enc.encode(
            "Need to fix the login bug before release.",
            metadata={"type": "error"},
        )

        anchor = next(n for n in result.neurons_created if n.metadata.get("is_anchor"))
        # Explicit "error" should be preserved, not overridden to "todo"
        assert anchor.metadata["type"] == "error"

    @pytest.mark.asyncio
    async def test_auto_tags_separated_from_agent_tags(self, encoder: tuple) -> None:
        """Auto-generated and agent-provided tags should be in separate sets."""
        enc, storage = encoder

        result = await enc.encode(
            "Implemented the Redis caching layer for better performance.",
            tags={"sprint-42", "perf-improvement"},
        )

        # Agent tags should be in agent_tags
        assert "sprint-42" in result.fiber.agent_tags
        assert "perf-improvement" in result.fiber.agent_tags

        # Auto tags should be generated from content
        assert len(result.fiber.auto_tags) > 0

        # Tags property should be the union
        assert result.fiber.tags == result.fiber.auto_tags | result.fiber.agent_tags

    @pytest.mark.asyncio
    async def test_encoding_with_all_features(self, encoder: tuple) -> None:
        """Full encoding with causal content, agent tags, and auto-tags."""
        enc, storage = encoder

        result = await enc.encode(
            "The API latency increased because the cache TTL expired. "
            "First we invalidated the stale entries, then rebuilt the index.",
            tags={"api", "production-incident"},
        )

        # Should have neurons, synapses, and proper tag separation
        assert len(result.neurons_created) > 0
        assert len(result.synapses_created) > 0
        assert result.fiber is not None

        # Tag origin tracking
        assert "production-incident" in result.fiber.agent_tags
        assert len(result.fiber.auto_tags) > 0

        # Anchor should have auto-detected type
        anchor = next(n for n in result.neurons_created if n.metadata.get("is_anchor"))
        assert "type" in anchor.metadata
