#!/usr/bin/env python3
# Copyright (c) 2026 PULSE Contributors. Proprietary. All rights reserved.
"""Stages 6/8/10/11/12: Pruning, truth verification, metamemory, homeostasis, deep consolidation."""
from collections import defaultdict
from datetime import datetime, timezone, timedelta

from pulse.core.brain_utils import (
    LESSONS_DIR, PATTERNS_DIR, FAILS_DIR,
    LESSONS_JSONL, FAILS_JSONL, PATTERNS_JSONL,
    read_jsonl_entries, now_iso as _now_iso,
)
from .stage_io import _MD_TO_JSONL

def stage_hierarchical_prune(verbose=False):
    """Stage 6: Miller's Law — max 5 items per section, 200 entries per file."""
    from pulse.core.brain_utils import load_config
    from pulse.admin.brain_ops.pulse_prune import prune_file, BRAIN_FILES
    from pulse.brain.save_writers import rewrite_jsonl
    config = load_config()
    max_chunk = config.get("validation", {}).get("max_chunk_size", 5)

    for bf in BRAIN_FILES:
        try:
            prune_file(bf)
        except Exception as e:
            if verbose:
                print(f"  [PRUNE-L1] Error pruning {bf.name}: {e}")

    trimmed_total = 0
    domain_cap = max_chunk * 40
    _table_map = {
        "auto_lessons.jsonl": "lessons",
        "auto_fails.jsonl": "failures",
        "auto_patterns.jsonl": "patterns",
    }
    for md_file in [
        LESSONS_DIR / "auto_lessons.md",
        PATTERNS_DIR / "auto_patterns.md",
        FAILS_DIR / "auto_fails.md",
    ]:
        jsonl_path = _MD_TO_JSONL.get(md_file)
        if not jsonl_path:
            continue
        rows = read_jsonl_entries(jsonl_path)
        if not rows:
            continue
        by_domain = defaultdict(list)
        for r in rows:
            by_domain[r.get("domain", "general")].append(r)
        surviving = []
        file_trimmed = 0
        fps_to_delete = []
        for domain, items in by_domain.items():
            items.sort(key=lambda x: x.get("salience", 0) * x.get("confidence", 0.5), reverse=True)
            if len(items) > domain_cap:
                over_cap = items[domain_cap:]
                file_trimmed += len(over_cap)
                fps_to_delete.extend(r.get("fingerprint") for r in over_cap if r.get("fingerprint"))
                items = items[:domain_cap]
            surviving.extend(items)
        if file_trimmed > 0:
            trimmed_total += file_trimmed
            _sqlite_ok = False
            try:
                from pulse.core.brain_db import get_conn
                table = _table_map.get(jsonl_path.name)
                if table and fps_to_delete:
                    conn = get_conn()
                    with conn:
                        placeholders = ",".join("?" * len(fps_to_delete))
                        conn.execute(
                            f"DELETE FROM knowledge_fts WHERE item_id IN "
                            f"(SELECT id FROM {table} WHERE fingerprint IN ({placeholders}))",
                            fps_to_delete,
                        )
                        conn.execute(
                            f"DELETE FROM {table} WHERE fingerprint IN ({placeholders})",
                            fps_to_delete,
                        )
                    _sqlite_ok = True
            except Exception:
                _sqlite_ok = False
            if not _sqlite_ok:
                rewrite_jsonl(jsonl_path, surviving)
    if verbose:
        print(f"  [PRUNE] Hierarchical: {trimmed_total} over-limit items trimmed")
    return {"status": "ok", "trimmed": trimmed_total}

def stage_truth_verification(verbose=False):
    """Stage 8: Dimension A — scan for contradictions and apply penalties."""
    from pulse.admin.audit.truth_verifier import scan_contradictions, apply_penalties
    scan = scan_contradictions(verbose=verbose)
    penalized = apply_penalties(verbose=verbose)
    return {"status": "ok", "new_contradictions": scan["new"],
            "pending": scan["pending"], "penalized": penalized}

