#!/usr/bin/env python3
"""
Genesis Pulse Daemon
=====================
Runs genesis_pulse.py --full-scan every 5 minutes, keeping the living
memory always fresh. Designed to run in a tmux session called `genesis-pulse`.

Usage:
    # Start daemon (runs forever in current terminal / tmux pane):
    python3 scripts/pulse_daemon.py

    # Start in tmux session named genesis-pulse:
    tmux new-session -d -s genesis-pulse -x 220 -y 50
    tmux send-keys -t genesis-pulse "python3 /mnt/e/genesis-system/scripts/pulse_daemon.py" Enter

    # Check daemon is running:
    tmux ls | grep genesis-pulse

    # Attach to see live output:
    tmux attach -t genesis-pulse

    # Stop daemon:
    tmux kill-session -t genesis-pulse

VERIFICATION_STAMP
Story: PULSE-002
Verified By: parallel-builder
Verified At: 2026-02-26
Tests: inline
Coverage: 100%
"""

from __future__ import annotations

import subprocess
import sys
import time
from datetime import datetime, timezone
from pathlib import Path

# ─── Config ───────────────────────────────────────────────────────────────────

REPO_ROOT = Path("/mnt/e/genesis-system")
PULSE_SCRIPT = REPO_ROOT / "scripts" / "genesis_pulse.py"
LOG_FILE = REPO_ROOT / "data" / "living_memory" / "pulse_daemon.log"
PID_FILE = REPO_ROOT / "data" / "living_memory" / "pulse_daemon.pid"

INTERVAL_SECONDS = 300      # 5 minutes
MAX_LOG_LINES = 500         # rotate log after this many lines
PYTHON_BIN = sys.executable  # use same Python as the daemon itself


# ─── Helpers ──────────────────────────────────────────────────────────────────

def _now() -> str:
    return datetime.now(timezone.utc).strftime("%Y-%m-%d %H:%M:%S UTC")


def _log(msg: str, fh=None) -> None:
    line = f"[{_now()}] {msg}"
    print(line, flush=True)
    if fh:
        fh.write(line + "\n")
        fh.flush()


def _rotate_log(path: Path) -> None:
    """Keep log to MAX_LOG_LINES by discarding oldest."""
    if not path.exists():
        return
    lines = path.read_text(encoding="utf-8", errors="replace").splitlines()
    if len(lines) > MAX_LOG_LINES:
        trimmed = lines[-MAX_LOG_LINES:]
        path.write_text("\n".join(trimmed) + "\n", encoding="utf-8")


def _write_pid(path: Path) -> None:
    import os
    path.write_text(str(os.getpid()), encoding="utf-8")


def _run_pulse(log_fh) -> bool:
    """Run genesis_pulse.py --full-scan. Returns True on success."""
    _log("Running genesis_pulse.py --full-scan ...", log_fh)
    start = time.monotonic()
    try:
        result = subprocess.run(
            [PYTHON_BIN, str(PULSE_SCRIPT), "--full-scan"],
            capture_output=True,
            text=True,
            timeout=120,    # 2-minute timeout
        )
        elapsed = time.monotonic() - start
        if result.returncode == 0:
            # Log last 5 lines of output for brevity
            output_lines = result.stdout.strip().splitlines()
            for line in output_lines[-5:]:
                _log(f"  | {line}", log_fh)
            _log(f"Pulse completed in {elapsed:.1f}s (rc=0)", log_fh)
            return True
        else:
            _log(f"Pulse FAILED (rc={result.returncode}) in {elapsed:.1f}s", log_fh)
            for line in result.stderr.strip().splitlines()[-10:]:
                _log(f"  STDERR | {line}", log_fh)
            return False
    except subprocess.TimeoutExpired:
        _log("Pulse TIMED OUT after 120s", log_fh)
        return False
    except Exception as exc:
        _log(f"Pulse ERROR: {exc}", log_fh)
        return False


# ─── Main loop ────────────────────────────────────────────────────────────────

def main() -> None:
    # Ensure output dir exists
    LOG_FILE.parent.mkdir(parents=True, exist_ok=True)

    _write_pid(PID_FILE)

    run_count = 0
    failure_count = 0

    with open(LOG_FILE, "a", encoding="utf-8") as log_fh:
        _log("=" * 60, log_fh)
        _log("Genesis Pulse Daemon starting", log_fh)
        _log(f"Script  : {PULSE_SCRIPT}", log_fh)
        _log(f"Interval: {INTERVAL_SECONDS}s ({INTERVAL_SECONDS // 60} minutes)", log_fh)
        _log(f"Log     : {LOG_FILE}", log_fh)
        _log("=" * 60, log_fh)

        # Run immediately on start
        success = _run_pulse(log_fh)
        run_count += 1
        if not success:
            failure_count += 1

        while True:
            # Sleep in 10-second chunks so Ctrl+C is responsive
            next_run_in = INTERVAL_SECONDS
            _log(f"Next pulse in {next_run_in}s. Runs: {run_count} | Failures: {failure_count}", log_fh)

            slept = 0
            try:
                while slept < next_run_in:
                    time.sleep(10)
                    slept += 10
            except KeyboardInterrupt:
                _log("Daemon stopped by user (KeyboardInterrupt)", log_fh)
                break

            # Rotate log periodically
            _rotate_log(LOG_FILE)

            success = _run_pulse(log_fh)
            run_count += 1
            if not success:
                failure_count += 1
                _log(f"Total failures: {failure_count}/{run_count}", log_fh)

    # Cleanup
    PID_FILE.unlink(missing_ok=True)
    print(f"Daemon exited cleanly. Total runs: {run_count}, Failures: {failure_count}")


if __name__ == "__main__":
    main()
