# Copyright (c) 2026 PULSE Contributors. Proprietary. All rights reserved.
"""Query runner — orchestrate brain search and return formatted briefing."""

import logging

try:
    from pulse.brain.brain_search import brain_search
except Exception:
    brain_search = None

from .briefing import format_briefing
from .query_log import _log_query
from .reflex import _inline_reflex
from .briefing_ext import record_query_outcome as _record_outcome


def run_query(query: str, raw_prompt: str = "", brief: bool = False, model_tier: str = "") -> str:
    """Run brain search and return formatted briefing.

    If raw_prompt is provided, runs inline reflex detection first (<5ms).
    """
    reflex_lines = _inline_reflex(raw_prompt) if raw_prompt else []

    # FIX 9: Phonological buffer — store raw query for working memory rehearsal
    try:
        from pulse.brain.memory.working_memory import _add_phonological
        _add_phonological(query)
    except Exception:
        pass

    if not brain_search or not query or len(query) < 3:
        if reflex_lines:
            return "\n".join(["[BRAIN] Inline reflex detection"] + reflex_lines)
        return ""
    try:
        # Infer task_type for goal-conditioned attention (Phase 1 wiring)
        task_type = "general"
        ql = query.lower()
        if "fix" in ql or "debug" in ql or "error" in ql: task_type = "debug"
        elif "design" in ql or "arch" in ql: task_type = "design"
        elif "feat" in ql or "impl" in ql or "add" in ql: task_type = "feature"
        elif "review" in ql or "audit" in ql: task_type = "review"

        # Metacognitive control: adapt search depth to domain competence
        _max_per_source = None
        try:
            from pulse.brain.metacognition.metacognitive_control import get_search_directive
            directive = get_search_directive(query)
            _max_per_source = directive.max_per_source
        except Exception:
            pass

        # Phase C Item 11: Vector Prediction Prior
        # Only compute when model is already loaded (warm). Cold model load
        # takes ~24s and dominates query latency in subprocess calls.
        expected_emb = None
        if not brief:
            try:
                from pulse.brain.index.rule_embeddings.backends import _ST_MODEL, embed_texts, cosine_similarity
                if _ST_MODEL is not None:
                    expected_emb = embed_texts([query])[0]
            except Exception as e:
                logging.debug(f"Prediction Prior failed: {e}")

        if brief:
            # Brief mode: cap sources to 3 results each
            _max_per_source = 3
        results = brain_search(query, max_per_source=_max_per_source, task_type=task_type)
        total = results.get("total_results", 0)
        _log_query(query, total)

        # Modulate plasticity with mathematical surprise
        if expected_emb and total > 0:
            try:
                top_hit = None
                max_conf = -1.0
                for src, hits in results.get("results", {}).items():
                    if isinstance(hits, list):
                        for hit in hits:
                            conf = hit.get("confidence", 0)
                            if conf > max_conf:
                                max_conf = conf
                                top_hit = hit

                if top_hit:
                    hit_text = (top_hit.get("content") or top_hit.get("title") or top_hit.get("text", "")).strip()
                    if hit_text:
                        actual_emb = embed_texts([hit_text])[0]
                        similarity = cosine_similarity(expected_emb, actual_emb)
                        surprise = max(0.0, 1.0 - similarity)
                        
                        top_hit["_prediction_surprise"] = round(surprise, 4)
                        
                        if surprise > 0.05:
                            from pulse.brain.plasticity.reconsolidation_labile import mark_labile
                            item_id = top_hit.get("id") or top_hit.get("item_id")
                            table = top_hit.get("source") or top_hit.get("table_name")
                            if item_id and table:
                                mark_labile(item_id, table, prediction_error=surprise)
            except Exception as e:
                logging.debug(f"Plasticity modulation failed: {e}")

        # P2-3: Structured ignorance — record failed query outcome only.
        # NOTE: EXPLORE intent insertion REMOVED — it ran on every hook call
        # (PreToolUse + UserPromptSubmit), flooding the task board with junk
        # EXPLORE tasks via the DMN daemon.  Intents should be seeded by the
        # consolidation pipeline, not by the real-time query hot-path.
        if total == 0:
            try:
                _record_outcome(query, failed=True)
            except Exception:
                pass

        # Phase 3A: Global Workspace Ignition — skip in brief mode (expensive)
        _workspace_winners: list = []
        if not brief:
            try:
                from pulse.brain.workspace.global_workspace import run_workspace_ignition
                broadcast = run_workspace_ignition("brain_query", query, results, task_type)
                _workspace_winners = broadcast.get("winning_sources", [])
                _broadcast_items = broadcast.get("items", [])
                if _broadcast_items:
                    results["_workspace_broadcast"] = _broadcast_items
            except Exception as e:
                logging.debug(f"Workspace Ignition failed: {e}")

        briefing = format_briefing(query, results, model_tier=model_tier)

        if _workspace_winners:
            briefing = f"  [WORKSPACE IGNITION] Winning Coalitions: {', '.join(_workspace_winners).upper()}\n" + briefing

        if reflex_lines:
            # Inject reflex lines at the top, right after the header
            parts = briefing.split("\n", 1) if briefing else ["[BRAIN] Inline reflex"]
            header = parts[0]
            rest = parts[1] if len(parts) > 1 else ""
            return "\n".join([header] + reflex_lines + ([rest] if rest else []))
        return briefing
    except Exception:
        if reflex_lines:
            return "\n".join(["[BRAIN] Inline reflex detection"] + reflex_lines)
        return ""
