#!/usr/bin/env python3
# Copyright (c) 2026 PULSE Contributors. Proprietary. All rights reserved.
"""Surprise-Gated Attention (Predictive Coding).

Implements Gap 3 from QR8. Part of the Predictive Coding mechanism.
If a new query is semantically novel compared to recent queries, the search
depth is automatically increased to improve the chances of finding relevant but
distant information.
"""
import logging
from typing import Tuple

log = logging.getLogger("pulse.surprise_gate")
_EMBED_FUNC = None
_QUERY_LOG_FUNC = None

def _init_dependencies():
    """Lazy load heavy dependencies to keep agent boot fast."""
    global _EMBED_FUNC, _QUERY_LOG_FUNC
    if _EMBED_FUNC is None:
        from pulse.brain.index.rule_embeddings.backends import embed_texts
        _EMBED_FUNC = embed_texts
    if _QUERY_LOG_FUNC is None:
        from pulse.daemons.neural.prediction_daemon import _get_recent_query_log
        _QUERY_LOG_FUNC = _get_recent_query_log

def get_surprising_search_depth(query: str, default_top_k: int, default_max_per_source: int) -> Tuple[int, int]:
    """Returns (top_k, max_per_source), increasing them if the query is novel."""
    try:
        _init_dependencies()
        import numpy as np

        recent_queries = [q.get("query", "") for q in _QUERY_LOG_FUNC(limit=20)]
        if not recent_queries:
            return default_top_k, default_max_per_source

        query_vec_result = _EMBED_FUNC([query])
        query_vec = np.array(query_vec_result[0])

        recent_vecs_result = _EMBED_FUNC(recent_queries)
        recent_vecs = np.array(recent_vecs_result)

        sims = np.dot(recent_vecs, query_vec) / (np.linalg.norm(recent_vecs, axis=1) * np.linalg.norm(query_vec))
        max_sim = np.max(sims) if len(sims) > 0 else 0.0

        if max_sim < 0.65:
            log.info(f"Surprise-gated attention: Novel query (sim={max_sim:.2f}). Deepening search.")
            return 15, 15
            
    except Exception as e:
        log.debug(f"Surprise gate check failed: {e}")

    return default_top_k, default_max_per_source
