# Copyright (c) 2026 PULSE Contributors. Proprietary. All rights reserved.
"""Briefing signal helpers — module-level state, somatic veto, and signal generators.

Extracted from briefing.py to keep both files under 300 lines.
Contains: module-level state, _check_somatic_veto, _DANGER_WORDS,
          signal helper functions, _format_explain.
"""

from collections import deque

# Module-level state shared by briefing.py (imported directly)
_RECENT_QUALITY: deque = deque(maxlen=5)
_RECENT_QUERIES: list = []
_HABITUATION_THRESHOLD = 3

# P10-35: Priority keyword scanner — hardwired danger words
_DANGER_WORDS = frozenset({
    "security", "crash", "data_loss", "corruption",
    "vulnerability", "exploit", "injection", "overflow"
})


def _check_somatic_veto(hits: dict) -> list[str]:
    """Return SOMATIC_VETO lines for anti-pattern hits with conf > 0.8."""
    veto_lines = []
    for h in hits.get("anti_patterns", []):
        if (h.get("type") == "anti_pattern" or h.get("source") == "anti_patterns") and h.get("confidence", 0.0) > 0.8:
            rule = (h.get("title") or h.get("text", ""))[:120].strip()
            if rule:
                veto_lines.append(f"!! SOMATIC_VETO: {rule}")
    return veto_lines


def get_flexibility_warning() -> list[str]:
    """Item 184: Cognitive flexibility — warn on tunnel vision."""
    try:
        from pulse.brain.metacognition.metamemory_tracking import get_cognitive_flexibility_index
        import os as _os184
        _agent184 = _os184.environ.get('PULSE_AGENT_NAME', 'unknown')
        _session184 = _os184.environ.get('PULSE_SESSION_ID', 'default')
        _flex184 = get_cognitive_flexibility_index(_agent184, _session184)
        if _flex184.get('tunnel_vision_warning') and _flex184.get('unique_domains', 0) > 0:
            return [
                f"!! TUNNEL VISION: Only {_flex184['unique_domains']} domain(s) accessed this session. "
                f"Broaden exploration (flexibility_score={_flex184['flexibility_score']:.2f})"
            ]
    except Exception:
        pass
    return []


def check_schema_violations(all_hits_flat: list) -> list[str]:
    """Item 109: Schema violation detection — flag results conflicting with established schemas."""
    extra_lines = []
    try:
        from pulse.core.brain_db import get_conn as _gc109
        _conn109 = _gc109()
        _schemas109 = _conn109.execute(
            "SELECT name, description FROM schemas WHERE confidence >= 0.7 LIMIT 10"
        ).fetchall()
        from pulse.core.brain_analysis import text_similarity as _ts109
        for _h109 in all_hits_flat[:5]:
            for _s109 in _schemas109:
                if _ts109(_h109.get("text", "")[:100], _s109["description"][:100]) > 0.5:
                    if _h109.get("_contradiction_flag"):
                        extra_lines.append(
                            f"  !! SCHEMA VIOLATION: '{_h109.get('text','')[:40]}' "
                            f"conflicts with schema '{_s109['name']}'"
                        )
                        break
    except Exception:
        pass
    return extra_lines


def check_interference(all_hits_flat: list) -> list[str]:
    """Item 87: Interference detection — flag contradicting results."""
    extra_lines = []
    try:
        from pulse.core.brain_analysis import text_similarity as _ts87
        _seen87 = False
        for _i87, _a87 in enumerate(all_hits_flat[:10]):
            if _seen87:
                break
            for _b87 in all_hits_flat[_i87 + 1:10]:
                _sim87 = _ts87(_a87.get("text", "")[:100], _b87.get("text", "")[:100])
                if 0.4 < _sim87 < 0.8 and _a87.get("source") != _b87.get("source"):
                    extra_lines.append(
                        f"  !! CONFLICT: '{_a87.get('text','')[:40]}' vs '{_b87.get('text','')[:40]}'"
                    )
                    _seen87 = True
                    break
    except Exception:
        pass
    return extra_lines


def render_standard_sections(hits: dict, _trunc: int, _system_mode: str) -> list[str]:
    """Render standard briefing sections (wins, domains, patterns, schemas, etc.)."""
    section_lines = []
    for key, prefix, n in [
        ("wins", "  * PROVEN:", 2), ("domains", "  * DOMAIN:", 1),
        ("patterns", "  * PATTERN:", 1), ("schemas", "  @ SCHEMA:", 2),
        ("procedures", "  @ PROCEDURE:", 2), ("evidence", "  * EVIDENCE:", 2),
        ("episodic", "  ~ EPISODE:", 2),
    ]:
        for h in hits.get(key, [])[:n]:
            text = (h.get("text") or h.get("title") or h.get("description", ""))[:_trunc].strip()
            if text:
                if _system_mode == "S2":
                    section_lines.append(
                        f"{prefix} {text} (conf={h.get('confidence', 0):.2f}, src={h.get('source', '')})"
                    )
                else:
                    section_lines.append(f"{prefix} {text}")

    # Facts (declarative reference knowledge)
    for h in hits.get("facts", [])[:3]:
        text = (h.get("text") or h.get("title", ""))[:100].strip()
        if not text:
            continue
        suffix = f" [{h.get('domain', '')}]" if h.get("domain") else ""
        if h.get("temporal") == "historical":
            suffix += " (historical)"
        section_lines.append(f"  > FACT: {text}{suffix}")

    # Decisions (N0) + Graph (causal)
    for h in hits.get("decisions", [])[:2]:
        text = (h.get("text") or h.get("title", ""))[:100].strip()
        if text:
            tag = "[SUPERSEDED] " if h.get("status") == "superseded" else ""
            section_lines.append(f"  @ DECISION: {tag}{text}")
    for h in hits.get("graph", [])[:2]:
        text = (h.get("text") or h.get("title", ""))[:80].strip()
        if text:
            section_lines.append(f"  # CAUSAL ({h.get('relationship', '')}): {text}")

    return section_lines


def _format_explain(query: str, results: dict) -> str:
    """Explain mode: show score breakdown for each result."""
    lines = [
        f"[BRAIN EXPLAIN] Query: {query}",
        f"Total: {results.get('total_results', 0)} results in {results.get('elapsed_ms', 0)}ms",
    ]
    for source, items in sorted(results.get("results", {}).items()):
        if not isinstance(items, list) or not items:
            continue
        lines.append(f"-- {source.upper()} ({len(items)} hits) --")
        for h in items[:5]:
            text = (h.get("content") or h.get("text") or h.get("title", ""))[:60].strip()
            conf, sal, w = h.get("confidence", 0), h.get("salience", 0), h.get("_wilson_lower", 0)
            domain, comp = h.get("domain", "?"), h.get("_domain_competence", "")
            flags = (
                (["CONTESTED"] if h.get("_contradiction_flag") else [])
                + (["LOW_DOMAIN"] if h.get("_low_confidence_domain") else [])
                + ([f"trust={h['_source_trust']:.2f}"] if h.get("_source_trust") else [])
            )
            flag_str = f" [{', '.join(flags)}]" if flags else ""
            lines.append(
                f"  conf={conf:.2f} sal={sal:.1f} wilson={w:.2f} domain={domain}"
                + (f"({comp})" if comp else "") + flag_str
            )
            lines.append(f"    {text}")
    return "\n".join(lines)

from .briefing_signals_render import (
    adversarial_injection, surprise_detection, predictive_triage,
    cortical_columns_summary, render_predictions_and_warnings,
)