"""Tests for the knowledge quality engine.

Cycle 15/40: Validates duplicate detection, contradiction analysis,
stale entry detection, trust weighting, and merge operations.
"""
import time
from datetime import datetime, timedelta

import pytest

_has_sqlite_vec = True
try:
    import sqlite_vec  # noqa: F401
except ImportError:
    _has_sqlite_vec = False


@pytest.fixture
def db(tmp_path):
    from greymatter_core.db import SQLiteDatabase
    return SQLiteDatabase(str(tmp_path / "quality_test.db"))


def _insert_knowledge(db, entry_id, title, content, entry_type="pattern",
                       domain="general", created_days_ago=0):
    """Helper to insert a knowledge entry for testing."""
    created = datetime.now() - timedelta(days=created_days_ago)
    db._conn.execute(
        "INSERT INTO knowledge_entries (id, entry_type, title, content, domain, "
        "scope, created_at, updated_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?)",
        (entry_id, entry_type, title, content, domain, "cross-cutting",
         created.isoformat(), created.isoformat()),
    )
    db._conn.commit()


# =========================================================================
# Duplicate Detection
# =========================================================================


@pytest.mark.skipif(not _has_sqlite_vec, reason="sqlite_vec not installed")
class TestDuplicateDetection:

    def test_identical_titles_detected(self, db):
        from greymatter_core.quality import QualityEngine
        _insert_knowledge(db, "a1", "Always use type hints in Python",
                          "Type hints improve readability")
        _insert_knowledge(db, "a2", "Always use type hints in Python",
                          "Type hints improve readability and tooling")

        qe = QualityEngine(db)
        issues = qe.find_duplicates(threshold=0.5)
        assert len(issues) >= 1
        assert issues[0].issue_type == "duplicate"
        assert set(issues[0].entry_ids) == {"a1", "a2"}

    def test_similar_content_detected(self, db):
        from greymatter_core.quality import QualityEngine
        _insert_knowledge(db, "b1", "Python logging best practices",
                          "Use structured logging with JSON output for production services")
        _insert_knowledge(db, "b2", "Logging practices for Python",
                          "Use structured logging with JSON output for production deployments")

        qe = QualityEngine(db)
        issues = qe.find_duplicates(threshold=0.5)
        assert len(issues) >= 1

    def test_unrelated_entries_not_flagged(self, db):
        from greymatter_core.quality import QualityEngine
        _insert_knowledge(db, "c1", "Python type hints",
                          "Use type hints for better IDE support")
        _insert_knowledge(db, "c2", "Docker container networking",
                          "Use bridge networks for inter-container communication")

        qe = QualityEngine(db)
        issues = qe.find_duplicates(threshold=0.7)
        assert len(issues) == 0

    def test_threshold_controls_sensitivity(self, db):
        from greymatter_core.quality import QualityEngine
        _insert_knowledge(db, "d1", "Use pytest for testing",
                          "Pytest provides fixtures and parametrize")
        _insert_knowledge(db, "d2", "Testing with pytest",
                          "Pytest supports fixtures and parametrize decorators")

        qe = QualityEngine(db)
        # Strict threshold should find fewer
        strict = qe.find_duplicates(threshold=0.9)
        # Loose threshold should find more
        loose = qe.find_duplicates(threshold=0.3)
        assert len(loose) >= len(strict)

    def test_trust_weight_determines_keep(self, db):
        """Higher-trust entry is recommended to keep."""
        from greymatter_core.quality import QualityEngine
        _insert_knowledge(db, "e1", "Always use type hints",
                          "Type hints improve code quality",
                          entry_type="observation")  # 0.4 trust
        _insert_knowledge(db, "e2", "Always use type hints",
                          "Type hints improve code quality",
                          entry_type="correction")  # 1.0 trust

        qe = QualityEngine(db)
        issues = qe.find_duplicates(threshold=0.5)
        assert len(issues) >= 1
        assert "e2" in issues[0].suggestion  # correction should be kept


# =========================================================================
# Contradiction Detection
# =========================================================================


