"""Tests for mirror facts: synthetic ``ExtractedFact`` creation for relations
without textual evidence.

Covers:
- ``_build_mirror_extracted_fact``: subject, fact text template, confidence
- Prompt improvement: FACT_EXTRACTION_PROMPT and EXTRACTION_SYSTEM_PROMPT include
  implicit fact extraction instructions
"""

from __future__ import annotations

import json
from unittest.mock import MagicMock

from conftest import FakeLLMProvider, run_async

# ---------------------------------------------------------------------------
# Helpers
# ---------------------------------------------------------------------------


def _make_resolved_entity(
    entity_key: str = "person:ana",
    display_name: str = "Ana",
    entity_type: str = "person",
) -> MagicMock:
    """Create a mock ResolvedEntity."""
    entity = MagicMock()
    entity.entity_key = entity_key
    entity.display_name = display_name
    entity.entity_type = entity_type
    entity.is_new = False
    entity.resolution_method = "exact"
    entity.fuzzy_score = None
    return entity


# ===========================================================================
# Tests: _build_mirror_extracted_fact
# ===========================================================================


class TestBuildMirrorExtractedFact:
    """Tests for the ``_build_mirror_extracted_fact`` helper."""

    def test_mirror_fact_text_template(self):
        """Mirror fact generates readable text from relation components."""
        from arandu.write.upsert import _build_mirror_extracted_fact

        source = _make_resolved_entity("person:ana", "Ana")
        target = _make_resolved_entity("place:curitiba", "Curitiba")

        fact = _build_mirror_extracted_fact(source, target, "lives_in")

        assert fact.fact == "Ana lives in Curitiba"

    def test_mirror_fact_confidence(self):
        """Mirror fact has confidence=0.60 (weak inference)."""
        from arandu.write.upsert import _build_mirror_extracted_fact

        source = _make_resolved_entity("person:ana", "Ana")
        target = _make_resolved_entity("organization:acme", "Acme Corp")

        fact = _build_mirror_extracted_fact(source, target, "works_at")

        assert fact.confidence == 0.60
        assert fact.action == "NEW"

    def test_mirror_fact_subject_is_source_display_name(self):
        """Subject matches source entity display name (for entity_map lookup)."""
        from arandu.write.upsert import _build_mirror_extracted_fact

        source = _make_resolved_entity("person:ana", "Ana", "person")
        target = _make_resolved_entity("place:curitiba", "Curitiba", "place")

        fact = _build_mirror_extracted_fact(source, target, "lives_in")

        assert fact.subject == "Ana"

    def test_mirror_fact_rel_type_readable(self):
        """Underscores in rel_type are replaced with spaces in fact_text."""
        from arandu.write.upsert import _build_mirror_extracted_fact

        source = _make_resolved_entity("person:ana", "Ana")
        target = _make_resolved_entity("person:silva", "Professor Silva")

        fact = _build_mirror_extracted_fact(source, target, "mentored_by")

        assert fact.fact == "Ana mentored by Professor Silva"


# ===========================================================================
# Tests: Prompt improvement for implicit facts
# ===========================================================================


class TestPromptImplicitFacts:
    """Tests that extraction prompts instruct LLM to extract implicit facts."""

    def test_fact_extraction_prompt_prohibits_mirror_facts(self):
        """FACT_EXTRACTION_PROMPT_TEMPLATE prohibits mirror facts."""
        from arandu.write.extract import FACT_EXTRACTION_PROMPT_TEMPLATE

        prompt = FACT_EXTRACTION_PROMPT_TEMPLATE.format(speaker_name="Pedro")
        assert "mirror facts" in prompt.lower()

    def test_extraction_with_implicit_fact(self):
        """LLM can return an implicit fact alongside a relation."""
        from arandu.write.extract import extract_from_message

        response = json.dumps(
            {
                "entities": [
                    {"name": "user", "type": "person"},
                    {"name": "Mom", "type": "person"},
                    {"name": "Curitiba", "type": "place"},
                ],
                "facts": [
                    {"subject": "user", "fact": "User is visiting mom in Curitiba", "confidence": 0.95},
                    {"subject": "Mom", "fact": "User's mom lives in Curitiba", "confidence": 0.60},
                ],
                "relations": [
                    {"source": "Mom", "target": "Curitiba", "rel_type": "lives_in"},
                ],
            }
        )

        llm = FakeLLMProvider([response])
        result, _meta = run_async(extract_from_message("Vou pra Curitiba ver minha mãe", llm=llm, speaker_name="Pedro"))

        # Both the explicit and implicit facts are extracted
        assert len(result.facts) == 2
        assert any("lives in Curitiba" in f.fact for f in result.facts)
        assert len(result.relations) == 1
