#!/usr/bin/env python3
"""Layer 2b of the install-confidence ladder: standalone-Python codesign seam.

This exercises the install seam Layer 2 can't reach. Layer 2 builds the venv from
whatever ``python3.12`` is on PATH (e.g. pyenv's). A clean machine instead uses
the **bundled** ``PythonStandalone.zip`` interpreter, and PyPI's native-extension
wheels (``pydantic_core``, ``cryptography``, ...) are signed with Team IDs that do
NOT match python-build-standalone. ``backend/bootstrap.py`` ad-hoc resigns the
interpreter with ``com.apple.security.cs.disable-library-validation`` so those
wheels can ``dlopen``. This script runs that exact chain end-to-end, no 2nd machine:

1. Extract the bundled ``PythonStandalone.zip`` to a temp dir (a pristine,
   unsigned interpreter — like a fresh machine), or use ``--installed``.
2. (darwin) Ad-hoc codesign it with the runtime entitlements plist — the same
   command bootstrap runs (catches a missing plist / broken codesign invocation).
3. Create a venv from it (catches a standalone that can't build a venv).
4. ``pip install --prefer-binary`` the native-extension packages (fast subset by
   default; ``--full`` installs requirements-headless.txt) — catches wheels with
   no ABI-compatible build for the standalone's Python.
5. Import the native modules through the venv python.

HONEST BOUNDARY: a plain CLI invocation does NOT run under the app's hardened
runtime, so macOS library validation is not enforced here — i.e. ``--no-codesign``
may still pass at the terminal. This layer therefore validates the
extract -> codesign-command -> venv -> native-wheel-ABI chain (the parts that
commonly break the bundle), but the *full* library-validation rejection only
reproduces under the launched app: use ``make fresh-install-smoke-launch`` for that.

Opt-in (downloads wheels). Apple Silicon only for the codesign step.

Usage:
    python scripts/standalone_python_smoke.py            # extract zip, codesign, verify
    python scripts/standalone_python_smoke.py --installed  # use ~/.mugi/python-3.12
    python scripts/standalone_python_smoke.py --full       # full requirements-headless
    python scripts/standalone_python_smoke.py --no-codesign # skip the resign step

Exit 0 + "STANDALONE_SMOKE_OK" on success.
"""

from __future__ import annotations

import argparse
import os
import shutil
import subprocess
import sys
import tempfile

REPO_ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
DEFAULT_ZIP = os.path.join(REPO_ROOT, "MugiApp", "Mugi", "Resources", "PythonStandalone.zip")
ENTITLEMENTS = os.path.join(REPO_ROOT, "backend", "mugi_python_runtime_entitlements.plist")
INSTALLED_STANDALONE = os.path.join(
    os.path.expanduser("~"), ".mugi", "python-3.12", "bin", "python3"
)

# Native C-extension wheels whose dlopen is what library validation rejects. The
# import names differ from the pip names, so install + import are separate maps.
NATIVE_PIP_PACKAGES = ["pydantic", "cryptography", "numpy", "pillow"]
NATIVE_IMPORT_MODULES = ["pydantic_core", "cryptography", "_cffi_backend", "numpy", "PIL"]


def _extract_standalone(zip_path: str, dest: str) -> str:
    """Extract PythonStandalone.zip (preserving perms via unzip) -> interpreter path."""
    if not os.path.isfile(zip_path):
        raise SystemExit(f"FAIL: PythonStandalone.zip not found: {zip_path}")
    if shutil.which("unzip") is None:
        raise SystemExit("FAIL: 'unzip' not available to extract the standalone")
    subprocess.run(["unzip", "-q", zip_path, "-d", dest], check=True)
    interp = os.path.join(dest, "python-3.12", "bin", "python3")
    if not os.path.isfile(interp):
        raise SystemExit(f"FAIL: interpreter not found after extract: {interp}")
    os.chmod(interp, 0o755)
    real = os.path.realpath(interp)
    if os.path.isfile(real):
        os.chmod(real, 0o755)
    return interp


def _codesign(interp: str) -> None:
    """Ad-hoc resign the real interpreter binary with the library-validation entitlement."""
    if sys.platform != "darwin":
        print("[standalone-smoke] non-darwin: skipping codesign", file=sys.stderr)
        return
    if not os.path.isfile(ENTITLEMENTS):
        raise SystemExit(f"FAIL: entitlements plist missing: {ENTITLEMENTS}")
    real_bin = os.path.realpath(interp)
    print(f"[standalone-smoke] codesign (disable-library-validation): {real_bin}")
    r = subprocess.run(
        [
            "/usr/bin/codesign",
            "-s",
            "-",
            "--force",
            "--timestamp=none",
            "--entitlements",
            ENTITLEMENTS,
            real_bin,
        ],
        capture_output=True,
        text=True,
        timeout=120,
    )
    if r.returncode != 0:
        raise SystemExit(f"FAIL: codesign failed: {(r.stderr or r.stdout).strip()}")


