#!/usr/bin/env python3
# Copyright (c) 2026 PULSE Contributors. Proprietary. All rights reserved.
"""Metamemory extensions — P34/P35 neuroscience items 142, 147, 149, 150.

Kept separate from metamemory.py (291 lines) to respect the 300-line ceiling.

  - Item 142: Memory trace decay monitoring
  - Item 147: Emotional valence tracking per domain
  - Item 149: Agent collaboration history
  - Item 150: Brain entropy metric
"""
import logging
import math
from datetime import datetime, timezone, timedelta
from typing import Dict, Any

log = logging.getLogger("pulse.brain.metamemory_p34")


# ---------------------------------------------------------------------------
# Item 142: Memory trace decay monitoring (Monitoring)
# ---------------------------------------------------------------------------

def get_decay_rate_24h() -> Dict[str, Any]:
    """Count items that have decayed below 0.2 confidence in the last 24h.

    Rising decay rate = brain is losing knowledge faster than gaining it.
    Returns a dict with decay_count, total_valid, decay_rate_pct, and warning flag.
    Fails open (returns zeros) on any DB error.
    """
    result: Dict[str, Any] = {
        "decay_count": 0,
        "total_valid": 0,
        "decay_rate_pct": 0.0,
        "warning": False,
    }
    try:
        from pulse.core.brain_db import get_conn
        conn = get_conn()
        cutoff_24h = (datetime.now(timezone.utc) - timedelta(hours=24)).isoformat()
        decay_count = 0
        total_valid = 0
        for tbl in ("lessons", "failures", "patterns", "facts"):
            try:
                t_row = conn.execute(
                    f"SELECT COUNT(*) FROM {tbl} WHERE validity = 'valid'"
                ).fetchone()
                d_row = conn.execute(
                    f"SELECT COUNT(*) FROM {tbl} "
                    f"WHERE validity = 'valid' AND confidence < 0.2 "
                    f"AND timestamp >= ?",
                    (cutoff_24h,),
                ).fetchone()
                total_valid += int(t_row[0] or 0)
                decay_count += int(d_row[0] or 0)
            except Exception:
                pass  # per-table fail open

        decay_rate = round(decay_count / total_valid * 100, 2) if total_valid > 0 else 0.0
        result.update({
            "decay_count": decay_count,
            "total_valid": total_valid,
            "decay_rate_pct": decay_rate,
            # Warning if >5% of valid items decayed in 24h
            "warning": decay_rate > 5.0,
        })
    except Exception as exc:
        log.debug("get_decay_rate_24h failed: %s", exc)
    return result


# ---------------------------------------------------------------------------
# Item 147: Emotional valence tracking per domain (Memory)
# ---------------------------------------------------------------------------
# In-memory accumulator: {domain: {"sum_intensity": float, "count": int}}
_DOMAIN_VALENCE: Dict[str, Dict[str, float]] = {}

# High-valence keywords (each word adds 1 to intensity score; max 5 per text)
_VALENCE_WORDS = frozenset({
    "critical", "urgent", "crash", "failure", "fatal", "broken", "loss",
    "danger", "severe", "important", "crucial", "panic", "emergency",
    "excellent", "success", "amazing", "perfect", "great",
})
_HIGH_VALENCE_THRESHOLD = 3.0  # avg intensity above this = high valence domain


def _compute_text_intensity(text: str) -> float:
    """Return emotional intensity score [0, 5] for a text fragment."""
    try:
        import re
        words = re.findall(r"\b[a-z]{4,}\b", text.lower())
        hits = sum(1 for w in words if w in _VALENCE_WORDS)
        return float(min(5, hits))
    except Exception:
        return 0.0


def _record_domain_valence(domain: str, text: str) -> None:
    """Record the emotional intensity of a knowledge item for its domain.

    Call this when saving/extracting knowledge items to track per-domain
    emotional valence over time.
    """
    try:
        if not domain or not text:
            return
        intensity = _compute_text_intensity(text)
        entry = _DOMAIN_VALENCE.setdefault(domain, {"sum_intensity": 0.0, "count": 0})
        entry["sum_intensity"] += intensity
        entry["count"] += 1
    except Exception:
        pass  # fail open


