#!/usr/bin/env python3
"""Registry read-back gate for hub releases (REQ-121).

Three releases (0.16.16-0.16.18) were recorded as published while npm never
received them: the publish workflow failed and nothing checked the registry
before the ceremony recorded "publication evidence" and closed the release
tracker. This script is the missing gate — the ceremony may record publication
evidence ONLY after this exits 0.

Confirmation requires BOTH, with bounded retry/backoff for propagation:
  1. `npm view <pkg>@<version> version` returns exactly <version>
  2. `npm view <pkg> dist-tags.latest` returns exactly <version>

Anything else fails closed (exit 1): no publication evidence, the release
tracker stays open, and the diagnostic names the version that did not land.

Usage:
  python scripts/publish_verify.py --version 0.16.19 [--package prd-plugin]
                                   [--attempts 10] [--delay-seconds 30]
"""
from __future__ import annotations

import argparse
import json
import subprocess
import sys
import time


def _npm_run(args):
    """(returncode, stdout) for an npm invocation; never raises."""
    try:
        proc = subprocess.run(["npm", *args], capture_output=True, text=True,
                              timeout=120, shell=(sys.platform == "win32"))
        return proc.returncode, proc.stdout
    except (OSError, subprocess.TimeoutExpired) as exc:
        return 1, f"npm unavailable: {exc}"


def verify(package, version, run=_npm_run, attempts=10, delay_seconds=30):
    """Poll the registry until the version is confirmed or attempts exhaust."""
    version_seen = latest_seen = None
    used = 0
    for attempt in range(1, attempts + 1):
        used = attempt
        rc, out = run(["view", f"{package}@{version}", "version"])
        version_seen = out.strip() if rc == 0 and out.strip() else None
        if version_seen == version:
            rc, out = run(["view", package, "dist-tags.latest"])
            latest_seen = out.strip() if rc == 0 and out.strip() else None
            if latest_seen == version:
                return {"published": True, "package": package, "version": version,
                        "version_seen": version_seen, "latest_seen": latest_seen,
                        "attempts_used": used}
        if attempt < attempts and delay_seconds:
            time.sleep(delay_seconds)
    return {"published": False, "package": package, "version": version,
            "version_seen": version_seen, "latest_seen": latest_seen,
            "attempts_used": used}


def main(argv=None):
    parser = argparse.ArgumentParser(description=__doc__.splitlines()[0])
    parser.add_argument("--package", default="prd-plugin")
    parser.add_argument("--version", required=True)
    parser.add_argument("--attempts", type=int, default=10)
    parser.add_argument("--delay-seconds", type=int, default=30)
    parser.add_argument("--runner-test-mode", choices=("absent", "present"),
                        help="Testing only: fake registry that never/always has the version.")
    args = parser.parse_args(argv)

    run = _npm_run
    if args.runner_test_mode == "absent":
        run = lambda a: (1, "")  # noqa: E731
    elif args.runner_test_mode == "present":
        run = lambda a: (0, args.version + "\n")  # noqa: E731

    report = verify(args.package, args.version, run=run,
                    attempts=args.attempts, delay_seconds=args.delay_seconds)
    print(json.dumps(report, indent=2))
    if not report["published"]:
        print(f"FAIL-CLOSED: {args.package}@{args.version} is NOT confirmed on the "
              "registry — do not record publication evidence; the release tracker "
              "stays open.", file=sys.stderr)
        return 1
    return 0


if __name__ == "__main__":
    sys.exit(main())