def run_standalone_smoke(args) -> int:
    tmp = tempfile.mkdtemp(prefix="mugi_standalone_smoke_")
    try:
        if args.installed:
            interp = INSTALLED_STANDALONE
            if not os.path.isfile(interp):
                print(
                    f"FAIL: installed standalone not found: {interp}. Launch the app "
                    "once or drop --installed to extract from the bundled zip.",
                    file=sys.stderr,
                )
                return 1
            print(f"[standalone-smoke] using installed standalone: {interp}")
        else:
            extract_dir = os.path.join(tmp, "py")
            os.makedirs(extract_dir, exist_ok=True)
            print(f"[standalone-smoke] extracting {args.zip} -> {extract_dir}")
            interp = _extract_standalone(args.zip, extract_dir)
            if not args.no_codesign:
                _codesign(interp)

        ver = subprocess.run(
            [interp, "-c", "import sys;print('.'.join(map(str,sys.version_info[:3])))"],
            capture_output=True,
            text=True,
            timeout=60,
        )
        print(f"[standalone-smoke] interpreter Python {ver.stdout.strip() or '?'}")

        venv_dir = os.path.join(tmp, "venv")
        print(f"[standalone-smoke] creating venv -> {venv_dir}")
        r = subprocess.run(
            [interp, "-m", "venv", venv_dir], capture_output=True, text=True, timeout=300
        )
        if r.returncode != 0:
            print("FAIL: venv creation from standalone failed.", file=sys.stderr)
            print((r.stderr or r.stdout).rstrip(), file=sys.stderr)
            return 1

        venv_python = os.path.join(venv_dir, "bin", "python3")
        if not os.path.isfile(venv_python):
            venv_python = os.path.join(venv_dir, "bin", "python")

        subprocess.run([venv_python, "-m", "pip", "install", "--upgrade", "pip"], timeout=300)

        if args.full:
            req = os.path.join(REPO_ROOT, "requirements-headless.txt")
            pip_cmd = [venv_python, "-m", "pip", "install", "--prefer-binary", "-r", req]
            print(f"[standalone-smoke] installing full requirements: {req}")
        else:
            pip_cmd = [venv_python, "-m", "pip", "install", "--prefer-binary", *NATIVE_PIP_PACKAGES]
            print(f"[standalone-smoke] installing native subset: {', '.join(NATIVE_PIP_PACKAGES)}")
        install = subprocess.run(pip_cmd, cwd=REPO_ROOT, timeout=2400)
        if install.returncode != 0:
            print(f"FAIL: pip install failed (rc={install.returncode}).", file=sys.stderr)
            return 1

        print(f"[standalone-smoke] importing native modules: {', '.join(NATIVE_IMPORT_MODULES)}")
        check = subprocess.run(
            [
                venv_python,
                "-c",
                "import importlib, sys\n"
                "mods=" + repr(NATIVE_IMPORT_MODULES) + "\n"
                "bad=[]\n"
                "for m in mods:\n"
                "    try:\n"
                "        importlib.import_module(m)\n"
                "    except Exception as e:\n"
                "        bad.append(f'{m}: {type(e).__name__}: {e}')\n"
                "print('IMPORT_FAILURES=' + ' | '.join(bad))\n"
                "sys.exit(1 if bad else 0)",
            ],
            capture_output=True,
            text=True,
            timeout=180,
        )
        print((check.stdout or "").rstrip())
        if check.returncode != 0:
            print(
                "FAIL: native module import failed under the standalone interpreter "
                "(wheel ABI mismatch, broken extract, or codesign/entitlement error). "
                "A clean machine would crash on first run.",
                file=sys.stderr,
            )
            if check.stderr.strip():
                print(check.stderr.rstrip(), file=sys.stderr)
            return 1

        print("STANDALONE_SMOKE_OK")
        print("PASS: bundled standalone Python created a venv and imported native wheels.")
        return 0
    finally:
        if args.keep:
            print(f"[standalone-smoke] kept temp dir: {tmp}")
        else:
            shutil.rmtree(tmp, ignore_errors=True)


def main() -> int:
    parser = argparse.ArgumentParser(description="Standalone-Python codesign/native-import smoke.")
    parser.add_argument("--zip", default=DEFAULT_ZIP, help="path to PythonStandalone.zip")
    parser.add_argument("--installed", action="store_true", help="use ~/.mugi/python-3.12 instead of the zip")
    parser.add_argument("--full", action="store_true", help="install requirements-headless.txt (slow)")
    parser.add_argument("--no-codesign", action="store_true", help="skip the ad-hoc resign step")
    parser.add_argument("--keep", action="store_true", help="keep the temp dir")
    args = parser.parse_args()
    return run_standalone_smoke(args)


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