#!/usr/bin/env python3
# Copyright (c) 2026 PULSE Contributors. Proprietary. All rights reserved.
"""
Metamemory tracking utilities — Items 92, 97, 104, 105, 107.

Source reliability, forgetting-curve stats, agent specialization,
brain health dashboard, and domain expertise decay.

Extracted from metamemory.py to keep that file under 300 lines.
"""
import logging
from datetime import datetime, timezone, timedelta

log = logging.getLogger("pulse.metamemory_tracking")


# Item 92: Source reliability tracking (Metacognition)
_SOURCE_RELIABILITY: dict = {}  # {source_name: {"hits": N, "successes": N}}


def _record_source_outcome(source: str, success: bool):
    """Record whether a search source contributed to a successful task outcome."""
    try:
        entry = _SOURCE_RELIABILITY.setdefault(source, {"hits": 0, "successes": 0})
        entry["hits"] += 1
        if success:
            entry["successes"] += 1
    except Exception:
        pass


def _get_source_reliability(source: str) -> float:
    """Return reliability score [0.0, 1.0] for a source. 1.0 until 5+ observations."""
    try:
        entry = _SOURCE_RELIABILITY.get(source, {})
        hits = entry.get("hits", 0)
        if hits < 5:
            return 1.0  # Not enough data — fail open
        return entry.get("successes", 0) / hits
    except Exception:
        return 1.0  # fail open


# Item 97: Forgetting curve visualization data (Metacognition)
def _compute_domain_decay_stats(domain: str) -> dict:
    """Track per-domain forgetting curve: avg confidence + item count for dashboard."""
    try:
        from pulse.core.brain_db import get_conn
        conn = get_conn()
        row = conn.execute(
            "SELECT AVG(confidence) as avg_conf, COUNT(*) as total FROM lessons WHERE domain = ?",
            (domain,)
        ).fetchone()
        return {
            "domain": domain,
            "avg_confidence": round(float(row[0] or 0.5), 3),
            "item_count": row[1] or 0,
        }
    except Exception:
        return {"domain": domain, "avg_confidence": 0.5, "item_count": 0}


# Item 104: Agent specialization tracking (Multi-agent)
_AGENT_SPECIALIZATION: dict = {}  # {agent: {domain: {"tasks": int, "successes": int}}}


def _record_agent_domain(agent: str, domain: str, success: bool):
    """Track which agents perform best in which domains. Build a specialization map."""
    try:
        spec = _AGENT_SPECIALIZATION.setdefault(agent, {})
        entry = spec.setdefault(domain, {"tasks": 0, "successes": 0})
        entry["tasks"] += 1
        if success:
            entry["successes"] += 1
    except Exception:
        pass


def get_agent_specialization() -> dict:
    """Return current agent specialization map with success rates."""
    result = {}
    for agent, domains in _AGENT_SPECIALIZATION.items():
        result[agent] = {}
        for domain, stats in domains.items():
            tasks = stats.get("tasks", 0)
            succs = stats.get("successes", 0)
            result[agent][domain] = {
                "tasks": tasks,
                "successes": succs,
                "success_rate": round(succs / tasks, 3) if tasks > 0 else 0.0,
            }
    return result


# Item 105: Brain health dashboard data (Monitoring)
def _compute_brain_health() -> dict:
    """Compute simple brain health score: avg confidence, active items, dying items."""
    try:
        from pulse.core.brain_db import get_conn
        conn = get_conn()
        stats = conn.execute(
            "SELECT COUNT(*) as total, AVG(confidence) as avg_conf, "
            "SUM(CASE WHEN confidence < 0.2 THEN 1 ELSE 0 END) as dying "
            "FROM lessons WHERE validity = 'valid'"
        ).fetchone()
        return {
            "total_items": stats[0] or 0,
            "avg_confidence": round(float(stats[1] or 0.5), 3),
            "dying_items": stats[2] or 0,
            "health_score": round(float(stats[1] or 0.5) * 100, 1),
        }
    except Exception:
        return {"total_items": 0, "avg_confidence": 0.5, "dying_items": 0, "health_score": 50.0}


# Item 107: Domain expertise decay — unused expertise fades (Metacognition)
def _decay_domain_expertise(get_metamemory_fn, save_metamemory_fn):
    """Downgrade competence one level for domains idle more than 14 days."""
    try:
        from pulse.core.brain_db import get_conn
        conn = get_conn()
        fourteen_days = (datetime.now(timezone.utc) - timedelta(days=14)).isoformat()
        stale = conn.execute(
            "SELECT DISTINCT domain FROM lessons WHERE domain NOT IN "
            "(SELECT DISTINCT domain FROM lessons WHERE timestamp > ?) "
            "GROUP BY domain",
            (fourteen_days,)
        ).fetchall()
        meta = get_metamemory_fn()
        levels = ["expert", "competent", "novice", "blind_spot"]
        for row in stale:
            domain = row[0]
            if domain in meta:
                current = meta[domain].get("competence_level", "novice")
                if current in levels:
                    idx = levels.index(current)
                    if idx < len(levels) - 1:
                        meta[domain]["competence_level"] = levels[idx + 1]
        save_metamemory_fn(meta)
    except Exception:
        pass


