#!/usr/bin/env python3
# Copyright (c) 2026 PULSE Contributors. Proprietary. All rights reserved.
"""Orchestrator lifecycle helpers — process checks, daemon alerts, orphan cleanup."""
from __future__ import annotations

import json
import os
import signal
import subprocess
import time
from datetime import datetime, timezone
from pathlib import Path

from pulse.core.brain_utils import STATE_DIR, DAEMON_PIDS_FILE

DAEMON_ALERTS_FILE = STATE_DIR / "daemon_alerts.json"


def _is_process_running(pid: int) -> bool:
    if os.name == 'nt':
        try:
            output = subprocess.check_output(
                ["tasklist", "/FI", f"PID eq {pid}"],
                stderr=subprocess.STDOUT, creationflags=0x08000000).decode()
            return str(pid) in output
        except Exception:
            return False
    else:
        try:
            os.kill(pid, 0)
        except OSError:
            return False
        return True


def _write_daemon_alert(daemon_name: str, label: str,
                        exit_code: int, max_restarts: int):
    """Write alert to daemon_alerts.json when a daemon exceeds MAX_RESTARTS."""
    alerts = []
    if DAEMON_ALERTS_FILE.exists():
        try:
            alerts = json.loads(DAEMON_ALERTS_FILE.read_text(encoding="utf-8"))
        except Exception:
            alerts = []
    alerts.append({
        "daemon": daemon_name, "label": label,
        "timestamp": datetime.now(timezone.utc).isoformat(),
        "max_restarts": max_restarts, "exit_code": exit_code,
        "status": "dead",
    })
    try:
        DAEMON_ALERTS_FILE.write_text(json.dumps(alerts, indent=2), encoding="utf-8")
    except Exception as e:
        print(f"[PULSE] Failed to write daemon alert: {e}")


def _kill_orphan_daemons():
    """Gracefully terminate daemon processes left from a previous crashed run."""
    if not DAEMON_PIDS_FILE.exists():
        return
    try:
        pids = json.loads(DAEMON_PIDS_FILE.read_text())
        if not isinstance(pids, list):
            return
        killed = 0
        for pid in pids:
            if _is_process_running(pid):
                try:
                    if os.name == 'nt':
                        subprocess.run(["taskkill", "/F", "/T", "/PID", str(pid)],
                                       stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL,
                                       creationflags=0x08000000)
                    else:
                        os.kill(pid, signal.SIGTERM)
                        for _ in range(10):
                            if not _is_process_running(pid):
                                break
                            time.sleep(0.5)
                        if _is_process_running(pid):
                            os.kill(pid, signal.SIGKILL)
                    killed += 1
                except Exception:
                    pass
        if killed:
            print(f"[PULSE] Cleaned up {killed} orphan daemon(s).")
            time.sleep(1)  # Let Windows release file handles from killed processes
        DAEMON_PIDS_FILE.unlink(missing_ok=True)
    except Exception:
        pass


def _save_daemon_pids(processes: dict):
    """Persist child PIDs for orphan cleanup on next boot."""
    try:
        pids = [p.pid for p in processes.values() if p.poll() is None]
        DAEMON_PIDS_FILE.write_text(json.dumps(pids))
    except Exception:
        pass
