"""P11 Neuroscience Refinements — Integration tests (Items 36-40)."""
import importlib
import sys
import types
import time
from collections import deque
from pathlib import Path
from unittest.mock import patch, MagicMock

ROOT = Path(__file__).parent.parent

# ---------------------------------------------------------------------------
# Item 37: Domain footprint contraction on dormancy
# ---------------------------------------------------------------------------

def _read_maintenance_src():
    return (ROOT / "pulse/daemons/consolidation/stages/stage_maintenance.py").read_text(encoding="utf-8")


def test_footprint_contraction_code_present():
    """Domain footprint contraction block must exist in stage_maintenance.py."""
    src = _read_maintenance_src()
    assert "salience * 0.9" in src or "salience*0.9" in src, "Contraction multiplier 0.9 missing"


def test_footprint_contraction_skips_permanent():
    """Contraction query must have validity != 'permanent' guard."""
    src = _read_maintenance_src()
    assert "permanent" in src, "permanent validity guard missing from homeostasis"


def test_footprint_contraction_uses_seven_days():
    """Contraction threshold must be 7 days."""
    src = _read_maintenance_src()
    assert "days=7" in src or "timedelta(days=7)" in src, "7-day threshold missing"


def test_footprint_contraction_covers_all_tables():
    """Contraction must cover lessons, failures, and patterns tables."""
    src = _read_maintenance_src()
    contraction_block = src[src.find("Domain footprint"):]
    for table in ["lessons", "failures", "patterns"]:
        assert table in contraction_block, f"Table '{table}' missing from contraction logic"


def test_footprint_contraction_fail_open():
    """Contraction block must be wrapped in try/except (fail-open)."""
    src = _read_maintenance_src()
    contraction_idx = src.find("Domain footprint")
    block = src[contraction_idx:contraction_idx + 1000]
    assert "except Exception" in block, "Contraction block missing fail-open try/except"


# ---------------------------------------------------------------------------
# Item 38: Cognitive load-based System 1/2 switching
# ---------------------------------------------------------------------------

def _read_briefing_src():
    return (ROOT / "pulse/brain/brain_query/briefing.py").read_text(encoding="utf-8")


def test_system12_switching_code_present():
    """System 1/2 switching block must exist in briefing.py."""
    src = _read_briefing_src()
    assert "agent_load" in src, "agent_load check missing from briefing.py"
    assert "_max_items" in src, "_max_items variable missing from briefing.py"


def test_system1_max_items_is_3():
    """System 1 must limit to 3 items."""
    src = _read_briefing_src()
    assert "_max_items = 3" in src, "System 1 max_items=3 not found"


def test_system2_max_items_is_8():
    """System 2 must allow up to 8 items."""
    src = _read_briefing_src()
    assert "_max_items = 8" in src, "System 2 max_items=8 not found"


def test_system12_threshold_is_5():
    """Load threshold for S1/S2 switch must be 5."""
    src = _read_briefing_src()
    assert "load > 5" in src or "_load > 5" in src, "Load threshold of 5 not found"


def test_system12_fail_open():
    """System 1/2 block must be wrapped in try/except."""
    src = _read_briefing_src()
    block_start = src.find("Cognitive load")
    block = src[block_start:block_start + 700]
    assert "except Exception" in block, "System 1/2 block missing fail-open try/except"


def test_system12_applied_to_lines():
    """_max_items must be used to slice lines."""
    src = _read_briefing_src()
    assert "lines[:_max_items]" in src, "_max_items not applied to lines slice"


# ---------------------------------------------------------------------------
# Item 39: Drift detection -> attention override
# ---------------------------------------------------------------------------

def test_drift_detection_sets_override_flag():
    """When one source has >70% of hits, minority sources get _drift_override=True."""
    results = {
        "wins": [{"text": f"w{i}", "confidence": 0.7, "_wilson_lower": 0.6} for i in range(15)],
        "fails": [{"text": "f0", "confidence": 0.5, "_wilson_lower": 0.4}],
        "patterns": [{"text": "p0", "confidence": 0.5, "_wilson_lower": 0.4}],
    }
    total = sum(len(v) for v in results.values() if isinstance(v, list))
    drift_detected = False
    for src, hits in list(results.items()):
        if isinstance(hits, list) and len(hits) / total > 0.7:
            drift_detected = True
            for other_src, other_hits in results.items():
                if other_src != src and isinstance(other_hits, list):
                    for h in other_hits[:2]:
                        h["_drift_override"] = True
                        h["confidence"] = max(h.get("confidence", 0.5), 0.6)
            break

    assert drift_detected, "Drift should be detected with dominant source"
    overridden = [h for src in ("fails", "patterns") for h in results[src] if h.get("_drift_override")]
    assert len(overridden) > 0, "No items received drift override flag"


def test_drift_detection_boosts_confidence():
    """Drift override must set confidence >= 0.6 on minority items."""
    results = {
        "wins": [{"text": f"w{i}", "confidence": 0.7} for i in range(15)],
        "fails": [{"text": "f0", "confidence": 0.3}],
        "patterns": [{"text": "p0", "confidence": 0.3}],
    }
    total = sum(len(v) for v in results.values() if isinstance(v, list))
    for src, hits in list(results.items()):
        if isinstance(hits, list) and len(hits) / total > 0.7:
            for other_src, other_hits in results.items():
                if other_src != src and isinstance(other_hits, list):
                    for h in other_hits[:2]:
                        h["_drift_override"] = True
                        h["confidence"] = max(h.get("confidence", 0.5), 0.6)
            break

    overridden = [h for src in ("fails", "patterns") for h in results[src] if h.get("_drift_override")]
    assert all(h["confidence"] >= 0.6 for h in overridden), "Drift override did not boost confidence"


