"""Integration tests for Phase 3 (Embeddings + Cross-Refs) and Phase 4 (Search Perf)."""
import pytest
import numpy as np


@pytest.fixture
def db_conn():
    from pulse.core.brain_db import get_conn
    return get_conn()


class TestPhase3Embeddings:

    def test_embedding_populator_imports(self):
        from pulse.brain.index.embedding_populator import (
            populate_table, populate_all, embedding_to_numpy, numpy_to_blob,
        )

    def test_embedding_roundtrip(self):
        from pulse.brain.index.embedding_populator import embedding_to_numpy, numpy_to_blob
        vec = np.random.randn(384).astype(np.float32)
        blob = numpy_to_blob(vec)
        assert len(blob) == 384 * 4
        recovered = embedding_to_numpy(blob)
        np.testing.assert_array_almost_equal(vec, recovered)

    def test_embeddings_populated(self, db_conn):
        for table in ("lessons", "failures", "patterns", "facts", "evidence", "intents"):
            total = db_conn.execute(f"SELECT COUNT(*) FROM {table}").fetchone()[0]
            with_emb = db_conn.execute(
                f"SELECT COUNT(*) FROM {table} WHERE embedding IS NOT NULL"
            ).fetchone()[0]
            if total > 0:
                pct = with_emb / total
                assert pct > 0.95, f"{table}: only {pct:.0%} have embeddings"

    def test_embedding_dimensions(self, db_conn):
        row = db_conn.execute(
            "SELECT embedding FROM lessons WHERE embedding IS NOT NULL LIMIT 1"
        ).fetchone()
        if row and row[0]:
            vec = np.frombuffer(row[0], dtype=np.float32)
            assert vec.shape == (384,), f"Expected 384-dim, got {vec.shape}"
            assert np.linalg.norm(vec) > 0


class TestPhase3CrossRefs:

    def test_cross_ref_builder_imports(self):
        from pulse.brain.index.cross_ref_builder import build_cross_refs

    def test_cross_refs_format(self, db_conn):
        import json
        for table in ("lessons", "failures", "patterns", "facts"):
            rows = db_conn.execute(
                f"SELECT cross_refs FROM {table} WHERE cross_refs != '[]' LIMIT 5"
            ).fetchall()
            for r in rows:
                refs = json.loads(r[0])
                assert isinstance(refs, list)
                for ref in refs:
                    assert "id" in ref
                    assert "table" in ref
                    assert "sim" in ref
                    assert 0 < ref["sim"] <= 1.0


class TestPhase4ScoringCache:

    def test_scoring_cache_builds(self):
        from pulse.brain.brain_search.search_scoring_core import build_scoring_cache
        cache = build_scoring_cache()
        assert "retrieval_metrics" in cache
        assert "calibration_factor" in cache
        assert "ior_penalties" in cache
        assert isinstance(cache["retrieval_metrics"], dict)
        assert isinstance(cache["calibration_factor"], (int, float))

    def test_score_item_accepts_cache(self):
        from pulse.brain.brain_search.search_scoring_core import _score_item, build_scoring_cache
        cache = build_scoring_cache()
        item = {
            "content": "test content for scoring",
            "confidence": 0.5, "salience": 1.0,
            "access_count": 0, "evidence_count": 1,
            "validity": "valid", "domain": "general",
        }
        from pulse.core.temporal import temporal_confidence, temporal_salience
        from pulse.core.uncertainty import wilson_interval
        result = _score_item(
            item, "lessons", "test", "general", set(),
            None, {}, {}, temporal_confidence, temporal_salience, wilson_interval,
            scoring_cache=cache,
        )
        # May be filtered by quality gate, both outcomes valid
        assert result is None or isinstance(result, dict)

    def test_batch_ior_penalties(self):
        from pulse.brain.memory.wm_buffer import _get_all_ior_penalties
        penalties = _get_all_ior_penalties()
        assert isinstance(penalties, dict)


class TestPhase4FTS:

    def test_fts_stemming_imports(self):
        from pulse.brain.index.fts_stemming import migrate_fts_to_porter, test_stemming

    def test_fts_uses_porter(self, db_conn):
        row = db_conn.execute(
            "SELECT sql FROM sqlite_master WHERE name='knowledge_fts'"
        ).fetchone()
        if row:
            assert "porter" in row[0].lower(), f"FTS not using porter: {row[0]}"


class TestPhase4QueryExpansion:

    def test_query_expansion_imports(self):
        from pulse.brain.brain_search.query_expansion import (
            expand_query_via_graph, apply_faceted_filters,
        )

    def test_expansion_returns_string(self):
        from pulse.brain.brain_search.query_expansion import expand_query_via_graph
        result = expand_query_via_graph("embedding configuration")
        assert isinstance(result, str)
        assert len(result) >= len("embedding configuration")

    def test_faceted_filter_min_confidence(self):
        from pulse.brain.brain_search.query_expansion import apply_faceted_filters
        results = {"lessons": [
            {"confidence": 0.8, "domain": "general"},
            {"confidence": 0.3, "domain": "general"},
        ]}
        apply_faceted_filters(results, {"min_confidence": 0.5})
        assert len(results["lessons"]) == 1
        assert results["lessons"][0]["confidence"] == 0.8

    def test_faceted_filter_domain(self):
        from pulse.brain.brain_search.query_expansion import apply_faceted_filters
        results = {"facts": [
            {"confidence": 0.5, "domain": "neural_infra"},
            {"confidence": 0.5, "domain": "database"},
        ]}
        apply_faceted_filters(results, {"domain": "neural_infra"})
        assert len(results["facts"]) == 1

    def test_brain_search_accepts_filters(self):
        import inspect
        from pulse.brain.brain_search.search_engine import brain_search
        sig = inspect.signature(brain_search)
        assert "filters" in sig.parameters
        assert "expand_query" in sig.parameters


if __name__ == "__main__":
    pytest.main([__file__, "-v", "--tb=short"])
