#!/usr/bin/env python3
"""Collect every gateway-test nodeid in an isolated sealed-interpreter process."""

from __future__ import annotations

import hashlib
import json
import os
import subprocess
from concurrent.futures import ThreadPoolExecutor
from pathlib import Path

ROOT = Path("/home/cube/projects/richard/.worktrees/nutricoach-v140-impl")
EVIDENCE = Path('/home/cube/projects/richard/traning coach/task-12-independent-verification-r9/gateway-sharded')
PYTHON = ROOT / ".venv/bin/python"
GATEWAY = ROOT / "tests/gateway"
LOGS = EVIDENCE / "logs/gateway-file-collection"
MANIFEST = EVIDENCE / "broad-gateway-nodeids-isolated.json"
WORKERS = 12


def sha256(path: Path) -> str:
    return hashlib.sha256(path.read_bytes()).hexdigest()


def nodeids(output: str) -> list[str]:
    return [
        line for line in output.splitlines()
        if "::" in line and (line.startswith("tests/gateway/") or "/tests/gateway/" in line)
    ]


def collect(index_and_path: tuple[int, Path]) -> dict[str, object]:
    index, path = index_and_path
    relative = path.relative_to(ROOT).as_posix()
    log = LOGS / f"{index:04d}-{path.name}.log"
    home = EVIDENCE / "gateway-file-collection-homes" / f"{index:04d}"
    pycache = EVIDENCE / "gateway-file-collection-pycache" / f"{index:04d}"
    home.mkdir(parents=True, exist_ok=True)
    pycache.mkdir(parents=True, exist_ok=True)
    env = {
        "PATH": os.environ["PATH"],
        "HOME": str(home),
        "TMPDIR": "/tmp",
        "PYTHONPATH": f"{ROOT / 'dualcoach/profile'}:{ROOT}",
        "PYTHONHASHSEED": "0",
        "TZ": "Asia/Seoul",
        "UV_OFFLINE": "1",
        "PYTHONDONTWRITEBYTECODE": "1",
        "PYTHONPYCACHEPREFIX": str(pycache),
    }
    basetemp = f"/tmp/nc9c-{index:04d}"
    argv = (
        str(PYTHON), "-m", "pytest", "--collect-only", "-q",
        "-p", "no:cacheprovider", "--import-mode=importlib",
        f"--basetemp={basetemp}", str(path),
    )
    completed = subprocess.run(argv, cwd=ROOT, env=env, text=True, capture_output=True, check=False)
    output = completed.stdout + completed.stderr
    log.write_text(output, encoding="utf-8")
    collected = nodeids(completed.stdout)
    outcome = (
        "collection_skip"
        if completed.returncode in {0, 5} and not collected
        else "collected"
        if completed.returncode == 0
        else "failed"
    )
    return {
        "test_file": relative,
        "test_file_sha256": sha256(path),
        "argv": list(argv),
        "returncode": completed.returncode,
        "outcome": outcome,
        "nodeids": collected,
        "output_log": log.relative_to(EVIDENCE).as_posix(),
        "output_sha256": sha256(log),
    }


def main() -> int:
    LOGS.mkdir(parents=True, exist_ok=True)
    paths = tuple(sorted(GATEWAY.glob("test_*.py")))
    with ThreadPoolExecutor(max_workers=WORKERS) as pool:
        results = list(pool.map(collect, enumerate(paths)))
    results.sort(key=lambda item: str(item["test_file"]))
    failures = [item["test_file"] for item in results if item["outcome"] == "failed"]
    manifest = {
        "schema": "nutricoach-v140-task12-isolated-gateway-nodeids-v1",
        "repository": ".",
        "interpreter": {
            "entrypoint": str(PYTHON),
            "resolved": str(PYTHON.resolve()),
            "sha256": sha256(PYTHON),
        },
        "isolation": {
            "collection": "one fresh subprocess per test file",
            "workers": WORKERS,
            "cwd": str(ROOT),
            "pythonpath": f"{ROOT / 'dualcoach/profile'}:{ROOT}",
        },
        "files": results,
        "file_count": len(results),
        "nodeid_count": sum(len(item["nodeids"]) for item in results),
        "collection_failures": failures,
        "collection_skips": [
            item["test_file"] for item in results
            if item["outcome"] == "collection_skip"
        ],
    }
    MANIFEST.write_text(json.dumps(manifest, sort_keys=True, separators=(",", ":")) + "\n", encoding="utf-8")
    print(f"GATEWAY_ISOLATED_COLLECTION files={manifest['file_count']} nodeids={manifest['nodeid_count']} failures={len(failures)}")
    return 0 if not failures else 1


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