#!/usr/bin/env python3

"""
Build arti and rpc client library, and run RPC integration tests.

ENVIRONMENT VARIABLES:

- CARGO: the cargo binary to use.  Defaults to "cargo".
- PYTHON3: path to a python binary to use.  Defaults to `sys.executable`
- ARTI_RPC_TEST_DIR: A location in which to store arti's state and config.
  Defaults to ".arti_rpc_test" at the top level of this git repo.

ARGUMENTS:

- `--arti-dir=DIR`: Override ARTI_RPC_TEST_DIR.
"""

from __future__ import annotations

import argparse
import os
import sys
import subprocess


def parent(path: str, n: int = 1):
    """
    Return the parent of (the parent of (... the directory of path)).

    The number of "parent of"s is controlled by "n"
    """
    for _ in range(n):
        path = os.path.split(path)[0]
    return path


def cargo_build(packages: list[str], extra_flags: list[str] = []):
    """
    Use cargo to build all of the rust packages in `packages`,
    passing `extra_flags` on the command line.
    """
    args = [
        CARGO,
        "build",
        "--profile",
        CARGO_PROFILE,
    ]
    for p in packages:
        args.extend(["-p", p])
    args.extend(extra_flags)

    outcome = subprocess.run(args, cwd=TOPLEVEL)
    outcome.check_returncode()


######
# Parse the command line.

parser = argparse.ArgumentParser(
    prog="run", description="Invoke Arti RPC integration tests"
)
parser.add_argument("--arti-dir", help="Location for Arti proxy storage and config")
parser.add_argument("remainder", nargs="*", help="Passed directly to arti_rpc_tests")
args = parser.parse_args()

######
# Set up locations and paths from the environment.

CARGO_PROFILE = "quicktest"
TOPLEVEL = os.path.abspath(parent(os.path.dirname(__file__), n=3))
CARGO = os.environ.get("CARGO", "cargo")
PYTHON3 = os.environ.get("PYTHON3", sys.executable)

lib_extension = {"win32": "dll", "darwin": "dylib"}.get(sys.platform, "so")

ARTI = os.path.join(TOPLEVEL, "target", CARGO_PROFILE, "arti")
LIBRPC = os.path.join(
    TOPLEVEL, "target", CARGO_PROFILE, "libarti_rpc_client_core." + lib_extension
)
PYRPC = os.path.join(TOPLEVEL, "python", "arti_rpc", "src")
PYRPC_TESTS = os.path.join(TOPLEVEL, "python", "arti_rpc_tests", "src")

if args.arti_dir is not None:
    ARTI_RPC_TEST_DIR = args.arti_dir
else:
    ARTI_RPC_TEST_DIR = os.environ.get(
        "ARTI_RPC_TEST_DIR", os.path.join(TOPLEVEL, ".arti_rpc_test")
    )
os.makedirs(ARTI_RPC_TEST_DIR, mode=0o700, exist_ok=True)

#####
# Build Arti, and make sure it is there.

cargo_build(["arti", "arti-rpc-client-core"], ["--all-features"])

if not os.path.exists(ARTI):
    print("whoops no arti at", ARTI)
    sys.exit(1)
if not os.path.exists(ARTI):
    print("whoops no librpc at", LIBRPC)
    sys.exit(1)

#####
# Set up the environment expected by `arti_rpc_tests`.

os.environ["ARTI"] = ARTI
os.environ["LIBARTI_RPC_CLIENT_CORE"] = LIBRPC
os.environ["ARTI_RPC_TEST_DIR"] = ARTI_RPC_TEST_DIR

# Note that we're prepending these locations to PYTHONPATH.
# We want to use these versions, not ones that might happen to be installed.
pathelts = [PYRPC, PYRPC_TESTS]
try:
    pathelts.append(os.environ["PYTHONPATH"])
except KeyError:
    pass
os.environ["PYTHONPATH"] = os.pathsep.join(pathelts)

# Run `arti_rpc_tests` and wait for it to finish.
outcome = subprocess.run([PYTHON3, "-m", "arti_rpc_tests"] + args.remainder)
# Give an error if it failed.
outcome.check_returncode()
