"""
Cross-platform process management utilities.

Centralizes all OS-specific process operations so the rest of the codebase
can call platform-agnostic functions.
"""

import os
import sys
import stat
import signal
import asyncio
import subprocess
import logging

import psutil

logger = logging.getLogger("skop-runner")


# --- Process Lifecycle ---


def is_pid_alive(pid: int) -> bool:
    """Check if a process with the given PID is running. Cross-platform."""
    return psutil.pid_exists(pid)


def kill_process_tree(pid: int, timeout: float = 5.0) -> None:
    """Kill a process and all its children. Cross-platform.

    On Unix: uses os.killpg() for atomic group kill, then psutil tree walk
    as cleanup.
    On Windows: uses psutil tree walk only (no process groups).
    """
    # Unix: atomic process group kill first (single kernel syscall, no TOCTOU race)
    if sys.platform != "win32":
        try:
            os.killpg(os.getpgid(pid), signal.SIGKILL)
        except (OSError, ProcessLookupError):
            pass
    # Then psutil tree walk as cleanup / Windows primary path
    try:
        parent = psutil.Process(pid)
    except (psutil.NoSuchProcess, psutil.AccessDenied):
        return
    try:
        children = parent.children(recursive=True)
    except (psutil.NoSuchProcess, psutil.AccessDenied):
        children = []
    for child in children:
        try:
            child.kill()
        except (psutil.NoSuchProcess, psutil.AccessDenied):
            pass
    try:
        parent.kill()
    except (psutil.NoSuchProcess, psutil.AccessDenied):
        pass
    psutil.wait_procs(children + [parent], timeout=timeout)


def terminate_process(pid: int, timeout: float = 10.0) -> None:
    """Gracefully terminate a process (SIGTERM), then force kill. Cross-platform."""
    try:
        proc = psutil.Process(pid)
    except (psutil.NoSuchProcess, psutil.AccessDenied):
        return
    try:
        proc.terminate()  # SIGTERM on Unix, TerminateProcess on Windows
        proc.wait(timeout=timeout)
    except psutil.TimeoutExpired:
        try:
            proc.kill()
            proc.wait(timeout=5)
        except (psutil.NoSuchProcess, psutil.AccessDenied):
            pass
    except (psutil.NoSuchProcess, psutil.AccessDenied):
        pass


# --- Subprocess Creation ---


def get_subprocess_kwargs() -> dict:
    """Get platform-specific kwargs for asyncio.create_subprocess_shell."""
    if sys.platform == "win32":
        # CREATE_NEW_PROCESS_GROUP is only defined on Windows
        return {"creationflags": getattr(subprocess, "CREATE_NEW_PROCESS_GROUP", 0x00000200)}
    else:
        return {"start_new_session": True}


# --- Signal Handling ---


def setup_signal_handlers(loop: asyncio.AbstractEventLoop, handler: callable) -> None:
    """Register shutdown signal handlers. Cross-platform."""
    if sys.platform != "win32":
        for sig in (signal.SIGINT, signal.SIGTERM):
            loop.add_signal_handler(sig, handler)
    else:
        def _win_handler(signum, frame):
            loop.call_soon_threadsafe(handler)
        signal.signal(signal.SIGINT, _win_handler)
        if hasattr(signal, "SIGBREAK"):
            signal.signal(signal.SIGBREAK, _win_handler)


# --- File Security ---


def secure_file_permissions(path: str) -> None:
    """Restrict file to owner-only access. Cross-platform."""
    if sys.platform != "win32":
        os.chmod(path, stat.S_IRUSR | stat.S_IWUSR)
    else:
        try:
            username = os.environ.get("USERNAME", "")
            if username:
                subprocess.run(
                    ["icacls", str(path), "/inheritance:r",
                     "/grant:r", f"{username}:(R,W)"],
                    capture_output=True, timeout=10,
                )
        except Exception:
            logger.debug(f"Could not set Windows file permissions on {path}")


# --- PATH Construction ---


def get_default_system_path() -> str:
    """Get a sensible default PATH for the current platform."""
    if sys.platform == "win32":
        sys_root = os.environ.get("SystemRoot", r"C:\Windows")
        return f"{sys_root}\\System32;{sys_root}"
    else:
        return "/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin"
