#!/usr/bin/env python3
# Copyright (c) 2026 PULSE Contributors. Proprietary. All rights reserved.
# See LICENSE file for details.
"""
Bridge Routing: Knowledge routing and evidence writing for pulse_bridge.

Handles routing extracted knowledge to brain regions (lessons, patterns,
failures, decisions) and appending evidence to session files.

Split from pulse_bridge.py for Law 7 compliance.
"""

import json
import logging
from datetime import datetime, timezone
from pathlib import Path

SCRIPT_DIR = Path(__file__).resolve().parent
ROOT_DIR = SCRIPT_DIR.parent.parent.parent

import sys
from pulse.core.brain_utils import (
    atomic_update_json, HIPPOCAMPUS_DIR, EVIDENCE_DIR,
    LESSONS_FILE, PATTERNS_FILE, FAILS_FILE, LESSONS_JSONL, text_similarity,
)
from pulse.brain.save_writers import append_knowledge_md, append_decision

OUTPUT_JSON = EVIDENCE_DIR / "Sessions" / "current_session.json"
CONTRADICTION_LOG = ROOT_DIR / "state" / "contradiction_log.json"


def append_to_evidence(parsed_data, user_text="", agent_text="", verbose=False):
    """Appends parsed data to current_session.json with file locking."""
    new_items = []
    ts = parsed_data["timestamp"]
    domain = parsed_data["primary_domain"]
    visibility = parsed_data.get("visibility", "public")

    for want in parsed_data["wants"]:
        new_items.append({
            "type": "WANT",
            "domain": domain,
            "description": want["want"],
            "priority": want["priority"],
            "visibility": want.get("visibility", visibility),
            "timestamp": ts,
            "source": "pulse_bridge"
        })

    for dw in parsed_data["dont_wants"]:
        new_items.append({
            "type": "DONT_WANT",
            "domain": domain,
            "description": dw["dont_want"],
            "reason": dw["reason"],
            "visibility": dw.get("visibility", visibility),
            "timestamp": ts,
            "source": "pulse_bridge"
        })

    if not new_items:
        desc_parts = []
        if user_text:
            desc_parts.append(f"User: {user_text[:100]}")
        if agent_text:
            desc_parts.append(f"Agent: {agent_text[:100]}")
        description = " | ".join(desc_parts) if desc_parts else "[no content]"
        new_items.append({
            "type": "OBSERVATION",
            "domain": domain,
            "description": description,
            "visibility": visibility,
            "timestamp": ts,
            "source": "pulse_bridge"
        })

    # Context envelope: attach git branch + recent files to every item
    try:
        from pulse.brain.session.context_envelope import get_context_envelope
        ctx = get_context_envelope()
        for it in new_items:
            it["context"] = ctx
    except Exception:
        pass

    # Layer 3: Sign evidence items for integrity tracking
    from pulse.core.pulse_crypto import sign_item
    new_items = [sign_item(it, it.get("source", "bridge")) for it in new_items]

    def _updater(items):
        if not isinstance(items, list):
            items = []
        items.extend(new_items)
        return items

    # P47: Also write observation to SQLite evidence table
    try:
        from pulse.core.brain_db import get_conn
        _conn = get_conn()
        import hashlib as _hl
        _obs_text = (user_text[:800] + ' | ' + agent_text[:800]) if agent_text else user_text[:1600]
        _fp = _hl.md5(_obs_text[:100].encode()).hexdigest()[:12]
        with _conn:
            _conn.execute(
                "INSERT OR IGNORE INTO evidence (id, fingerprint, type, content, title, domain, agent, timestamp, salience, confidence) "
                "VALUES (?, ?, 'observation', ?, ?, ?, ?, ?, 1.0, 0.6)",
                (f"OBS-{_fp}", _fp, _obs_text[:2000], _obs_text[:80],
                 parsed_data.get('primary_domain', 'general'), parsed_data.get('source', 'bridge').lower(),
                 parsed_data.get('timestamp', '')))
    except Exception:
        pass
    atomic_update_json(OUTPUT_JSON, _updater, default=[])

    if verbose:
        print(f"[BRIDGE] Digested {len(new_items)} items into current_session.json")