@pytest.mark.skipif(not _has_sqlite_vec, reason="sqlite_vec not installed")
class TestContradictionDetection:

    def test_negation_pair_detected(self, db):
        from greymatter_core.quality import QualityEngine
        _insert_knowledge(db, "f1", "Always use global variables for config",
                          "Global variables simplify configuration management",
                          domain="python")
        _insert_knowledge(db, "f2", "Never use global variables for config",
                          "Global variables complicate testing and configuration",
                          domain="python")

        qe = QualityEngine(db)
        issues = qe.find_contradictions()
        assert len(issues) >= 1
        assert issues[0].issue_type == "contradiction"
        assert issues[0].severity == "high"

    def test_different_domains_not_contradicting(self, db):
        """Entries in different domains shouldn't be compared."""
        from greymatter_core.quality import QualityEngine
        _insert_knowledge(db, "g1", "Always use semicolons",
                          "Semicolons are required in JavaScript",
                          domain="javascript")
        _insert_knowledge(db, "g2", "Never use semicolons",
                          "Semicolons are not needed in Python",
                          domain="python")

        qe = QualityEngine(db)
        issues = qe.find_contradictions()
        assert len(issues) == 0

    def test_no_topic_overlap_no_contradiction(self, db):
        """Negation words alone don't trigger if topics differ."""
        from greymatter_core.quality import QualityEngine
        _insert_knowledge(db, "h1", "Always restart the database nightly",
                          "Database maintenance requires nightly restarts",
                          domain="ops")
        _insert_knowledge(db, "h2", "Never restart the frontend server",
                          "Frontend server should run continuously with zero downtime",
                          domain="ops")

        qe = QualityEngine(db)
        issues = qe.find_contradictions()
        # These have "always"/"never" but different topics
        # May or may not trigger depending on overlap — just verify no crash
        assert isinstance(issues, list)


# =========================================================================
# Stale Entry Detection
# =========================================================================


@pytest.mark.skipif(not _has_sqlite_vec, reason="sqlite_vec not installed")
class TestStaleDetection:

    def test_old_entries_flagged(self, db):
        from greymatter_core.quality import QualityEngine
        _insert_knowledge(db, "i1", "Old pattern",
                          "This was relevant months ago",
                          created_days_ago=120)

        qe = QualityEngine(db)
        issues = qe.find_stale(days=90)
        assert len(issues) >= 1
        assert issues[0].issue_type == "stale"
        assert issues[0].severity == "low"

    def test_recent_entries_not_flagged(self, db):
        from greymatter_core.quality import QualityEngine
        _insert_knowledge(db, "j1", "Recent pattern",
                          "This is current and relevant")

        qe = QualityEngine(db)
        issues = qe.find_stale(days=90)
        assert len(issues) == 0


# =========================================================================
# Full Quality Check
# =========================================================================


@pytest.mark.skipif(not _has_sqlite_vec, reason="sqlite_vec not installed")
class TestFullQualityCheck:

    def test_empty_graph_perfect_score(self, db):
        from greymatter_core.quality import QualityEngine
        qe = QualityEngine(db)
        report = qe.full_check()
        assert report.total_entries == 0
        assert report.score == 1.0
        assert report.issue_count == 0

    def test_clean_graph_high_score(self, db):
        from greymatter_core.quality import QualityEngine
        _insert_knowledge(db, "k1", "Use pytest", "Testing framework")
        _insert_knowledge(db, "k2", "Docker networking", "Bridge networks")

        qe = QualityEngine(db)
        report = qe.full_check()
        assert report.total_entries == 2
        assert report.score >= 0.9

    def test_issues_reduce_score(self, db):
        from greymatter_core.quality import QualityEngine
        # Create duplicates
        _insert_knowledge(db, "l1", "Identical entry about testing",
                          "Use pytest for all Python testing")
        _insert_knowledge(db, "l2", "Identical entry about testing",
                          "Use pytest for all Python testing")
        # Create old stale entry
        _insert_knowledge(db, "l3", "Ancient wisdom",
                          "This is very old", created_days_ago=200)

        qe = QualityEngine(db)
        report = qe.full_check()
        assert report.score < 1.0
        assert report.issue_count >= 1

    def test_report_to_dict(self, db):
        from greymatter_core.quality import QualityEngine
        _insert_knowledge(db, "m1", "Test entry", "Content")
        qe = QualityEngine(db)
        report = qe.full_check()
        d = report.to_dict()
        assert "total_entries" in d
        assert "issue_count" in d
        assert "score" in d
        assert "issues" in d
        assert isinstance(d["issues"], list)


