"""
Provides uniform and consistent path helpers for `specklepy`
"""
import os
import sys
from pathlib import Path
from typing import Optional
from importlib import import_module, invalidate_caches
import pkg_resources

from speckle.utils.utils import get_qgis_python_path

_user_data_env_var = "SPECKLE_USERDATA_PATH"
_debug = False
_vs_code_directory = os.path.expanduser(
    "~\.vscode\extensions\ms-python.python-2023.20.0\pythonFiles\lib\python"
)


def _path() -> Optional[Path]:
    """Read the user data path override setting."""
    path_override = os.environ.get(_user_data_env_var)
    if path_override:
        return Path(path_override)
    return None


_application_name = "Speckle"


def override_application_name(application_name: str) -> None:
    """Override the global Speckle application name."""
    global _application_name
    _application_name = application_name


def override_application_data_path(path: Optional[str]) -> None:
    """
    Override the global Speckle application data path.

    If the value of path is `None` the environment variable gets deleted.
    """
    if path:
        os.environ[_user_data_env_var] = path
    else:
        os.environ.pop(_user_data_env_var, None)


def _ensure_folder_exists(base_path: Path, folder_name: str) -> Path:
    path = base_path.joinpath(folder_name)
    path.mkdir(exist_ok=True, parents=True)
    return path


def user_application_data_path() -> Path:
    """Get the platform specific user configuration folder path"""
    path_override = _path()
    if path_override:
        return path_override

    try:
        if sys.platform.startswith("win"):
            app_data_path = os.getenv("APPDATA")
            if not app_data_path:
                raise Exception("Cannot get appdata path from environment.")
            return Path(app_data_path)
        else:
            # try getting the standard XDG_DATA_HOME value
            # as that is used as an override
            app_data_path = os.getenv("XDG_DATA_HOME")
            if app_data_path:
                return Path(app_data_path)
            else:
                return _ensure_folder_exists(Path.home(), ".config")
    except Exception as ex:
        raise Exception("Failed to initialize user application data path.", ex)


def user_speckle_folder_path() -> Path:
    """Get the folder where the user's Speckle data should be stored."""
    return _ensure_folder_exists(user_application_data_path(), _application_name)


def user_speckle_connector_installation_path(host_application: str) -> Path:
    """
    Gets a connector specific installation folder.

    In this folder we can put our connector installation and all python packages.
    """
    return _ensure_folder_exists(
        _ensure_folder_exists(user_speckle_folder_path(), "connector_installations"),
        host_application,
    )


print("Starting module dependency installation")
print(sys.executable)

PYTHON_PATH = get_qgis_python_path()


def connector_installation_path(host_application: str) -> Path:
    connector_installation_path = user_speckle_connector_installation_path(
        host_application
    )
    connector_installation_path.mkdir(exist_ok=True, parents=True)

    # set user modules path at beginning of paths for earlier hit
    if sys.path[0] != connector_installation_path:
        sys.path.insert(0, str(connector_installation_path))

    print(f"Using connector installation path {connector_installation_path}")
    return connector_installation_path


def is_pip_available() -> bool:
    try:
        import_module("pip")  # noqa F401
        return True
    except ImportError:
        return False


def ensure_pip() -> None:
    print("Installing pip... ")

    from subprocess import run

    print(PYTHON_PATH)

    completed_process = run([PYTHON_PATH, "-m", "ensurepip"])

    if completed_process.returncode == 0:
        print("Successfully installed pip")
    else:
        raise Exception(
            f"Failed to install pip, got {completed_process.returncode} return code"
        )


def get_requirements_path() -> Path:
    # we assume that a requirements.txt exists next to the __init__.py file
    path = Path(Path(__file__).parent, "requirements.txt")
    assert path.exists(), f"path not found {path}"
    return path


def _dependencies_installed(requirements: str, path: str) -> bool:
    for d in pkg_resources.find_distributions(path):
        entry = f"{d.key}=={d.version}"
        if entry in requirements:
            requirements = requirements.replace(entry, "")
    requirements = requirements.replace(" ", "").replace(";", "").replace(",", "")
    if len(requirements) > 0:
        return False
    print("Dependencies already installed")
    return True


def install_requirements(host_application: str) -> None:
    # set up addons/modules under the user
    # script path. Here we'll install the
    # dependencies
    requirements = get_requirements_path().read_text().replace("\n", "")
    path = str(connector_installation_path(host_application))

    print(f"Installing debugpy to {path}")
    from subprocess import run

    if _debug is True:
        try:
            import debugpy
        except:
            completed_process = run(
                [
                    PYTHON_PATH,
                    "-m",
                    "pip",
                    "install",
                    "-t",
                    str(path),
                    "debugpy==1.8.0",
                ],
                capture_output=True,
                text=True,
            )
            if completed_process.returncode != 0:
                m = f"Failed to install debugpy through pip. Disable debug mode or install debugpy manually. Full log: {completed_process}"
                raise Exception(completed_process)

    if _dependencies_installed(requirements, path):
        return

    try:
        import shutil

        shutil.rmtree(path)
    except PermissionError as e:
        # from speckle.utils.panel_logging import logger
        raise Exception("Restart QGIS for changes to take effect")

    print(f"Installing Speckle dependencies to {path}")
    from subprocess import run

    completed_process = run(
        [
            PYTHON_PATH,
            "-m",
            "pip",
            "install",
            "-t",
            str(path),
            "-r",
            str(get_requirements_path()),
        ],
        capture_output=True,
        text=True,
    )

    if completed_process.returncode != 0:
        m = f"Failed to install dependenices through pip, got {completed_process.returncode} as return code. Full log: {completed_process}"
        print(m)
        print(completed_process.stdout)
        print(completed_process.stderr)
        raise Exception(m)


def install_dependencies(host_application: str) -> None:
    if not is_pip_available():
        ensure_pip()

    install_requirements(host_application)


def _import_dependencies() -> None:
    import_module("specklepy")
    # the code above doesn't work for now, it fails on importing graphql-core
    # despite that, the connector seams to be working as expected
    # But it would be nice to make this solution work
    # it would ensure that all dependencies are fully loaded
    # requirements = get_requirements_path().read_text()
    # reqs = [
    #     req.split(" ; ")[0].split("==")[0].split("[")[0].replace("-", "_")
    #     for req in requirements.split("\n")
    #     if req and not req.startswith(" ")
    # ]
    # for req in reqs:
    #     print(req)
    #     import_module("specklepy")


def ensure_dependencies(host_application: str) -> None:
    try:
        install_dependencies(host_application)
        invalidate_caches()
        # _import_dependencies()
        print("Successfully found dependencies")
    except ImportError:
        raise Exception(
            f"Cannot automatically ensure Speckle dependencies. Please try restarting the host application {host_application}!"
        )


def startDegugger() -> None:
    try:
        # debugger: https://gist.github.com/giohappy/8a30f14678aa7e446f9b694c632d7089
        if _debug is True:
            import debugpy
            import shutil

            sys.path.append(_vs_code_directory)
            debugpy.configure(python=shutil.which("python"))

            try:
                debugpy.listen(("localhost", 5678))
            except:
                debugpy.connect(("localhost", 5678))
    except:
        pass


# path = str(connector_installation_path("QGIS"))
# print(path)
