"""
tests/test_batch_extraction_yield.py — Tests for R-133 batch extraction yield fixes.

Covers:
  Fix 1: "Andrew" in known_subjects (predicate filter)
  Fix 2: _DATE_FILENAME_RE matching HHMM-suffixed filenames
  Fix 3: FileSource.clean_content() noise stripping
"""
from __future__ import annotations

import tempfile
from pathlib import Path

import pytest

from cortex.batch_ingest import FileSource, _DATE_FILENAME_RE
from cortex.extraction.predicate_filter import filter_unknown_subjects


# ---------------------------------------------------------------------------
# Fix 1: filter_unknown_subjects with "Andrew"
# ---------------------------------------------------------------------------

class TestKnownSubjectsAndrew:
    """Verify that 'Andrew' passes the known_subjects filter."""

    KNOWN = {"user", "agent", "assistant", "Eva", "eva", "Andrew", "andrew"}

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

    def test_andrew_lowercase_passes(self):
        claims = [{"subject": "andrew", "predicate": "uses", "object_value": "Vim"}]
        result = filter_unknown_subjects(claims, self.KNOWN)
        assert len(result) == 1

    def test_unknown_subject_still_blocked(self):
        claims = [
            {"subject": "Andrew", "predicate": "prefers", "object_value": "Python"},
            {"subject": "RandomPerson", "predicate": "likes", "object_value": "Java"},
        ]
        result = filter_unknown_subjects(claims, self.KNOWN)
        assert len(result) == 1
        assert result[0]["subject"] == "Andrew"

    def test_eva_and_andrew_both_pass(self):
        claims = [
            {"subject": "Eva", "predicate": "built", "object_value": "Cortex"},
            {"subject": "Andrew", "predicate": "designed", "object_value": "evaOS"},
        ]
        result = filter_unknown_subjects(claims, self.KNOWN)
        assert len(result) == 2


# ---------------------------------------------------------------------------
# Fix 2: _DATE_FILENAME_RE matching HHMM-suffixed filenames
# ---------------------------------------------------------------------------

class TestDateFilenameRegex:
    """Verify regex matches both plain date and HHMM-suffixed filenames."""

    def test_plain_date_stem(self):
        """2026-02-22 matches and extracts date."""
        m = _DATE_FILENAME_RE.match("2026-02-22")
        assert m is not None
        assert m.group(1) == "2026-02-22"

    def test_plain_date_with_extension(self):
        """2026-02-22.md matches."""
        m = _DATE_FILENAME_RE.match("2026-02-22.md")
        assert m is not None
        assert m.group(1) == "2026-02-22"

    def test_hhmm_suffixed_stem(self):
        """2026-02-22-2104 matches and extracts date."""
        m = _DATE_FILENAME_RE.match("2026-02-22-2104")
        assert m is not None
        assert m.group(1) == "2026-02-22"

    def test_hhmm_suffixed_with_extension(self):
        """2026-02-22-2104.md matches."""
        m = _DATE_FILENAME_RE.match("2026-02-22-2104.md")
        assert m is not None
        assert m.group(1) == "2026-02-22"

    def test_non_date_no_match(self):
        """Random filenames don't match."""
        assert _DATE_FILENAME_RE.match("session-notes.md") is None
        assert _DATE_FILENAME_RE.match("readme") is None

    def test_filesource_extracts_date_hhmm(self):
        """FileSource._extract_session_date works for HHMM-suffixed files."""
        with tempfile.NamedTemporaryFile(suffix=".md", prefix="2026-02-22-2104",
                                         dir="/tmp", delete=False) as f:
            f.write(b"test content")
            fpath = f.name
        try:
            # We need the filename to be exactly right — create manually
            p = Path("/tmp/2026-02-22-2104.md")
            p.write_text("test content")
            src = FileSource(str(p))
            assert src._extract_session_date() == "2026-02-22"
        finally:
            p.unlink(missing_ok=True)
            Path(fpath).unlink(missing_ok=True)

    def test_filesource_extracts_date_plain(self):
        """FileSource._extract_session_date works for plain date files."""
        p = Path("/tmp/2026-02-22.md")
        p.write_text("test content")
        try:
            src = FileSource(str(p))
            assert src._extract_session_date() == "2026-02-22"
        finally:
            p.unlink(missing_ok=True)


