"""Tests for extraction quality fixes — issue #1173.

Covers:
  - Expanded predicate blocklist (18 new blocked predicates)
  - Subject validation filter (unknown subject blocking)
  - Goals category tightening (prompt text verification)
  - Architecture decision routing to preferences (prompt text verification)
"""
import pytest
from cortex.extraction.predicate_filter import (
    filter_claims,
    filter_unknown_subjects,
    BLOCKED_PREDICATES,
)


# ------------------------------------------------------------------ #
# Fix 2: Expanded predicate blocklist
# ------------------------------------------------------------------ #

class TestExpandedPredicateBlocklist:
    """Verify that all 18 new sprint/task-noise predicates are blocked."""

    NEW_BLOCKED_PREDICATES = [
        'has_completed_task',
        'has_pending_tasks',
        'has_pending_task',
        'has_milestones',
        'has_milestone',
        'considers',
        'considered',
        'assigns_task',
        'assigned_task',
        'has_ambiguous_item',
        'has_refactor_targets',
        'plans_to_refactor',
        'proposes',
        'prioritizes',
        'prioritized',
        'has_plan',
        # 'interacts_with' — moved to tautology_filter.py (handles it more precisely)
        'noted',
        'updates',
        'has_agent_metadata',
        'has_draft',
    ]

    @pytest.mark.parametrize("predicate", NEW_BLOCKED_PREDICATES)
    def test_new_predicate_in_blocklist(self, predicate):
        """Each new predicate must be in BLOCKED_PREDICATES."""
        assert predicate in BLOCKED_PREDICATES, (
            f"{predicate!r} missing from BLOCKED_PREDICATES"
        )

    @pytest.mark.parametrize("predicate", NEW_BLOCKED_PREDICATES)
    def test_new_predicate_filtered(self, predicate):
        """Claims with new blocked predicates must be removed by filter_claims."""
        claims = [
            {"subject": "user", "predicate": predicate, "object_value": "some value"},
            {"subject": "user", "predicate": "prefers", "object_value": "Python"},
        ]
        result = filter_claims(claims)
        assert len(result) == 1
        assert result[0]["predicate"] == "prefers"

    def test_all_new_predicates_blocked_together(self):
        """A batch containing only new-blocked predicates should yield empty."""
        claims = [
            {"subject": "user", "predicate": p, "object_value": f"val-{i}"}
            for i, p in enumerate(self.NEW_BLOCKED_PREDICATES)
        ]
        result = filter_claims(claims)
        assert result == []


# ------------------------------------------------------------------ #
# Fix 3: Subject validation filter
# ------------------------------------------------------------------ #

