#!/usr/bin/env python3
"""Run the pytest suite. Auto-installs pytest via the system package
manager on first run (sudo will prompt for your password)."""

import os
import shutil
import subprocess
import sys
from pathlib import Path


REPO = Path(__file__).resolve().parent
sys.path.insert(0, str(REPO / "src" / "scripts"))


def _have_pytest() -> bool:
    try:
        import pytest  # noqa: F401
        return True
    except ImportError:
        return False


def _install_pytest() -> bool:
    print("pytest not installed — installing now (sudo will prompt)...")
    if shutil.which("pacman"):
        cmd = ["sudo", "pacman", "-S", "--noconfirm", "python-pytest"]
    elif shutil.which("apt"):
        cmd = ["sudo", "apt", "install", "-y", "python3-pytest"]
    elif shutil.which("dnf"):
        cmd = ["sudo", "dnf", "install", "-y", "python3-pytest"]
    elif shutil.which("zypper"):
        cmd = ["sudo", "zypper", "--non-interactive", "install", "python3-pytest"]
    else:
        print("no supported package manager (pacman / apt / dnf / zypper) found.",
              file=sys.stderr)
        print("install pytest manually, e.g. `pip install --user pytest`",
              file=sys.stderr)
        return False
    return subprocess.run(cmd, check=False).returncode == 0


def _run_vm_harness(extra_args: list[str]) -> int:
    """Delegate to the Plymouth render harness and inherit its rc.

    The harness runs plymouthd in debug mode inside a private Xvfb server —
    no Docker, VM, or initramfs rebuild. It captures Plymouth's own nested-X
    window pixmap rather than the desktop or active app. PNGs
    land in tests/vm/output/ — boot mode (logo + progress bar)
    and shutdown mode (logo only).

    Requires sudo because plymouthd refuses to run unless uid==0.
    render.sh checks $EUID upfront and prints
    `Re-run as: sudo ./test --vm` rather than prompting inline,
    avoiding the pam_faillock cascade on terminals where sudo can't
    read the password.

    Kept out of pytest because it needs sudo and system theme staging.
    The harness validates the captured pixels and returns non-zero when the
    logo, progress fill, shutdown state, or script log is wrong."""
    vm_dir = REPO / "tests" / "vm"
    render_script = vm_dir / "render.sh"
    if not render_script.is_file():
        print(f"Render harness missing: {render_script}", file=sys.stderr)
        return 1
    proc = subprocess.Popen([str(render_script), *extra_args])
    try:
        return proc.wait()
    except KeyboardInterrupt:
        proc.terminate()
        try:
            return proc.wait(timeout=10)
        except subprocess.TimeoutExpired:
            proc.kill()
            return proc.wait()


def main() -> int:
    args = list(sys.argv[1:])

    # ``./test --vm`` short-circuits the pytest path entirely because the
    # privileged host render cannot run inside pytest's normal process.
    # Strip --vm from argv so anything after it is forwarded to render.sh.
    if "--vm" in args:
        i = args.index("--vm")
        args.pop(i)
        return _run_vm_harness(args)

    if not _have_pytest():
        if not _install_pytest() or not _have_pytest():
            return 1

    env = os.environ.copy()
    if "--slow" in args:
        # Opt-in to the C++ build tests (~30s) for a release run. They
        # also run automatically when ``CI`` is set in the environment.
        env["MAC_TAHOE_RUN_SLOW"] = "1"
        args.remove("--slow")

    tests_dir = REPO / "tests"
    # Verbose by default (one line per test as it starts) and a short
    # traceback on failure. Pass -q on the CLI to override.
    return subprocess.run(
        [sys.executable, "-m", "pytest", str(tests_dir),
         "-v", "--tb=short", *args],
        check=False, env=env,
    ).returncode


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