def test_no_drift_when_balanced():
    """When no source dominates, no drift override should be set."""
    results = {
        "wins": [{"text": f"w{i}", "confidence": 0.7} for i in range(5)],
        "fails": [{"text": f"f{i}", "confidence": 0.6} for i in range(5)],
        "patterns": [{"text": f"p{i}", "confidence": 0.5} for i in range(5)],
    }
    total = sum(len(v) for v in results.values() if isinstance(v, list))
    drift_detected = False
    for src, hits in list(results.items()):
        if isinstance(hits, list) and len(hits) / total > 0.7:
            drift_detected = True
            break
    assert not drift_detected, "No drift should be detected in balanced results"


def test_drift_detection_in_search_engine_source():
    """Drift detection code must exist in search_engine.py source."""
    src = (ROOT / "pulse/brain/brain_search/search_engine.py").read_text(encoding="utf-8")
    assert "_drift_override" in src, "_drift_override not found in search_engine.py"
    assert "0.7" in src, "70% threshold not found in search_engine.py"


def test_drift_detection_fail_open():
    """Drift detection block must be wrapped in try/except (fail-open)."""
    src = (ROOT / "pulse/brain/brain_search/search_engine.py").read_text(encoding="utf-8")
    drift_idx = src.find("_drift_override")
    # Find the enclosing try/except
    block_before = src[max(0, drift_idx - 400):drift_idx + 200]
    assert "except Exception" in block_before, "Drift block not wrapped in fail-open try/except"


# ---------------------------------------------------------------------------
# Item 40: Sustained attention quality metric
# ---------------------------------------------------------------------------

def test_attention_drift_warning_on_declining_quality():
    """If quality declines over last 3+ queries, ATTENTION DRIFT must appear in briefing."""
    import pulse.brain.brain_query.briefing as briefing
    importlib.reload(briefing)

    briefing._RECENT_QUALITY.clear()
    briefing._RECENT_QUALITY.extend([0.9, 0.8, 0.7, 0.6])

    results = {
        "results": {"wins": [{"text": "item1", "confidence": 0.5}]},
        "total_results": 1,
        "elapsed_ms": 1,
    }
    output = briefing.format_briefing("test declining", results)
    assert "ATTENTION DRIFT" in output, f"ATTENTION DRIFT warning not in output:\n{output}"


def test_no_attention_drift_on_stable_quality():
    """If quality is stable or improving, no drift warning should appear."""
    import pulse.brain.brain_query.briefing as briefing
    importlib.reload(briefing)

    briefing._RECENT_QUALITY.clear()
    briefing._RECENT_QUALITY.extend([0.5, 0.6, 0.7, 0.8])

    results = {
        "results": {"wins": [{"text": "item1", "confidence": 0.8}]},
        "total_results": 1,
        "elapsed_ms": 1,
    }
    output = briefing.format_briefing("test stable", results)
    assert "ATTENTION DRIFT" not in output, "False ATTENTION DRIFT triggered on stable quality"


def test_attention_drift_deque_maxlen():
    """_RECENT_QUALITY must be a deque with maxlen=5."""
    import pulse.brain.brain_query.briefing as briefing
    importlib.reload(briefing)
    assert isinstance(briefing._RECENT_QUALITY, deque), "_RECENT_QUALITY is not a deque"
    assert briefing._RECENT_QUALITY.maxlen == 5, f"Expected maxlen=5, got {briefing._RECENT_QUALITY.maxlen}"


def test_attention_drift_accumulates_over_queries():
    """Each call to format_briefing should add to _RECENT_QUALITY."""
    import pulse.brain.brain_query.briefing as briefing
    importlib.reload(briefing)
    briefing._RECENT_QUALITY.clear()

    results = {
        "results": {"wins": [{"text": "item", "confidence": 0.7}]},
        "total_results": 1,
        "elapsed_ms": 1,
    }
    briefing.format_briefing("q1", results)
    briefing.format_briefing("q2", results)
    assert len(briefing._RECENT_QUALITY) >= 2, "Quality deque not accumulating"


def test_attention_drift_no_crash_on_empty_results():
    """Attention drift logic must not crash when results are empty."""
    import pulse.brain.brain_query.briefing as briefing
    importlib.reload(briefing)

    results = {"results": {}, "total_results": 0, "elapsed_ms": 1}
    try:
        output = briefing.format_briefing("empty query", results)
        assert isinstance(output, str)
    except Exception as e:
        assert False, f"Crashed on empty results: {e}"


def test_attention_drift_requires_minimum_3_samples():
    """ATTENTION DRIFT should not trigger with fewer than 3 pre-existing samples
    when the new query's quality matches (non-declining)."""
    import pulse.brain.brain_query.briefing as briefing
    importlib.reload(briefing)

    briefing._RECENT_QUALITY.clear()
    # Pre-load 2 entries; then call with high confidence so new value is NOT lower
    briefing._RECENT_QUALITY.extend([0.4, 0.3])  # declining but only 2 entries

    results = {
        "results": {"wins": [{"text": "item", "confidence": 0.9}]},
        "total_results": 1,
        "elapsed_ms": 1,
    }
    # After append: [0.4, 0.3, 0.9] — not strictly declining, so no ATTENTION DRIFT
    output = briefing.format_briefing("test not declining", results)
    assert "ATTENTION DRIFT" not in output, "ATTENTION DRIFT should not trigger on non-declining series"
