import os
import re
import shutil
import tempfile
import subprocess
from pathlib import Path
from SCons.Errors import UserError
from contextlib import contextmanager


Import("env")


if env["platform"].startswith("windows"):
    PYTHON_RELATIVE_PATH = "python.exe"
    python = f"{env['DIST_PLATFORM']}/{PYTHON_RELATIVE_PATH}"
    scripts = f"{env['DIST_PLATFORM']}/Scripts"
else:
    PYTHON_RELATIVE_PATH = "bin/python3"
    python = f"{env['DIST_PLATFORM']}/{PYTHON_RELATIVE_PATH}"
    scripts = f"{env['DIST_PLATFORM']}/bin"


def run_cmd(cmd):
    if isinstance(cmd, str):
        cmd = cmd.split()
    try:
        return subprocess.run(cmd, check=True, capture_output=True)
    except subprocess.CalledProcessError as exc:
        print(f"Error !!! Non-zero return code: {exc.returncode}")
        print(f"command: {cmd}")
        print(f"stdout: {exc.stdout.decode()}")
        print(f"stderr: {exc.stderr.decode()}")
        raise UserError(f"Test has failed (returncode: {exc.returncode})") from exc


@contextmanager
def run_cmd_and_handle_errors(cmd):
    out = run_cmd(cmd)
    try:
        yield out
    except Exception as exc:
        print(f"Error !!! {str(exc)}")
        print(f"command: {cmd}")
        print(f"stdout: {out.stdout.decode()}")
        print(f"stderr: {out.stderr.decode()}")
        raise UserError(f"Test has failed ({str(exc)})") from exc


def test_factory(target, cmd):
    if callable(cmd):
        fn = cmd
    else:

        def fn(target, source, env):
            run_cmd(cmd)

        fn.__name__ = f"test_{target}"

    env.Command([target], [], fn, strfunction=lambda target, source, env: f"Test: {target[0]}")
    env.Depends(target, env["DIST_ROOT"])
    env.AlwaysBuild(target)

    return target


test_factory("run_python", f"{python} --version")


test_factory(
    "import_godot_module",
    [
        python,
        "-c",
        """
try:
    import godot
except ImportError as exc:
    assert "Cannot initialize godot module given Godot GDNative API not available." in str(exc)
""",
    ],
)


def test_ensurepip_and_run_pip(target, source, env):
    # ensurepip does modification, so copy dist first
    with tempfile.TemporaryDirectory() as tmpdirname:
        pythonscript_path = f"{tmpdirname}/pythonscript"
        shutil.copytree(str(env["DIST_PLATFORM"].abspath), pythonscript_path, symlinks=True)
        python = f"{pythonscript_path}/{PYTHON_RELATIVE_PATH}"

        run_cmd(f"{python} -m ensurepip")

        with run_cmd_and_handle_errors(
            f"{python} -m pip --disable-pip-version-check --version"
        ) as out:
            # Check pip site-packages location
            stdout = out.stdout.decode().strip()
            regex = r"^pip\s+[0-9.]+\s+from\s+(.*)\s+\(python\s+[0-9.+]+\)$"
            match = re.match(regex, stdout)
            if match:
                site_packages_path = Path(match.group(1))
                dist_platform_path = Path(pythonscript_path)
                try:
                    site_packages_path.relative_to(dist_platform_path)
                except ValueError as exc:
                    raise AssertionError(
                        f"pip site-packages is not located inside dist folder `{env['DIST_PLATFORM']}`"
                    ) from exc

            else:
                raise AssertionError(f"stdout doesn't match regex `{regex}`")

        run_cmd(
            f"{python} -m pip install requests"
        )  # Basically the most downloaded packages on pypi
        run_cmd([python, "-c", "import requests"])


test_factory("ensurepip_and_run_pip", test_ensurepip_and_run_pip)
