"""Task-orchestration helpers: CLI shape, dependency markers, completion
markers, lab config loading (docs/rules/build-pipeline-tasks.md,
docs/rules/code-python-style.md section 3).
"""

from __future__ import annotations

import argparse
import logging
import sys
from pathlib import Path

import psutil
import yaml

logger = logging.getLogger(__name__)

REPO_ROOT = Path(__file__).resolve().parent.parent


def parse_task_args(description: str) -> argparse.Namespace:
	parser = argparse.ArgumentParser(description=description)
	parser.add_argument("--log-level", default="INFO", choices=["DEBUG", "INFO", "WARNING", "ERROR"])
	parser.add_argument("--out-root", type=Path, default=REPO_ROOT / "out", help="artifact tree root (default: ./out)")
	args = parser.parse_args()
	logging.basicConfig(
		level=args.log_level,
		stream=sys.stderr,
		format="%(asctime)s %(levelname)-7s %(name)s: %(message)s",
	)
	return args


def check_dependencies(out_root: Path, deps: list[str]) -> None:
	for dep in deps:
		marker = out_root / ".done" / f"{dep}.ok"
		if not marker.exists():
			raise FileNotFoundError(f"dependency marker missing: {marker} - run task {dep} first")


def remove_stale_marker(out_root: Path, task_id: str) -> Path:
	marker = out_root / ".done" / f"{task_id}.ok"
	if marker.exists():
		logger.info("re-run: removing stale marker %s", marker)
		marker.unlink()
	return marker


def write_marker(out_root: Path, task_id: str, entries: dict) -> Path:
	marker = out_root / ".done" / f"{task_id}.ok"
	lines = [f"task: '{task_id}'", "status: ok"] + [f"{k}: {v}" for k, v in entries.items()]
	marker.parent.mkdir(parents=True, exist_ok=True)
	with open(marker, "w", encoding="utf-8", newline="\n") as fh:
		fh.write("\n".join(lines) + "\n")
	return marker


def load_lab_config(out_root: Path) -> dict:
	with open(out_root / "config" / "lab_config.yaml", encoding="utf-8") as fh:
		return yaml.safe_load(fh)


def process_metrics() -> dict:
	"""Peak RSS and CPU time of the current process.

	Every converter records its own build cost in its marker so the task 10
	hardware-sizing model can extrapolate per-format RAM/CPU needs, not just
	disk. peak_wset exists on Windows; the rss fallback covers Linux, where
	the value is the current (not peak) RSS - good enough for streaming
	converters whose RSS is flat by design.
	"""
	proc = psutil.Process()
	mem = proc.memory_info()
	peak = getattr(mem, "peak_wset", 0) or mem.rss
	cpu = proc.cpu_times()
	return {
		"proc_peak_rss_mb": round(peak / 1048576, 1),
		"proc_cpu_seconds": round(cpu.user + cpu.system, 1),
	}
