#!/usr/bin/env python3
# Copyright (c) 2026 PULSE Contributors. Proprietary. All rights reserved.
"""
Reconsolidation query API — domain plasticity, health metrics, and re-exports.

Phase 4: Critical Periods (Kleim & Jones 2008)
Young domains are plastic (easy to update), mature domains resist change.
Sigmoid: plasticity = 1.0 / (1.0 + exp(0.03 * (age_days - 90)))
Age <30d → plasticity ~0.86, Age 90d → 0.50, Age >180d → ~0.06

Split from the original reconsolidation.py for Law 7 compliance.
CRUD operations (mark_labile, reinforce, contradict, tombstone) live in
reconsolidation_labile.py. This module exposes the read-only query API
plus re-exports the full public surface so all existing callers keep working
with a single import path.
"""
import math
from datetime import datetime, timezone

from pulse.core.brain_db import get_conn

CRITICAL_PERIOD_MIDPOINT = 90    # Days at which plasticity = 0.5
CRITICAL_PERIOD_STEEPNESS = 0.03  # How sharp the transition is
MIN_PLASTICITY = 0.1              # Floor — even mature domains can change

# Item 125: Memory reconsolidation window tracking (Memory -- P29)
_LABILE_COUNT_WARN_THRESHOLD = 20


def get_domain_plasticity(domain: str) -> float:
    """Compute plasticity for a domain based on its age (Critical Periods).

    Young domains (<30 days) → high plasticity (~0.86), easy to update.
    Mature domains (>180 days) → low plasticity (~0.06), resist change.
    Returns float in [MIN_PLASTICITY, 1.0].
    """
    if not domain:
        return 1.0  # Unknown domain: fully plastic
    try:
        conn = get_conn()
        # Find earliest item timestamp in this domain
        earliest = None
        for table in ("lessons", "failures", "patterns"):
            row = conn.execute(
                f"SELECT MIN(timestamp) as first_ts FROM {table} "
                f"WHERE domain = ? AND validity = 'valid'", (domain,)
            ).fetchone()
            if row and row["first_ts"]:
                if earliest is None or row["first_ts"] < earliest:
                    earliest = row["first_ts"]
        if not earliest:
            return 1.0  # New domain: fully plastic
        first_dt = datetime.fromisoformat(earliest.replace("Z", "+00:00"))
        age_days = (datetime.now(timezone.utc) - first_dt).total_seconds() / 86400
        # Sigmoid: drops from ~1.0 to ~0.0 centered at MIDPOINT
        plasticity = 1.0 / (1.0 + math.exp(CRITICAL_PERIOD_STEEPNESS * (age_days - CRITICAL_PERIOD_MIDPOINT)))
        return max(MIN_PLASTICITY, round(plasticity, 3))
    except Exception:
        return 1.0  # On error: fully plastic (safe default)


def _get_labile_count() -> int:
    """Count items in labile (reconsolidation) window. Large counts warn instability."""
    try:
        from .reconsolidation_labile import _sweep_expired, _labile_state, _load_labile, _lock
        _sweep_expired()
        with _lock:
            if not _labile_state:
                _load_labile()
            return len(_labile_state)
    except Exception:
        return 0  # fail-open


def _get_reconsolidation_health() -> dict:
    """Return reconsolidation window health metrics.

    Used by brain_health watchdog and dashboard.
    Returns a dict with labile_count and a warning if count > threshold.
    """
    try:
        count = _get_labile_count()
        warning = None
        if count > _LABILE_COUNT_WARN_THRESHOLD:
            warning = (
                f"Memory instability warning: {count} items in labile window "
                f"(threshold={_LABILE_COUNT_WARN_THRESHOLD}). "
                "Too many simultaneous reconsolidations may corrupt knowledge."
            )
        return {
            "labile_count": count,
            "threshold": _LABILE_COUNT_WARN_THRESHOLD,
            "stable": count <= _LABILE_COUNT_WARN_THRESHOLD,
            "warning": warning,
        }
    except Exception:
        return {"labile_count": 0, "threshold": _LABILE_COUNT_WARN_THRESHOLD,
                "stable": True, "warning": None}


# Public alias (backward compat)
get_reconsolidation_health = _get_reconsolidation_health

# ---------------------------------------------------------------------------
# Re-export full CRUD surface so all existing callers can keep using
# `from pulse.brain.reconsolidation import mark_labile, reinforce, ...`
# without any import path changes.
# ---------------------------------------------------------------------------
from .reconsolidation_labile import (  # noqa: E402, F401
    mark_labile,
    reinforce,
    contradict,
    tombstone_item,
    _sweep_expired,
    _resolve_superseded_chain,
    LABILE_ITEMS_FILE,
    TTL_SECONDS,
    _VALID_TABLES,
    _lock,
    _labile_state,
)