def get_domain_valence() -> Dict[str, Dict[str, Any]]:
    """Return per-domain valence stats with high_valence flag.

    Returns: {domain: {avg_intensity, count, high_valence}}
    """
    result: Dict[str, Dict[str, Any]] = {}
    try:
        for domain, stats in _DOMAIN_VALENCE.items():
            count = stats.get("count", 0)
            avg = stats["sum_intensity"] / count if count > 0 else 0.0
            result[domain] = {
                "avg_intensity": round(avg, 3),
                "count": count,
                "high_valence": avg >= _HIGH_VALENCE_THRESHOLD,
            }
    except Exception as exc:
        log.debug("get_domain_valence failed: %s", exc)
    return result


# ---------------------------------------------------------------------------
# Item 149: Agent collaboration history (Multi-agent)
# ---------------------------------------------------------------------------
# {(agent_a, agent_b): {domain: {"co_claims": int, "co_successes": int}}}
_COLLAB_HISTORY: Dict[str, Dict[str, Dict[str, int]]] = {}


def record_agent_collaboration(agent_a: str, agent_b: str,
                               domain: str, both_succeeded: bool) -> None:
    """Record a co-task event between two agents in a domain.

    A co-task = both agents claimed tasks in the same domain around the same time.
    """
    try:
        if not agent_a or not agent_b or agent_a == agent_b:
            return
        key = "|".join(sorted([agent_a, agent_b]))
        pair = _COLLAB_HISTORY.setdefault(key, {})
        entry = pair.setdefault(domain, {"co_claims": 0, "co_successes": 0})
        entry["co_claims"] += 1
        if both_succeeded:
            entry["co_successes"] += 1
    except Exception:
        pass  # fail open


def get_collaboration_recommendations() -> list:
    """Return agent pair recommendations sorted by co-success rate.

    Returns: list of {"agents": [a, b], "domain": str, "success_rate": float}
    sorted by success_rate descending (best partners first).
    """
    recs: list = []
    try:
        for pair_key, domains in _COLLAB_HISTORY.items():
            agents = pair_key.split("|")
            for domain, stats in domains.items():
                claims = stats.get("co_claims", 0)
                succs = stats.get("co_successes", 0)
                if claims >= 2:  # need at least 2 co-task events
                    rate = round(succs / claims, 3)
                    recs.append({
                        "agents": agents,
                        "domain": domain,
                        "co_claims": claims,
                        "co_successes": succs,
                        "success_rate": rate,
                    })
        recs.sort(key=lambda x: x["success_rate"], reverse=True)
    except Exception as exc:
        log.debug("get_collaboration_recommendations failed: %s", exc)
    return recs


# ---------------------------------------------------------------------------
# Item 150: Brain entropy metric (Monitoring)
# ---------------------------------------------------------------------------

def compute_brain_entropy() -> Dict[str, Any]:
    """Compute Shannon entropy of knowledge distribution across domains.

    High entropy = diverse brain. Low entropy = narrow specialization.
    Returns: {entropy, domain_count, top_domain, distribution, timestamp}
    Fails open with entropy=0.0 on any error.
    """
    result: Dict[str, Any] = {
        "entropy": 0.0,
        "domain_count": 0,
        "top_domain": None,
        "distribution": {},
        "timestamp": datetime.now(timezone.utc).isoformat(),
    }
    try:
        from pulse.core.brain_db import get_conn
        conn = get_conn()
        domain_counts: Dict[str, int] = {}
        for tbl in ("lessons", "failures", "patterns", "facts"):
            try:
                rows = conn.execute(
                    f"SELECT domain, COUNT(*) as cnt FROM {tbl} "
                    f"WHERE validity = 'valid' AND domain IS NOT NULL AND domain != '' "
                    f"GROUP BY domain"
                ).fetchall()
                for row in rows:
                    try:
                        d = str(row[0])
                        n = int(row[1])
                        domain_counts[d] = domain_counts.get(d, 0) + n
                    except Exception:
                        pass
            except Exception:
                pass

        if not domain_counts:
            return result

        total = sum(domain_counts.values())
        if total == 0:
            return result

        # Shannon entropy: H = -sum(p * log2(p))
        entropy = 0.0
        distribution: Dict[str, float] = {}
        for domain, count in domain_counts.items():
            p = count / total
            distribution[domain] = round(p, 4)
            if p > 0:
                entropy -= p * math.log2(p)

        top_domain = max(domain_counts, key=domain_counts.get)
        result.update({
            "entropy": round(entropy, 4),
            "domain_count": len(domain_counts),
            "top_domain": top_domain,
            "distribution": distribution,
        })
    except Exception as exc:
        log.debug("compute_brain_entropy failed: %s", exc)
    return result
record_domain_valence = _record_domain_valence
