#!/usr/bin/env python3
# Copyright (c) 2026 PULSE Contributors. Proprietary. All rights reserved.
"""Tests for Gate 1 relevance filter that prevents false-positive blocks.

Scenario: editing search_engine.py should NOT trigger an anti-pattern whose
rule text mentions worker_tools.py, even when FTS5 matches shared keywords.
"""

import sqlite3
import sys
import tempfile
import unittest
from pathlib import Path

ROOT = Path(__file__).resolve().parent.parent
sys.path.insert(0, str(ROOT))

from pulse.brain.integrity.gate_checks import _is_relevant_to_content, gate1_anti_pattern


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

def _make_db(anti_patterns=()):
    tmp = tempfile.NamedTemporaryFile(suffix=".db", delete=False)
    tmp.close()
    db = Path(tmp.name)
    conn = sqlite3.connect(str(db))
    conn.execute(
        "CREATE TABLE anti_patterns "
        "(id TEXT PRIMARY KEY, description TEXT, rule TEXT, "
        " evidence_count INTEGER DEFAULT 1, confidence REAL DEFAULT 0.5, "
        " domain TEXT DEFAULT 'general', status TEXT DEFAULT 'active')"
    )
    conn.execute(
        "CREATE VIRTUAL TABLE knowledge_fts USING fts5"
        "(item_id UNINDEXED, content, title, domain, agent, table_name)"
    )
    for ap in anti_patterns:
        conn.execute(
            "INSERT INTO anti_patterns "
            "(id, description, rule, evidence_count, confidence, domain, status) "
            "VALUES (?,?,?,?,?,?,?)",
            (
                ap["id"], ap.get("description", ""), ap.get("rule", ""),
                ap.get("evidence_count", 1), ap.get("confidence", 0.5),
                ap.get("domain", "general"), ap.get("status", "active"),
            ),
        )
        conn.execute(
            "INSERT INTO knowledge_fts (item_id, content, title, domain, agent, table_name) "
            "VALUES (?,?,?,?,?,?)",
            (ap["id"], ap.get("description", ""), ap.get("rule", ""),
             ap.get("domain", "general"), "", "anti_patterns"),
        )
    conn.commit()
    conn.close()
    return db


_WORKER_TOOLS_AP = {
    "id": "AP-WORKER-01",
    "description": "Never write to hippocampus from worker_tools.py without explicit path isolation",
    "rule": "NEVER write to hippocampus from worker_tools.py without explicit path isolation",
    "confidence": 0.85,
    "evidence_count": 5,
}

# Keywords a search_engine.py edit might contain that could accidentally match
# the worker_tools AP via FTS5.
_SEARCH_ENGINE_KEYWORDS = ["hippocampus", "isolation", "explicit", "storage", "salience"]
# Keywords clearly tied to worker_tools behaviour.
_WORKER_TOOLS_KEYWORDS = ["hippocampus", "isolation", "explicit", "worker_tools", "write_direct"]


# ---------------------------------------------------------------------------
# Test 1: editing search_engine.py must NOT trigger the worker_tools AP
# ---------------------------------------------------------------------------

class TestSearchEngineDoesNotTriggerWorkerToolsAP(unittest.TestCase):

    def setUp(self):
        self.db = _make_db([_WORKER_TOOLS_AP])

    def tearDown(self):
        self.db.unlink(missing_ok=True)

    def test_search_engine_edit_not_blocked(self):
        """False-positive test: search_engine.py edit must not be blocked."""
        content = (
            "def search_hippocampus(query, isolation_level=None):\n"
            "    # explicit salience-based retrieval from storage\n"
            "    return engine.fetch(query)\n"
        )
        blocked, msg = gate1_anti_pattern(
            _SEARCH_ENGINE_KEYWORDS, content, self.db,
            file_path="pulse/brain/search_engine.py",
        )
        self.assertFalse(
            blocked,
            f"False positive: search_engine.py edit blocked by worker_tools AP. Msg: {msg}",
        )

    def test_search_engine_edit_not_blocked_windows_path(self):
        """Windows backslash paths are normalised before comparison."""
        content = "def fetch(q): return hippocampus_store.get(q)"
        blocked, msg = gate1_anti_pattern(
            ["hippocampus", "isolation"], content, self.db,
            file_path="pulse\\brain\\search_engine.py",
        )
        self.assertFalse(blocked, f"Windows path false positive: {msg}")