def _check_lesson_contradiction(failure_desc: str, source: str, confidence: float):
    """Check if a failure contradicts an existing lesson (U5: Dr. Anterior Cingulate Cortex).

    When routing a FAILURE: search lessons for similarity >= 0.5.
    If found, log to contradiction_log.json. After 3 repeats, queue
    the lesson for confidence reduction. Tries JSONL first, falls back to MD.
    """
    if len(failure_desc) < 20:
        return

    # JSONL fast-path: structured titles, no regex needed
    lesson_titles = []
    if LESSONS_JSONL.exists() and LESSONS_JSONL.stat().st_size > 0:
        try:
            import json as _json
            with open(LESSONS_JSONL, "r", encoding="utf-8") as f:
                for line in f:
                    line = line.strip()
                    if not line:
                        continue
                    try:
                        entry = _json.loads(line)
                        title = entry.get("title", "")
                        if title and len(title) >= 10:
                            lesson_titles.append(title)
                    except Exception:
                        continue
        except Exception:
            pass

    # JSONL is sole source — no MD fallback

    if not lesson_titles:
        return
    try:
        for title in lesson_titles:
            sim = text_similarity(failure_desc[:80].lower(), title[:80].lower())
            if sim >= 0.5:
                # Found contradiction — log it
                def _updater(data):
                    if not isinstance(data, dict):
                        data = {"contradictions": [], "repeat_counts": {}}
                    data.setdefault("contradictions", [])
                    data.setdefault("repeat_counts", {})
                    key = failure_desc[:60].lower()
                    data["repeat_counts"][key] = data["repeat_counts"].get(key, 0) + 1
                    data["contradictions"].append({
                        "failure": failure_desc[:200],
                        "contradicts_lesson": title[:200],
                        "similarity": round(sim, 2),
                        "source": source,
                        "confidence": confidence,
                        "repeat_count": data["repeat_counts"][key],
                        "timestamp": datetime.now(timezone.utc).isoformat(),
                    })
                    data["contradictions"] = data["contradictions"][-200:]
                    # Keep repeat_counts bounded
                    if len(data["repeat_counts"]) > 500:
                        sorted_keys = sorted(data["repeat_counts"], key=data["repeat_counts"].get)
                        for k in sorted_keys[:250]:
                            del data["repeat_counts"][k]
                    return data
                CONTRADICTION_LOG.parent.mkdir(parents=True, exist_ok=True)
                atomic_update_json(CONTRADICTION_LOG, _updater,
                                   default={"contradictions": [], "repeat_counts": {}})
                return  # Log first match only
    except Exception:
        logging.debug("Suppressed exception in bridge_routing", exc_info=True)


def route_knowledge(knowledge_items, source, verbose=False):
    """Route extracted knowledge to the correct brain regions."""
    if not knowledge_items:
        return

    routed = {"LEARNING": 0, "DECISION": 0, "SOLUTION": 0, "FAILURE": 0, "PATTERN": 0}

    for item in knowledge_items:
        ktype = item.get("type", "")
        desc = item.get("description", "").strip()
        if not desc:
            continue
        ts = item.get("timestamp", datetime.now(timezone.utc).isoformat())
        domain = item.get("domain", "general")
        confidence = item.get("confidence", 0.5)
        salience = item.get("salience", 1.0)

        # Dimension A: flag if this item matches a pending contradiction
        try:
            from pulse.admin.audit.truth_verifier import quick_contradiction_check
            if quick_contradiction_check(desc, ktype):
                item["_contradiction_flag"] = True
                confidence = max(0.3, confidence - 0.1)  # Slight penalty
        except Exception:
            pass

        if ktype == "LEARNING":
            append_knowledge_md(LESSONS_FILE, desc, source, ts, domain, confidence, salience)
            routed["LEARNING"] += 1
        elif ktype in ("SOLUTION", "PATTERN"):
            append_knowledge_md(PATTERNS_FILE, desc, source, ts, domain, confidence, salience)
            routed[ktype] += 1
        elif ktype == "FAILURE":
            append_knowledge_md(FAILS_FILE, desc, source, ts, domain, confidence, salience)
            routed["FAILURE"] += 1
            # U5: Check if this failure contradicts existing lessons
            _check_lesson_contradiction(desc, source, confidence)
            # U8: Broadcast high-salience failures (Dr. Global Workspace Theory)
            if salience >= 7.0:
                try:
                    from pulse.brain.save_knowledge_ops import broadcast_critical_failure
                    broadcast_critical_failure(desc, source)
                except Exception:
                    logging.debug("Suppressed exception in bridge_routing", exc_info=True)
        elif ktype == "DECISION":
            append_decision(item)
            routed["DECISION"] += 1

        # Dimension C: Provenance tracking — record bridge routing stage
        try:
            import hashlib as _hl
            from pulse.brain.integrity.provenance import append_provenance
            fp = _hl.md5(desc[:80].lower().strip().encode("utf-8")).hexdigest()[:12]
            _target_map = {
                "LEARNING": "auto_lessons.md", "FAILURE": "auto_fails.md",
                "SOLUTION": "auto_patterns.md", "PATTERN": "auto_patterns.md",
                "DECISION": "auto_decisions.json",
            }
            append_provenance(fp, {
                "stage": "bridge_route",
                "agent": source,
                "target": _target_map.get(ktype, "unknown"),
            })
        except Exception:
            pass  # Never crash the bridge

    # Hot cache: write high-salience items for instant search (<10ms)
    try:
        from pulse.brain.memory.hot_cache import write_hot
        for item in knowledge_items:
            sal = item.get("salience", 0)
            if sal >= 4.0:
                write_hot({
                    "content": item.get("description", ""),
                    "title": item.get("description", "")[:80],
                    "type": item.get("type", "unknown"),
                    "salience": sal,
                    "confidence": item.get("confidence", 0.5),
                    "source": source,
                    "domain": item.get("domain", "general"),
                    "timestamp": item.get("timestamp", ""),
                })
    except Exception:
        pass  # Never crash the bridge

    total = sum(routed.values())
    if total > 0 and verbose:
        breakdown = ", ".join(f"{v} {k.lower()}s" for k, v in routed.items() if v > 0)
        print(f"[BRIDGE] Routed {total} knowledge items ({breakdown})")