#!/usr/bin/env python3
# Copyright (c) 2026 PULSE Contributors. Proprietary. All rights reserved.
"""Metamemory P36 extensions — Items 152, 155.

Kept separate from metamemory.py / metamemory_p34.py to respect 300-line ceiling.
  - Item 152: Confidence distribution monitor
  - Item 155: Cognitive load balancing across agents
"""
import logging
from typing import Dict, Any, List

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

# ---------------------------------------------------------------------------
# Item 152: Confidence distribution monitor (Monitoring)
# ---------------------------------------------------------------------------
_UNCERTAIN_THRESHOLD = 0.3   # items below this are "uncertain"
_UNCERTAIN_MAJORITY = 0.50   # alert when >50% of items are below threshold


def get_confidence_histogram() -> Dict[str, Any]:
    """Compute histogram of confidence values across all brain items.

    Buckets: [0-0.2), [0.2-0.4), [0.4-0.6), [0.6-0.8), [0.8-1.0].
    Returns histogram + alert if >50% items are below 0.3.
    Fails open with empty result on any error.
    """
    result: Dict[str, Any] = {
        "buckets": {},
        "total": 0,
        "uncertain_pct": 0.0,
        "alert": None,
    }
    try:
        from pulse.core.brain_db import get_conn
        conn = get_conn()
        buckets = {"0.0-0.2": 0, "0.2-0.4": 0, "0.4-0.6": 0, "0.6-0.8": 0, "0.8-1.0": 0}
        total = 0
        below_threshold = 0
        for tbl in ("lessons", "failures", "patterns", "facts"):
            try:
                rows = conn.execute(
                    f"SELECT confidence FROM {tbl} WHERE validity = 'valid'"
                ).fetchall()
                for row in rows:
                    try:
                        c = float(row[0] or 0.0)
                        total += 1
                        if c < _UNCERTAIN_THRESHOLD:
                            below_threshold += 1
                        if c < 0.2:
                            buckets["0.0-0.2"] += 1
                        elif c < 0.4:
                            buckets["0.2-0.4"] += 1
                        elif c < 0.6:
                            buckets["0.4-0.6"] += 1
                        elif c < 0.8:
                            buckets["0.6-0.8"] += 1
                        else:
                            buckets["0.8-1.0"] += 1
                    except Exception:
                        pass
            except Exception:
                pass
        uncertain_pct = round(below_threshold / total, 3) if total > 0 else 0.0
        alert = None
        if total > 0 and uncertain_pct > _UNCERTAIN_MAJORITY:
            alert = (
                f"!! UNCERTAIN BRAIN: {uncertain_pct*100:.1f}% of items below "
                f"confidence {_UNCERTAIN_THRESHOLD} — brain is mostly uncertain"
            )
        result.update({
            "buckets": buckets,
            "total": total,
            "uncertain_pct": uncertain_pct,
            "alert": alert,
        })
    except Exception as exc:
        log.debug("get_confidence_histogram failed: %s", exc)
    return result


# ---------------------------------------------------------------------------
# Item 155: Cognitive load balancing across agents (Multi-agent)
# ---------------------------------------------------------------------------
_AGENT_OVERLOAD_THRESHOLD = 10  # alert when more than 10 agents active


def get_cognitive_load_status() -> Dict[str, Any]:
    """Check active agent count from interoception signals.

    Returns: {active_agents, overloaded, threshold, recommendation}
    Fails open (returns 0 agents, not overloaded) on any error.
    """
    result: Dict[str, Any] = {
        "active_agents": 0,
        "overloaded": False,
        "threshold": _AGENT_OVERLOAD_THRESHOLD,
        "recommendation": None,
    }
    try:
        from pulse.core.interoception import _get_signals
        signals = _get_signals()
        agent_load = float(signals.get("agent_load", 0))
        overloaded = agent_load > _AGENT_OVERLOAD_THRESHOLD
        result.update({
            "active_agents": int(agent_load),
            "overloaded": overloaded,
            "recommendation": (
                f"!! LOAD SHEDDING RECOMMENDED: {int(agent_load)} active agents "
                f"exceeds threshold {_AGENT_OVERLOAD_THRESHOLD}. Defer non-critical tasks."
                if overloaded else None
            ),
        })
    except Exception as exc:
        log.debug("get_cognitive_load_status failed: %s", exc)
    return result