# ---------------------------------------------------------------------------
# Test 2: editing worker_tools.py DOES trigger the worker_tools AP
# ---------------------------------------------------------------------------

class TestWorkerToolsEditIsBlocked(unittest.TestCase):

    def setUp(self):
        self.db = _make_db([_WORKER_TOOLS_AP])

    def tearDown(self):
        self.db.unlink(missing_ok=True)

    def test_worker_tools_edit_is_blocked(self):
        """True-positive test: editing worker_tools.py with matching keywords is blocked."""
        content = (
            "def write_direct(hippocampus_path, data):\n"
            "    open(hippocampus_path, 'w').write(data)\n"
        )
        blocked, msg = gate1_anti_pattern(
            _WORKER_TOOLS_KEYWORDS, content, self.db,
            file_path="pulse/workers/worker_tools.py",
        )
        self.assertTrue(blocked, "Expected block when editing worker_tools.py")
        self.assertIn("BRAIN BLOCK", msg or "")


# ---------------------------------------------------------------------------
# Test 3: _is_relevant_to_content returns False for mismatched files
# ---------------------------------------------------------------------------

class TestIsRelevantToContentMismatch(unittest.TestCase):

    def test_returns_false_for_mismatched_file(self):
        rule = "NEVER write to hippocampus from worker_tools.py without path isolation"
        self.assertFalse(
            _is_relevant_to_content(rule, "some content", "pulse/brain/search_engine.py"),
            "Rule mentions worker_tools.py; editing search_engine.py should be irrelevant.",
        )

    def test_returns_false_for_different_module(self):
        rule = "NEVER call sys.exit() inside brain_search.py library code"
        self.assertFalse(
            _is_relevant_to_content(rule, "def foo(): pass", "pulse/workers/worker_main.py")
        )

    def test_returns_false_for_multiple_file_refs_all_mismatched(self):
        rule = "NEVER import pulse_dispatcher.py or task_ops.py at module level in tests"
        self.assertFalse(
            _is_relevant_to_content(rule, "x = 1", "pulse/brain/gate_checks.py")
        )


# ---------------------------------------------------------------------------
# Test 4: _is_relevant_to_content returns True when no file refs in rule
# ---------------------------------------------------------------------------

class TestIsRelevantToContentNoFileRefs(unittest.TestCase):

    def test_returns_true_when_no_file_refs(self):
        """Rule has no .py file refs — always relevant regardless of edited file."""
        rule = "NEVER write to hippocampus from worker_tools without path isolation"
        self.assertTrue(
            _is_relevant_to_content(rule, "some content", "pulse/brain/search_engine.py"),
        )

    def test_returns_true_when_rule_is_empty(self):
        self.assertTrue(_is_relevant_to_content("", "content", "any/file.py"))

    def test_returns_true_when_file_path_is_empty(self):
        """When file_path is unknown we cannot exclude the rule."""
        rule = "NEVER write to hippocampus from worker_tools.py"
        self.assertTrue(_is_relevant_to_content(rule, "content", ""))

    def test_returns_true_when_editing_the_named_file(self):
        rule = "NEVER call sys.exit() inside worker_tools.py"
        self.assertTrue(
            _is_relevant_to_content(rule, "def f(): pass", "pulse/workers/worker_tools.py"),
        )

    def test_returns_true_when_one_of_multiple_file_refs_matches(self):
        rule = "NEVER import pulse_dispatcher.py or task_ops.py at module level"
        self.assertTrue(
            _is_relevant_to_content(rule, "import task_ops", "pulse/tasks/task_ops.py"),
        )


if __name__ == "__main__":
    unittest.main(verbosity=2)