def stage_10_metamemory(verbose=False):
    """Stage 10: Compute domain-level confidence map for self-modeling."""
    try:
        from pulse.brain.metamemory import compute_metamemory
        metamap = compute_metamemory()
        gaps = sum(1 for d in metamap.values() if d.get("gap_flag"))
        if verbose:
            print(f"  [METAMEMORY] {len(metamap)} domains, {gaps} flagged as gaps.")
        return {"status": "ok", "domains": len(metamap), "gaps": gaps}
    except ImportError:
        if verbose:
            print("  [METAMEMORY] pulse.brain.metamemory not found")
        return {"status": "skipped"}
    except Exception as e:
        if verbose:
            print(f"  [METAMEMORY] Error: {e}")
        return {"error": str(e)}

def stage_synaptic_homeostasis(verbose=False):
    """Stage 11: Normalize salience to prevent inflation (Tononi & Cirelli 2014)."""
    from pulse.brain.save_writers import rewrite_jsonl
    SALIENCE_CEILING = 4.0
    SALIENCE_TARGET = 3.0
    PROTECTION_HOURS = 6
    cutoff = datetime.now(timezone.utc) - timedelta(hours=PROTECTION_HOURS)
    total_downscaled = 0
    _table_map = {
        "auto_lessons.jsonl": "lessons",
        "auto_fails.jsonl": "failures",
        "auto_patterns.jsonl": "patterns",
    }
    for jsonl_path in [LESSONS_JSONL, FAILS_JSONL, PATTERNS_JSONL]:
        if not jsonl_path.exists():
            continue
        rows = read_jsonl_entries(jsonl_path)
        if not rows:
            continue
        saliences = [r.get("salience", 2.0) for r in rows]
        mean_sal = sum(saliences) / len(saliences)
        if mean_sal <= SALIENCE_CEILING:
            if verbose:
                print(f"  [HOMEOSTASIS] {jsonl_path.name}: mean={mean_sal:.2f} (OK)")
            continue
        scale = SALIENCE_TARGET / mean_sal
        downscaled = 0
        for r in rows:
            ts = r.get("timestamp", "")
            if ts:
                try:
                    item_dt = datetime.fromisoformat(ts.replace("Z", "+00:00"))
                    if item_dt.tzinfo is None:
                        item_dt = item_dt.replace(tzinfo=timezone.utc)
                    if item_dt > cutoff:
                        continue
                except (ValueError, TypeError):
                    pass
            old_sal = r.get("salience", 2.0)
            new_sal = round(max(0.5, old_sal * scale), 2)
            if new_sal != old_sal:
                r["salience"] = new_sal
                downscaled += 1
        if downscaled > 0:
            _sqlite_ok = False
            try:
                from pulse.core.brain_db import get_conn
                table = _table_map.get(jsonl_path.name)
                if table:
                    conn = get_conn()
                    cutoff_iso = cutoff.isoformat()
                    with conn:
                        conn.execute(
                            f"UPDATE {table} SET "
                            f"salience = MAX(0.5, ROUND(salience * ?, 2)), "
                            f"updated_at = strftime('%Y-%m-%dT%H:%M:%SZ','now') "
                            f"WHERE (timestamp <= ? OR timestamp = '') "
                            f"AND validity = 'valid'",
                            (scale, cutoff_iso),
                        )
                    _sqlite_ok = True
            except Exception:
                _sqlite_ok = False
            if not _sqlite_ok:
                rewrite_jsonl(jsonl_path, rows)
            total_downscaled += downscaled
        if verbose:
            print(f"  [HOMEOSTASIS] {jsonl_path.name}: mean={mean_sal:.2f} -> ~{SALIENCE_TARGET:.1f}, "
                  f"downscaled {downscaled}/{len(rows)} (scale={scale:.3f})")
    if verbose and total_downscaled == 0:
        print("  [HOMEOSTASIS] All files within normal range")
    try:
        from pulse.core.brain_db import get_conn
        conn = get_conn()
        seven_days_ago = (datetime.now(timezone.utc) - timedelta(days=7)).isoformat()
        contracted = 0
        for table in ["lessons", "failures", "patterns"]:
            res = conn.execute(
                f"UPDATE {table} SET salience = MAX(0.1, salience * 0.9) "
                f"WHERE domain IN ("
                f"  SELECT DISTINCT domain FROM {table} "
                f"  GROUP BY domain HAVING MAX(timestamp) < ?"
                f") AND validity != 'permanent'",
                (seven_days_ago,)
            )
            contracted += res.rowcount
        conn.commit()
        if verbose and contracted > 0:
            print(f"  [FOOTPRINT] Contracted {contracted} items in dormant domains")
    except Exception:
        pass
    return {"status": "ok", "downscaled": total_downscaled}