# ---------------------------------------------------------------------------
# Item 159: Adaptive rehearsal scheduling (Memory)
# ---------------------------------------------------------------------------
_REHEARSAL_LOW = 0.2
_REHEARSAL_HIGH = 0.3


def get_rehearsal_candidates() -> Dict[str, Any]:
    """Find items close to the forgetting threshold (confidence 0.2-0.3).

    Returns:
        {candidates_by_domain: {domain: count}, total, warning}
    Fails open with empty result on any error.
    """
    result: Dict[str, Any] = {
        "candidates_by_domain": {},
        "total": 0,
        "warning": None,
    }
    try:
        from pulse.core.brain_db import get_conn
        conn = get_conn()
        domain_counts: Dict[str, int] = {}
        total = 0
        for tbl in ("lessons", "failures", "patterns", "facts"):
            try:
                rows = conn.execute(
                    f"SELECT domain, COUNT(*) FROM {tbl} "
                    f"WHERE validity = 'valid' AND confidence >= ? AND confidence <= ?  "
                    f"GROUP BY domain",
                    (_REHEARSAL_LOW, _REHEARSAL_HIGH),
                ).fetchall()
                for row in rows:
                    d = str(row[0] or "general")
                    n = int(row[1] or 0)
                    domain_counts[d] = domain_counts.get(d, 0) + n
                    total += n
            except Exception:
                pass

        warning = None
        if total > 0:
            top_domain = max(domain_counts, key=domain_counts.get)
            warning = (
                f"!! REHEARSAL NEEDED: {total} items approaching decay threshold "
                f"in domain '{top_domain}' and {len(domain_counts)-1} other domains"
            )

        result.update({
            "candidates_by_domain": domain_counts,
            "total": total,
            "warning": warning,
        })
    except Exception as exc:
        log.debug("get_rehearsal_candidates failed: %s", exc)
    return result


# ---------------------------------------------------------------------------
# Item 171: Brain age metric (Monitoring)
# ---------------------------------------------------------------------------

def get_brain_age_metric() -> Dict[str, Any]:
    """Compute brain age = timestamp range from oldest to newest valid item.

    Returns:
        {oldest_ts, newest_ts, age_days, warning}
    Fails open with zeros on any error.
    """
    result: Dict[str, Any] = {
        "oldest_ts": None,
        "newest_ts": None,
        "age_days": 0.0,
        "warning": None,
    }
    try:
        from pulse.core.brain_db import get_conn
        from datetime import datetime, timezone
        conn = get_conn()
        oldest = None
        newest = None
        for tbl in ("lessons", "failures", "patterns", "facts"):
            try:
                row = conn.execute(
                    f"SELECT MIN(timestamp), MAX(timestamp) FROM {tbl} "
                    f"WHERE validity = 'valid'"
                ).fetchone()
                if row:
                    mn, mx = row[0], row[1]
                    if mn and (oldest is None or mn < oldest):
                        oldest = mn
                    if mx and (newest is None or mx > newest):
                        newest = mx
            except Exception:
                pass

        if oldest and newest:
            try:
                old_dt = datetime.fromisoformat(oldest.replace("Z", "+00:00"))
                new_dt = datetime.fromisoformat(newest.replace("Z", "+00:00"))
                if old_dt.tzinfo is None:
                    old_dt = old_dt.replace(tzinfo=timezone.utc)
                if new_dt.tzinfo is None:
                    new_dt = new_dt.replace(tzinfo=timezone.utc)
                age_days = round((new_dt - old_dt).total_seconds() / 86400, 1)
                result.update({
                    "oldest_ts": oldest,
                    "newest_ts": newest,
                    "age_days": age_days,
                })
            except Exception:
                pass

    except Exception as exc:
        log.debug("get_brain_age_metric failed: %s", exc)
    return result