# =========================================================================
# Merge Operations
# =========================================================================


@pytest.mark.skipif(not _has_sqlite_vec, reason="sqlite_vec not installed")
class TestMergeOperations:

    def test_merge_appends_content(self, db):
        from greymatter_core.quality import QualityEngine
        _insert_knowledge(db, "n1", "Pytest fixtures", "Fixtures provide test setup")
        _insert_knowledge(db, "n2", "Pytest fixtures", "Fixtures also handle teardown")

        qe = QualityEngine(db)
        success = qe.merge_duplicates("n1", "n2")
        assert success is True

        # Check merged content
        kept = db.execute_one(
            "SELECT content FROM knowledge_entries WHERE id = ?", ("n1",)
        )
        assert "test setup" in kept["content"]
        assert "teardown" in kept["content"]

        # Check pruned
        removed = db.execute_one(
            "SELECT pruned_at FROM knowledge_entries WHERE id = ?", ("n2",)
        )
        assert removed["pruned_at"] is not None

    def test_merge_nonexistent_fails(self, db):
        from greymatter_core.quality import QualityEngine
        _insert_knowledge(db, "o1", "Real entry", "Content")

        qe = QualityEngine(db)
        success = qe.merge_duplicates("o1", "nonexistent")
        assert success is False

    def test_merge_same_content_no_duplicate(self, db):
        """If content is already contained, don't append again."""
        from greymatter_core.quality import QualityEngine
        _insert_knowledge(db, "p1", "Entry", "Exact same content here")
        _insert_knowledge(db, "p2", "Entry", "Exact same content here")

        qe = QualityEngine(db)
        success = qe.merge_duplicates("p1", "p2")
        assert success is True

        kept = db.execute_one(
            "SELECT content FROM knowledge_entries WHERE id = ?", ("p1",)
        )
        # Content should NOT be duplicated
        assert kept["content"].count("Exact same content here") == 1


# =========================================================================
# Trust Weights
# =========================================================================


class TestTrustWeights:

    def test_known_types(self):
        from greymatter_core.quality import QualityEngine, TRUST_WEIGHTS
        qe = QualityEngine.__new__(QualityEngine)
        assert qe.get_trust_weight("correction") == 1.0
        assert qe.get_trust_weight("observation") == 0.4

    def test_unknown_type_default(self):
        from greymatter_core.quality import QualityEngine
        qe = QualityEngine.__new__(QualityEngine)
        assert qe.get_trust_weight("unknown_type") == 0.5


# =========================================================================
# Similarity Function
# =========================================================================


class TestSimilarity:

    def test_identical_texts(self):
        from greymatter_core.quality import QualityEngine
        a = {"title": "Python testing", "content": "Use pytest for testing"}
        b = {"title": "Python testing", "content": "Use pytest for testing"}
        assert QualityEngine._similarity(a, b) == 1.0

    def test_completely_different(self):
        from greymatter_core.quality import QualityEngine
        a = {"title": "Python testing", "content": "Unit tests are essential"}
        b = {"title": "Docker networking", "content": "Bridge networks connect containers"}
        sim = QualityEngine._similarity(a, b)
        assert sim < 0.3

    def test_empty_entries(self):
        from greymatter_core.quality import QualityEngine
        a = {"title": "", "content": ""}
        b = {"title": "", "content": ""}
        assert QualityEngine._similarity(a, b) == 0.0