# ---------------------------------------------------------------------------
# Fix 3: FileSource.clean_content()
# ---------------------------------------------------------------------------

class TestFileSourceCleanContent:
    """Verify FileSource.clean_content strips each noise pattern."""

    def _clean(self, text: str) -> str:
        """Helper — instantiate FileSource with a dummy path and clean."""
        src = FileSource("/dev/null")
        return src.clean_content(text)

    def test_strips_session_header(self):
        text = "# Session: abc-123\n- **Session Key**: xyz\n- **Started**: 2026-03-12\nActual content here."
        result = self._clean(text)
        assert "# Session:" not in result
        assert "**Session Key**" not in result
        assert "**Started**" not in result
        assert "Actual content here." in result

    def test_strips_untrusted_wrappers(self):
        text = "<<<EXTERNAL_UNTRUSTED_CONTENT>>>\nSome text\n<<<END_EXTERNAL_UNTRUSTED_CONTENT>>>"
        result = self._clean(text)
        assert "EXTERNAL_UNTRUSTED_CONTENT" not in result
        assert "Some text" in result

    def test_strips_conversation_info_block(self):
        text = 'Before\n\nConversation info (untrusted metadata): {"session": "x", "id": 1}\n\nAfter'
        result = self._clean(text)
        assert "Conversation info" not in result
        assert "Before" in result
        assert "After" in result

    def test_strips_message_id_tags(self):
        text = "Hello [message_id: abc-123] world"
        result = self._clean(text)
        assert "[message_id:" not in result
        assert "Hello" in result
        assert "world" in result

    def test_strips_metadata_json_fences(self):
        text = 'Before\n\n```json\n{"session_id": "abc", "config": {}}\n```\n\nAfter'
        result = self._clean(text)
        assert '"session_id"' not in result
        assert "Before" in result
        assert "After" in result

    def test_preserves_non_metadata_code_fences(self):
        text = 'Before\n\n```python\ndef hello():\n    return "world"\n```\n\nAfter'
        result = self._clean(text)
        assert "def hello():" in result
        assert "```python" in result

    def test_collapses_multiple_blank_lines(self):
        text = "Line 1\n\n\n\n\nLine 2"
        result = self._clean(text)
        assert "\n\n\n" not in result
        assert "Line 1" in result
        assert "Line 2" in result

    def test_combined_noise(self):
        """All noise patterns in one document get stripped."""
        text = (
            "# Session: test-session\n"
            "- **Session Key**: abc\n"
            "- **Started**: 2026-03-12\n"
            "\n"
            "<<<EXTERNAL_UNTRUSTED_CONTENT>>>\n"
            "Conversation info (untrusted metadata): {\"id\": 1}\n"
            "\n"
            "Hello [message_id: msg-001] world\n"
            "\n"
            "```json\n"
            "{\"session_id\": \"x\", \"metadata\": {}}\n"
            "```\n"
            "\n\n\n\n"
            "<<<END_EXTERNAL_UNTRUSTED_CONTENT>>>\n"
            "Real content lives here.\n"
        )
        result = self._clean(text)
        assert "Real content lives here." in result
        assert "Session Key" not in result
        assert "EXTERNAL_UNTRUSTED_CONTENT" not in result
        assert "Conversation info" not in result
        assert "[message_id:" not in result
        assert '"session_id"' not in result
        assert "\n\n\n" not in result

    def test_empty_input(self):
        assert self._clean("") == ""

    def test_only_noise(self):
        text = "# Session: test\n- **Session Key**: abc\n"
        result = self._clean(text)
        # Should be empty or minimal after stripping
        assert "Session Key" not in result

    def test_strips_duration_and_model_headers(self):
        text = "- **Duration**: 45m\n- **Model**: gpt-4\n- **Participants**: Eva, Andrew\nContent."
        result = self._clean(text)
        assert "**Duration**" not in result
        assert "**Model**" not in result
        assert "**Participants**" not in result
        assert "Content." in result
