from __future__ import annotations

import argparse
import json
import os
import pathlib
import subprocess
import sys
import tempfile
import time


ROOT = pathlib.Path(__file__).resolve().parents[1]
sys.path.insert(0, str(ROOT))
sys.path.insert(0, str(ROOT / "src"))


def _pid_alive(pid: int) -> bool:
    if os.name == "nt":
        result = subprocess.run(
            ["tasklist", "/FI", f"PID eq {pid}", "/FO", "CSV", "/NH"],
            capture_output=True,
            text=True,
            timeout=10,
            check=False,
        )
        return str(pid) in result.stdout and "No tasks" not in result.stdout
    try:
        fields = (pathlib.Path("/proc") / str(pid) / "stat").read_text(encoding="utf-8").rsplit(")", 1)[1].strip().split()
        return fields[0] != "Z"
    except (OSError, IndexError):
        return False


def _minimal_process_tree_probe(test_id: str) -> dict[str, object]:
    from reliability_core.process_control import run_supervised_process, total_timeout_for_operation
    from reliability_core.suite_isolation import phase_budget_from_environment

    started = time.perf_counter()
    phase_budget = phase_budget_from_environment(ROOT)
    ready_timeout_seconds = phase_budget.ready_budget_seconds
    with tempfile.TemporaryDirectory(prefix="r8-victim-") as directory:
        ready_path = pathlib.Path(directory) / "ready.json"
        record = run_supervised_process(
            [sys.executable, str(ROOT / "tests" / "fixtures" / "spawn_tree.py"), str(ready_path)],
            ROOT,
            environment={**os.environ, "PYTHONDONTWRITEBYTECODE": "1"},
            total_timeout_seconds=total_timeout_for_operation(ready_timeout_seconds + 0.1),
            execution_timeout_seconds=0.1,
            ready_path=ready_path,
            ready_timeout_seconds=ready_timeout_seconds,
            max_output_bytes=256 * 1024,
        )
        ready_payload = record["ready_handshake"].get("payload") or {}
        child_pids = [
            int(ready_payload[key])
            for key in ("child_pid", "grandchild_pid")
            if key in ready_payload
        ]
        alive = [pid for pid in child_pids if _pid_alive(pid)]
    observed = {
        "ready": record["ready_handshake"].get("ready") is True,
        "timed_out": record["timed_out"],
        "tracked_descendant_count": record["tracked_descendant_count"],
        "orphan_process_count": record["orphan_process_count"],
        "zombie_process_count": record["zombie_process_count"],
        "output_drain_threads_stopped": record["output_drain_threads_stopped"],
        "tracker_thread_stopped": record["tracker_thread_stopped"],
        "alive_after_timeout": alive,
        "elapsed_ms": record["elapsed_ms"],
        "ready_timeout_seconds": ready_timeout_seconds,
        "ready_budget_source": "run_scoped_phase_calibration",
    }
    passed = (
        observed["ready"] is True
        and observed["timed_out"] is True
        and int(observed["tracked_descendant_count"]) >= 2
        and observed["orphan_process_count"] == 0
        and observed["zombie_process_count"] == 0
        and observed["output_drain_threads_stopped"] is True
        and observed["tracker_thread_stopped"] is True
        and not alive
    )
    return {
        "test_id": test_id,
        "implementation": "minimal_process_tree_probe",
        "passed": passed,
        "latency_ms": round((time.perf_counter() - started) * 1000, 3),
        "observed": observed,
    }


def main() -> int:
    parser = argparse.ArgumentParser()
    parser.add_argument("--test-id", default="R4-RG-05")
    parser.add_argument("--iteration", type=int, required=True)
    parser.add_argument("--mode", choices=("pass", "fail", "hang"), default="pass")
    args = parser.parse_args()
    if args.mode == "hang":
        time.sleep(60)
        return 124
    result = _minimal_process_tree_probe(args.test_id)
    if args.mode == "fail":
        result["passed"] = False
        result["assertion"] = "injected sacrificial failure"
    print(json.dumps({"iteration": args.iteration, "result": result}, sort_keys=True))
    return 0 if result["passed"] else 2


if __name__ == "__main__":
    raise SystemExit(main())