def stage_recency_bonus(verbose=False):
    """Stage N: Proactive interference recency bonus (P18-72). Newer same-domain items get confidence + 0.05."""
    try:
        from pulse.core.brain_db import get_conn
        from datetime import datetime, timezone, timedelta
        conn = get_conn()
        one_day_ago = (datetime.now(timezone.utc) - timedelta(days=1)).isoformat()
        boosted = 0
        for table in ["lessons", "failures", "patterns"]:
            res = conn.execute(
                f"UPDATE {table} SET confidence = MIN(1.0, confidence + 0.05) "
                f"WHERE timestamp > ? AND confidence < 0.9",
                (one_day_ago,)
            )
            boosted += res.rowcount
        conn.commit()
        if verbose:
            print(f"  [RECENCY_BONUS] Boosted {boosted} recent items (+0.05 confidence)")
        return {"status": "ok", "boosted": boosted}
    except Exception as e:
        if verbose:
            print(f"  [RECENCY_BONUS] Error: {e}")
        return {"status": "error", "error": str(e)}

def stage_deep_consolidation(verbose=False):
    """Stage 12: Promote permanent knowledge, archive dead knowledge."""
    try:
        from pulse.core.brain_db import get_conn
        from pulse.core import event_bus
        conn = get_conn()
        now = datetime.now(timezone.utc)
        fourteen_days_ago = (now - timedelta(days=14)).isoformat()
        thirty_days_ago = (now - timedelta(days=30)).isoformat()
        stats = {"permanent": 0, "archived": 0}
        tables = ["lessons", "failures", "patterns", "decisions", "facts"]
        with conn:
            for table in tables:
                res = conn.execute(f"""
                    UPDATE {table} SET validity = 'permanent'
                    WHERE validity = 'valid' AND confidence >= 0.7
                      AND access_count >= 5 AND timestamp <= ?
                """, (fourteen_days_ago,))
                stats["permanent"] += res.rowcount
                res = conn.execute(f"""
                    UPDATE {table} SET validity = 'archived'
                    WHERE validity = 'valid' AND confidence < 0.3
                      AND coalesce(last_accessed, timestamp) <= ?
                """, (thirty_days_ago,))
                stats["archived"] += res.rowcount
        event_bus.publish("consolidation.deep.complete", "deep_consolidation", stats)
        if verbose:
            print(f"  [DEEP] Promoted {stats['permanent']} to permanent, archived {stats['archived']}")
        return {"status": "ok", **stats}
    except ImportError:
        if verbose:
            print("  [DEEP] SQLite DAL not available")
        return {"status": "skipped"}
    except Exception as e:
        if verbose:
            print(f"  [DEEP] Error: {e}")
        return {"status": "error", "error": str(e)}

# Item 116: Memory consolidation priority queue — re-exported from dedicated module
try:
    from pulse.daemons.consolidation.stages.stage_priority_queue import (
        stage_consolidation_priority_queue,
        build_consolidation_priority_queue,
    )
except Exception:
    def stage_consolidation_priority_queue(items=None, verbose=False):
        return {"status": "skipped", "total": 0, "ordered_items": []}
    def build_consolidation_priority_queue(items):
        return list(items) if items else []

def domain_footprint_contraction(conn, verbose=False) -> int:
    """Item 36: Domain footprint contraction — salience * 0.9 for items >7 days, low access. Skips permanent."""
    updated = 0
    try:
        from datetime import datetime, timezone, timedelta
        cutoff = (datetime.now(timezone.utc) - timedelta(days=7)).isoformat()
        for table in ("lessons", "failures", "patterns"):
            cur = conn.execute(
                f"UPDATE {table} SET salience = salience * 0.9 "
                "WHERE timestamp < ? AND validity != 'permanent' "
                "AND access_count < 3",
                (cutoff,)
            )
            updated += cur.rowcount
        conn.commit()
    except Exception:
        pass
    return updated
