#!/usr/bin/env python3
"""
Micro-Consolidation Daemon — 60-second lightweight cycle.

Unlike the full 22-stage consolidation (6h cycle), this runs every 60 seconds
and handles only fast operations on NEW items:
1. Fingerprint dedup on items written in the last 2 minutes
2. Pattern promotion for high-confidence failures
3. Contradiction detection between new lessons and existing failures

This is the "sharp wave ripple" equivalent — opportunistic replay during
idle periods that keeps the brain clean without waiting for deep sleep.

Expert 3 (Consolidation) P0-1 recommendation.
"""
import logging
import time
import hashlib
from datetime import datetime, timezone, timedelta

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

CYCLE_SECONDS = 60
LOOKBACK_SECONDS = 120  # Process items from last 2 minutes


def run_micro_cycle(verbose: bool = False) -> dict:
    """Run one micro-consolidation cycle. Returns stats."""
    stats = {"deduped": 0, "promoted": 0, "contradictions": 0, "timestamp": ""}
    try:
        from pulse.core.brain_db import get_conn
        conn = get_conn()
    except Exception as e:
        log.debug("Micro-consolidation: no DB connection: %s", e)
        return stats

    cutoff = (datetime.now(timezone.utc) - timedelta(seconds=LOOKBACK_SECONDS)).isoformat()
    stats["timestamp"] = datetime.now(timezone.utc).isoformat()

    # Stage 1: Dedup recent items by title similarity
    stats["deduped"] = _dedup_recent(conn, cutoff)

    # Stage 2: Promote high-confidence failures to anti-patterns
    stats["promoted"] = _promote_failures(conn, cutoff)

    # Stage 3: Detect contradictions between new lessons and failures
    stats["contradictions"] = _detect_contradictions(conn, cutoff)

    if verbose and any(v for k, v in stats.items() if k != "timestamp"):
        log.info("Micro-cycle: dedup=%d promoted=%d contradictions=%d",
                 stats["deduped"], stats["promoted"], stats["contradictions"])
    return stats


def _dedup_recent(conn, cutoff: str) -> int:
    """Remove duplicate recent items with identical titles."""
    deduped = 0
    for table in ["lessons", "failures", "patterns"]:
        try:
            # Find titles that appear more than once in recent items
            dupes = conn.execute(
                f"SELECT title, COUNT(*) as cnt, MIN(id) as keep_id "
                f"FROM {table} WHERE timestamp > ? AND title IS NOT NULL "
                f"GROUP BY title HAVING cnt > 1",
                (cutoff,)
            ).fetchall()
            for d in dupes:
                title, cnt, keep_id = d[0], d[1], d[2]
                conn.execute(
                    f"DELETE FROM {table} WHERE title = ? AND id != ? AND timestamp > ?",
                    (title, keep_id, cutoff)
                )
                deduped += cnt - 1
        except Exception as e:
            log.debug("Dedup on %s failed: %s", table, e)
    if deduped:
        conn.commit()
    return deduped


def _promote_failures(conn, cutoff: str) -> int:
    """Promote high-confidence recent failures to anti-patterns."""
    promoted = 0
    try:
        failures = conn.execute(
            "SELECT id, title, content, confidence, domain FROM failures "
            "WHERE timestamp > ? AND confidence >= 0.7 AND validity = 'valid'",
            (cutoff,)
        ).fetchall()
        for f in failures:
            fid, title, content, conf, domain = f[0], f[1], f[2], f[3], f[4]
            desc = title or (content[:200] if content else "")
            if not desc or len(desc) < 15:
                continue
            # Check if anti-pattern already exists
            existing = conn.execute(
                "SELECT id FROM anti_patterns WHERE description = ? LIMIT 1",
                (desc,)
            ).fetchone()
            if existing:
                continue
            # Create anti-pattern
            ap_id = f"AP-micro-{hashlib.md5(desc.encode()).hexdigest()[:8]}"
            conn.execute(
                "INSERT OR IGNORE INTO anti_patterns "
                "(id, description, rule, reason, domain, keywords, "
                " severity, evidence_count, status, source, created) "
                "VALUES (?,?,?,?,?,?,?,?,?,?,?)",
                (ap_id, desc[:500], "micro_promoted", f"Auto-promoted from failure {fid}",
                 domain or "general", "[]", "medium", 1, "active",
                 "micro_consolidation", datetime.now(timezone.utc).isoformat())
            )
            promoted += 1
    except Exception as e:
        log.debug("Failure promotion failed: %s", e)
    if promoted:
        conn.commit()
    return promoted


def _detect_contradictions(conn, cutoff: str) -> int:
    """Find new lessons that contradict existing failures (or vice versa)."""
    found = 0
    try:
        # Check recent lessons against all failures
        recent_lessons = conn.execute(
            "SELECT id, title FROM lessons WHERE timestamp > ? AND title IS NOT NULL",
            (cutoff,)
        ).fetchall()
        for lesson in recent_lessons:
            lid, ltitle = lesson[0], lesson[1]
            if not ltitle or len(ltitle) < 15:
                continue
            # Exact title match in failures = contradiction
            match = conn.execute(
                "SELECT id FROM failures WHERE title = ? LIMIT 1",
                (ltitle,)
            ).fetchone()
            if match:
                # Demote the lesson (failure takes precedence)
                conn.execute(
                    "UPDATE lessons SET confidence = MAX(0.15, confidence * 0.5) WHERE id = ?",
                    (lid,)
                )
                found += 1
    except Exception as e:
        log.debug("Contradiction detection failed: %s", e)
    if found:
        conn.commit()
    return found


def start_micro_daemon(verbose: bool = False):
    """Run micro-consolidation in a loop. Call from orchestrator."""
    log.info("Micro-consolidation daemon started (cycle=%ds)", CYCLE_SECONDS)
    while True:
        try:
            run_micro_cycle(verbose=verbose)
        except Exception as e:
            log.warning("Micro-cycle error: %s", e)
        time.sleep(CYCLE_SECONDS)


if __name__ == "__main__":
    logging.basicConfig(level=logging.INFO, format="%(asctime)s %(message)s")
    start_micro_daemon(verbose=True)