class TestSubjectValidationFilter:
    """Verify unknown subjects are blocked, known subjects pass."""

    DEFAULT_KNOWN = {"user", "agent", "assistant", "Eva", "eva"}

    def test_known_subject_passes(self):
        claims = [
            {"subject": "user", "predicate": "prefers", "object_value": "Python"},
            {"subject": "Eva", "predicate": "is", "object_value": "an agent"},
            {"subject": "agent", "predicate": "has", "object_value": "identity"},
            {"subject": "assistant", "predicate": "does", "object_value": "stuff"},
        ]
        result = filter_unknown_subjects(claims, self.DEFAULT_KNOWN)
        assert len(result) == 4

    def test_unknown_subject_blocked(self):
        claims = [
            {"subject": "Sarah Chen", "predicate": "takes", "object_value": "paternity leave"},
            {"subject": "user", "predicate": "prefers", "object_value": "Python"},
        ]
        result = filter_unknown_subjects(claims, self.DEFAULT_KNOWN)
        assert len(result) == 1
        assert result[0]["subject"] == "user"

    def test_case_insensitive_matching(self):
        claims = [
            {"subject": "USER", "predicate": "prefers", "object_value": "Python"},
            {"subject": "eva", "predicate": "is", "object_value": "agent"},
            {"subject": "EVA", "predicate": "has", "object_value": "identity"},
        ]
        result = filter_unknown_subjects(claims, self.DEFAULT_KNOWN)
        assert len(result) == 3

    def test_empty_known_subjects_disables_filter(self):
        claims = [
            {"subject": "Sarah Chen", "predicate": "takes", "object_value": "leave"},
            {"subject": "Random Person", "predicate": "does", "object_value": "stuff"},
        ]
        result = filter_unknown_subjects(claims, set())
        assert len(result) == 2

    def test_custom_known_subjects(self):
        custom = {"user", "agent", "Andrew", "Kira", "Sora"}
        claims = [
            {"subject": "Andrew", "predicate": "prefers", "object_value": "Python"},
            {"subject": "Kira", "predicate": "is", "object_value": "fleet agent"},
            {"subject": "Sora", "predicate": "is", "object_value": "fleet agent"},
            {"subject": "Jane Doe", "predicate": "likes", "object_value": "cats"},
        ]
        result = filter_unknown_subjects(claims, custom)
        assert len(result) == 3
        subjects = {c["subject"] for c in result}
        assert "Jane Doe" not in subjects

    def test_empty_subject_blocked(self):
        claims = [
            {"subject": "", "predicate": "prefers", "object_value": "Python"},
        ]
        result = filter_unknown_subjects(claims, self.DEFAULT_KNOWN)
        assert len(result) == 0

    def test_whitespace_subject_blocked(self):
        claims = [
            {"subject": "  ", "predicate": "prefers", "object_value": "Python"},
        ]
        result = filter_unknown_subjects(claims, self.DEFAULT_KNOWN)
        assert len(result) == 0

    def test_works_with_dataclass_claims(self):
        from cortex.extraction.extractor import ClaimExtraction
        claims = [
            ClaimExtraction(
                action="ADD", subject="Sarah Chen", predicate="takes",
                object_value="leave", confidence=0.9,
            ),
            ClaimExtraction(
                action="ADD", subject="user", predicate="prefers",
                object_value="Python", confidence=0.9,
            ),
        ]
        result = filter_unknown_subjects(claims, self.DEFAULT_KNOWN)
        assert len(result) == 1
        assert result[0].subject == "user"


# ------------------------------------------------------------------ #
# Fix 4 & 5: Prompt text verification
# ------------------------------------------------------------------ #

class TestPromptTextFixes:
    """Verify prompt text changes for goals and architecture decisions."""

    def test_v1_goals_tightened(self):
        from cortex.extraction.prompts import EXTRACTION_SYSTEM_PROMPT
        assert ">1 month" in EXTRACTION_SYSTEM_PROMPT
        assert "Will this still matter in 3 months" in EXTRACTION_SYSTEM_PROMPT
        assert "PR/issue number" in EXTRACTION_SYSTEM_PROMPT

    def test_v2_goals_tightened(self):
        from cortex.extraction.prompts_v2 import EXTRACTION_SYSTEM_PROMPT
        assert ">1 month" in EXTRACTION_SYSTEM_PROMPT
        assert "Will this still matter in 3 months" in EXTRACTION_SYSTEM_PROMPT
        assert "PR/issue number" in EXTRACTION_SYSTEM_PROMPT

    def test_v2_decisions_category_present(self):
        from cortex.extraction.prompts_v2 import EXTRACTION_SYSTEM_PROMPT
        assert "decisions" in EXTRACTION_SYSTEM_PROMPT
        assert "chose X over Y" in EXTRACTION_SYSTEM_PROMPT
        assert "architecture choices" in EXTRACTION_SYSTEM_PROMPT

    def test_v2_new_categories_present(self):
        from cortex.extraction.prompts_v2 import EXTRACTION_SYSTEM_PROMPT
        for cat in ("behavioral", "relational", "episodic"):
            assert cat in EXTRACTION_SYSTEM_PROMPT, f"{cat} not found in prompt"


# ------------------------------------------------------------------ #
# Fix 1: Default prompt version
# ------------------------------------------------------------------ #

class TestDefaultPromptVersion:
    """Verify default extraction prompt version is v2."""

    def test_config_default_is_v2(self):
        from cortex.config.model import CortexConfig
        config = CortexConfig()
        assert config.extraction_prompt_version == "v2"

    def test_known_subjects_default(self):
        from cortex.config.model import CortexConfig
        config = CortexConfig()
        assert "user" in config.known_subjects
        assert "Eva" in config.known_subjects
        assert "agent" in config.known_subjects
