#!/usr/bin/env python3
# Copyright (c) 2026 PULSE Contributors. Proprietary. All rights reserved.
"""
Post-scoring enrichment for brain search results.

Extracted from search_scoring.py to keep that file under 300 lines.
Contains: metamemory annotation, attention weighting, drift detection,
spreading activation, semantic priming, dedup, and P29 surprise attention.
"""
import logging
import time as _time_ps

log = logging.getLogger("pulse.brain_search.search_post_scoring")


def apply_post_scoring_impl(results, query, task_type,
                            apply_urgency_boost_fn, apply_semantic_satiation_fn,
                            apply_contextual_inhibition_fn, parent_log):
    """Full post-scoring pipeline. Called by _apply_post_scoring in search_scoring.py."""
    _ps_start = _time_ps.time()
    _log = parent_log or log
    # Metamemory domain annotation
    try:
        from pulse.brain.metacognition.metamemory import get_metamemory
        _mm = get_metamemory()
        if _mm:
            for _sh in results.values():
                if not isinstance(_sh, list): continue
                for h in _sh:
                    d = h.get("domain", "")
                    if d and d in _mm:
                        h["_domain_competence"] = _mm[d].get("competence_level", "unknown")
                        if _mm[d].get("gap_flag"):
                            h["_low_confidence_domain"] = True
    except Exception as e: _log.debug("Metamemory annotation failed: %s", e)
    # Attention weighting
    if task_type and task_type != "general":
        try:
            from pulse.brain.workspace.attention import apply_attention_to_results
            apply_attention_to_results({"results": results}, task_type)
            for sn, hits in results.items():
                if isinstance(hits, list) and any(h.get("_attention_confidence") for h in hits):
                    hits.sort(key=lambda x: x.get("_attention_confidence", x.get("_wilson_lower", 0)), reverse=True)
        except Exception as e: _log.debug("Attention weighting failed: %s", e)
    # Item 39: Drift detection
    try:
        _tot = sum(len(v) for v in results.values() if isinstance(v, list))
        if _tot > 0:
            for _src, _hits in list(results.items()):
                if isinstance(_hits, list) and len(_hits) / _tot > 0.7:
                    for _os, _oh in results.items():
                        if _os != _src and isinstance(_oh, list):
                            for _h in _oh[:2]:
                                _h["_drift_override"] = True
                                _h["confidence"] = max(_h.get("confidence", 0.5), 0.6)
                    break
    except Exception: pass
    # Spreading activation via COACTIVATION edges
    try:
        from pulse.core.brain_db import get_conn as _get_db_conn
        _conn = _get_db_conn()
        _all_e = sorted(
            [h for src in results.values() if isinstance(src, list) for h in src],
            key=lambda x: x.get("_wilson_lower", 0), reverse=True)
        top_ids = [h.get("id") for h in _all_e[:3] if h.get("id")]
        _spread_ids = [
            row[0] for _iid in top_ids
            for row in _conn.execute(
                "SELECT to_node FROM graph_edges WHERE from_node=? "
                "AND edge_type='COACTIVATION' AND confidence > 0.5 LIMIT 2", (_iid,)
            ).fetchall()
        ]
        try:
            for _rid in list(_spread_ids):
                hop2 = _conn.execute(
                    "SELECT to_node FROM graph_edges WHERE from_node=? "
                    "AND edge_type='COACTIVATION' AND confidence > 0.4 LIMIT 2",
                    (_rid,)
                ).fetchall()
                for r2 in hop2:
                    if r2[0] not in _spread_ids and r2[0] not in top_ids:
                        _spread_ids.append(r2[0])
        except Exception:
            pass
        if _spread_ids:
            for _rid in _spread_ids:
                _nr = _conn.execute("SELECT text FROM graph_nodes WHERE id=?", (_rid,)).fetchone()
                results.setdefault("spreading_activation", []).append({
                    "id": _rid, "text": _nr[0] if _nr else "",
                    "type": "spreading_activation", "source": "coactivation_edge",
                    "_spread_from": top_ids, "confidence": 0.4, "salience": 0.5,
                })
            try:
                from pulse.brain.memory.working_memory import _update_sketch
                _update_sketch(_spread_ids)
            except Exception: pass
    except Exception: pass
    # Item 86: Semantic priming
    try:
        _conn_sp = __import__("pulse.core.brain_db", fromlist=["get_conn"]).get_conn()
        words = [x for x in query.lower().split() if len(x) >= 4][:3]
        for w in words:
            for r in _conn_sp.execute(
                "SELECT gn.text, gn.confidence FROM graph_nodes gn "
                "WHERE gn.text LIKE ? AND gn.type='CONCEPT' LIMIT 3", ("%" + w + "%",)
            ).fetchall():
                results.setdefault("semantic_priming", []).append({
                    "text": r[0], "confidence": float(r[1] or 0.3) * 0.5,
                    "type": "semantic_prime", "source": "graph_concept",
                })
    except Exception: pass
    # Item 114: Urgency boost / 115: Satiation / 118: Contextual inhibition
    try: apply_urgency_boost_fn(results, query)
    except Exception: pass
    try: apply_semantic_satiation_fn(results)
    except Exception: pass
    try: apply_contextual_inhibition_fn(results, query)
    except Exception: pass
    # Item 95: Cognitive load signal (P23)
    try:
        n = sum(1 for v in results.values() if isinstance(v, list) for _ in v)
        __import__("pulse.core.interoception", fromlist=["update_signal"]).update_signal(
            "search_latency", min(10, n / 100))
    except Exception: pass
    # Item 157: Retrieval fluency — record elapsed time for latency trend monitoring
    try:
        from pulse.brain.brain_search.search_scoring_p36 import record_search_latency
        elapsed_ms = (_time_ps.time() - _ps_start) * 1000
        record_search_latency(elapsed_ms / 1000.0)
    except Exception: pass
    # Item 102: Search result dedup (P25)
    try:
        _seen = {}
        for _s in list(results.keys()):
            _h = results[_s]
            if not isinstance(_h, list): continue
            deduped = []
            for item in _h:
                fp = item.get("fingerprint", item.get("id", ""))
                if not fp or fp not in _seen:
                    if fp: _seen[fp] = True
                    deduped.append(item)
            results[_s] = deduped
    except Exception: pass
    # Item 124: Surprise-based attention capture (P29)
    try:
        from pulse.brain.brain_search.search_scoring_ext import apply_surprise_attention
        apply_surprise_attention(results)
    except Exception: pass
    # Item 132: Source credibility weighting (P32)
    try:
        from pulse.brain.metacognition.metamemory_tracking import apply_source_credibility_to_results
        apply_source_credibility_to_results(results)
    except Exception: pass
    # Item 131: Attention blink (P32)
    try:
        from pulse.brain.brain_search.search_scoring_p32 import apply_attention_blink
        apply_attention_blink(results)
    except Exception: pass
    # Item 134: Memory competition (P32)
    try:
        from pulse.brain.brain_search.search_scoring_p32 import apply_memory_competition
        apply_memory_competition(results)
    except Exception: pass
    # Item 140: Provenance chain (P33)
    try:
        from pulse.brain.brain_search.search_scoring_p32 import apply_provenance_chain
        apply_provenance_chain(results)
    except Exception: pass
    # N60 (Item 154): Memory interference resolution — suppress contradicting lower-conf items
    try:
        from pulse.brain.brain_search.search_scoring_p36 import resolve_memory_interference
        resolve_memory_interference(results)
    except Exception: pass
    # Items 143, 145, 146: P34 scoring — associative strength, freshness, task relevance
    try:
        from pulse.brain.brain_search.search_scoring_p34 import (
            apply_associative_strength, annotate_freshness, apply_task_relevance_filter,
        )
        apply_associative_strength(results)
        annotate_freshness(results)
        task_desc = query if task_type and task_type != "general" else None
        apply_task_relevance_filter(results, task_desc)
    except Exception: pass
    # N60 (Item 163): Neighborhood density bonus — boost well-connected items
    try:
        from pulse.brain.brain_search.search_scoring_p36 import apply_neighborhood_density_bonus
        apply_neighborhood_density_bonus(results)
    except Exception: pass
    # Item 176: Visuospatial sketchpad READ — boost results matching recently accessed graph nodes
    try:
        from pulse.brain.memory.working_memory import _VISUOSPATIAL_SKETCH
        if _VISUOSPATIAL_SKETCH:
            sketch_set = set(_VISUOSPATIAL_SKETCH)
            for _src in results.values():
                if not isinstance(_src, list):
                    continue
                for h in _src:
                    if h.get("id") in sketch_set:
                        h["confidence"] = min(1.0, float(h.get("confidence", 0.5)) + 0.05)
                        h["_sketch_boost"] = True
    except Exception: pass
    # N60 (Item 168): Search diversity metric — warn if results are tunnel-vision single-domain
    try:
        from pulse.brain.brain_search.search_scoring_p36 import compute_search_diversity
        _diversity = compute_search_diversity(results)
        if _diversity.get("warning"):
            results.setdefault("_meta", []).append({
                "type": "diversity_warning",
                "text": _diversity["warning"],
                "diversity_score": _diversity.get("diversity_score", 0.0),
                "unique_domains": _diversity.get("unique_domains", 0),
            })
    except Exception: pass
