#!/usr/bin/env python3
"""
Genesis Thinking Block Monitor (Layer 6 of Defense System)
==========================================================
PostToolUse hook that monitors for conditions that can lead to
thinking block corruption -- the root cause of the session 7e3fae84 crash.

The crash chain:
  1. Autocompact triggers during active agent notifications
  2. Post-compaction, Claude Code reconstructs the conversation
  3. Thinking blocks from pre-compaction get included but modified
  4. API rejects modified thinking blocks with 400 error
  5. Every subsequent request fails (session is dead)

This hook detects precursor conditions and warns BEFORE the crash:
  - Rapid queue operations (agent notification storm)
  - High compaction frequency (context thrashing)
  - Context at compaction boundary during active agents

Lightweight: reads state files only, no API calls.
"""

import sys
import json
import os
import time
from datetime import datetime, timezone
from pathlib import Path

STATE_DIR = Path("/mnt/e/genesis-system/data/context_state")
EVENTS_DIR = Path("/mnt/e/genesis-system/data/observability")
MONITOR_STATE = STATE_DIR / "thinking_monitor.json"

# Thresholds
RAPID_COMPACTION_WINDOW = 300  # seconds - if 2+ compactions in 5 min, warn
AGENT_STORM_THRESHOLD = 3      # if 3+ agent notifications in quick succession
DANGER_ZONE_PCT = 78           # context % where compaction can trigger unexpectedly


def get_monitor_state() -> dict:
    """Read monitor state."""
    try:
        if MONITOR_STATE.exists():
            with open(MONITOR_STATE, "r") as f:
                return json.load(f)
    except Exception:
        pass
    return {
        "last_compaction_ts": "",
        "compaction_timestamps": [],
        "warnings_issued": 0,
        "last_warning_ts": "",
    }


def save_monitor_state(state: dict):
    """Save monitor state."""
    try:
        STATE_DIR.mkdir(parents=True, exist_ok=True)
        with open(MONITOR_STATE, "w") as f:
            json.dump(state, f)
    except Exception:
        pass


def get_recent_compactions() -> list:
    """Read recent compaction timestamps."""
    timestamps = []
    try:
        log_file = STATE_DIR / "compaction_log.jsonl"
        if log_file.exists():
            with open(log_file, "r") as f:
                for line in f:
                    try:
                        event = json.loads(line.strip())
                        ts = event.get("timestamp", "")
                        if ts:
                            timestamps.append(ts)
                    except Exception:
                        continue
    except Exception:
        pass
    return timestamps


def get_active_agent_count() -> int:
    """Read active agent count from metrics."""
    try:
        metrics_file = EVENTS_DIR / "metrics.json"
        if metrics_file.exists():
            with open(metrics_file, "r") as f:
                metrics = json.load(f)
                return metrics.get("agents_active", 0)
    except Exception:
        pass
    return 0


def get_context_usage() -> float:
    """Read current context usage."""
    try:
        state_file = STATE_DIR / "current.json"
        if state_file.exists():
            with open(state_file, "r") as f:
                state = json.load(f)
                return state.get("used_percentage", 0)
    except Exception:
        pass
    return 0


def check_danger_conditions() -> str:
    """
    Check for conditions that precede thinking block corruption.
    Returns warning message or empty string.
    """
    now = datetime.now(timezone.utc)
    warnings = []

    context_pct = get_context_usage()
    active_agents = get_active_agent_count()
    compaction_timestamps = get_recent_compactions()

    # Condition 1: Context in danger zone WITH active agents
    # This is the EXACT scenario that killed session 7e3fae84
    if context_pct >= DANGER_ZONE_PCT and active_agents > 0:
        warnings.append(
            f"THINKING BLOCK DANGER: Context at {context_pct:.0f}% with "
            f"{active_agents} active agent(s). Autocompact may trigger during "
            f"agent notification delivery, corrupting thinking blocks. "
            f"ACTION: Kill or wait for agents to complete BEFORE context hits 85%."
        )

    # Condition 2: Rapid compaction (thrashing)
    if len(compaction_timestamps) >= 2:
        try:
            last_two = compaction_timestamps[-2:]
            t1 = datetime.fromisoformat(last_two[0])
            t2 = datetime.fromisoformat(last_two[1])
            delta = abs((t2 - t1).total_seconds())
            if delta < RAPID_COMPACTION_WINDOW:
                warnings.append(
                    f"COMPACTION THRASHING: 2 compactions within {delta:.0f}s. "
                    f"Session may be generating too much content. "
                    f"ACTION: Reduce agent count or switch to new session."
                )
        except Exception:
            pass

    # Condition 3: Very high context with no recent heartbeat
    if context_pct >= 85:
        warnings.append(
            f"CRITICAL CONTEXT: {context_pct:.0f}%. Session is in compaction zone. "
            f"Thinking blocks in the current turn are at risk. "
            f"ACTION: Complete current operation and compact manually."
        )

    if warnings:
        return " | ".join(warnings)
    return ""


def main():
    try:
        hook_input = json.loads(sys.stdin.read())
    except (json.JSONDecodeError, Exception):
        print(json.dumps({}))
        return

    warning = check_danger_conditions()

    result = {}
    if warning:
        # Rate limit warnings (max 1 per 60 seconds)
        monitor_state = get_monitor_state()
        last_warn = monitor_state.get("last_warning_ts", "")
        should_warn = True

        if last_warn:
            try:
                last_warn_dt = datetime.fromisoformat(last_warn)
                now = datetime.now(timezone.utc)
                if (now - last_warn_dt).total_seconds() < 60:
                    should_warn = False
            except Exception:
                pass

        if should_warn:
            result["additionalContext"] = warning
            monitor_state["warnings_issued"] = monitor_state.get("warnings_issued", 0) + 1
            monitor_state["last_warning_ts"] = datetime.now(timezone.utc).isoformat()
            save_monitor_state(monitor_state)

    print(json.dumps(result))


if __name__ == "__main__":
    main()