# ---------------------------------------------------------------------------
# Item 132: Source credibility weighting from task outcomes (P32)
# ---------------------------------------------------------------------------
# _SOURCE_RELIABILITY already defined above (Item 92) -- we extend it here.
# _record_source_outcome and _get_source_reliability already handle credibility.
# Wire the credibility into search scoring: call _get_source_reliability(source)
# from search_scoring.py to multiply item confidence by source credibility.

def apply_source_credibility_to_results(results: dict) -> None:
    """Multiply item confidence by source credibility based on past task outcomes.
    Sources with fewer than 5 observations return 1.0 (fail-open).
    """
    try:
        for source_name, hits in results.items():
            if not isinstance(hits, list):
                continue
            cred = _get_source_reliability(source_name)
            if cred >= 1.0:
                continue  # No downward adjustment needed
            for h in hits:
                h["confidence"] = max(0.05, h.get("confidence", 0.5) * cred)
                h["_source_credibility"] = round(cred, 3)
    except Exception:
        pass  # fail-open


# ---------------------------------------------------------------------------
# Item 135: Consolidation debt tracking (P32)
# ---------------------------------------------------------------------------
_CONSOLIDATION_DEBT: dict = {
    "inserted_since_last_run": 0,
    "last_consolidation_ts": "",
    "debt_threshold": 500,  # Warn when >500 items unprocessed
}


def _increment_consolidation_debt(n: int = 1) -> None:
    """Increment count of items inserted since last consolidation run."""
    try:
        _CONSOLIDATION_DEBT["inserted_since_last_run"] = (
            _CONSOLIDATION_DEBT.get("inserted_since_last_run", 0) + n
        )
    except Exception:
        pass


def _reset_consolidation_debt(timestamp: str = "") -> None:
    """Reset debt counter after a consolidation run completes."""
    try:
        from datetime import datetime, timezone
        _CONSOLIDATION_DEBT["inserted_since_last_run"] = 0
        _CONSOLIDATION_DEBT["last_consolidation_ts"] = (
            timestamp or datetime.now(timezone.utc).isoformat()
        )
    except Exception:
        pass


def get_consolidation_debt() -> dict:
    """Return current consolidation debt stats."""
    debt = _CONSOLIDATION_DEBT.get("inserted_since_last_run", 0)
    threshold = _CONSOLIDATION_DEBT.get("debt_threshold", 500)
    return {
        "inserted_since_last_run": debt,
        "last_consolidation_ts": _CONSOLIDATION_DEBT.get("last_consolidation_ts", ""),
        "debt_threshold": threshold,
        "high_debt": debt > threshold,
    }


# ---------------------------------------------------------------------------
# Item 138: Cognitive flexibility index (P33)
# ---------------------------------------------------------------------------
_AGENT_SESSION_DOMAINS: dict = {}  # {agent: {session_id: set(domains)}}
_TUNNEL_VISION_THRESHOLD = 1  # Only 1 unique domain = tunnel vision warning


def record_agent_session_domain(agent: str, session_id: str, domain: str) -> None:
    """Track domain diversity per agent per session for flexibility scoring."""
    try:
        sessions = _AGENT_SESSION_DOMAINS.setdefault(agent, {})
        domain_set = sessions.setdefault(session_id, set())
        domain_set.add(domain)
    except Exception:
        pass  # fail-open


def get_cognitive_flexibility_index(agent: str, session_id: str) -> dict:
    """Return cognitive flexibility score for an agent in a session.
    High flexibility (many domains) = good.
    Low flexibility (1 domain) = tunnel vision warning.
    """
    try:
        sessions = _AGENT_SESSION_DOMAINS.get(agent, {})
        domains = sessions.get(session_id, set())
        unique_count = len(domains)
        flexibility_score = min(1.0, unique_count / 5.0)  # Normalised to 5 domains = 1.0
        tunnel_vision = unique_count <= _TUNNEL_VISION_THRESHOLD
        return {
            "agent": agent,
            "session_id": session_id,
            "unique_domains": unique_count,
            "flexibility_score": round(flexibility_score, 3),
            "tunnel_vision_warning": tunnel_vision,
            "domains": list(domains),
        }
    except Exception:
        return {
            "agent": agent,
            "session_id": session_id,
            "unique_domains": 0,
            "flexibility_score": 0.0,
            "tunnel_vision_warning": True,
            "domains": [],
        }
increment_consolidation_debt = _increment_consolidation_debt
reset_consolidation_debt = _reset_consolidation_debt
