"""
module uses paths, relative to file location
"""

import sys

import logging
import os
import platform
import site
from pathlib import Path
from types import TracebackType
from typing import Type, Optional, Tuple
from ._utils import _try_except

__all__ = [
    "_setup_all",
]


def _install_excepthook() -> None:
    # install custom exception hook

    def axioma_excepthook(extype: Type[BaseException], value: BaseException, traceback: TracebackType) -> None:
        print("Unhandled Python Exception:", file=sys.stderr)
        sys.__excepthook__(extype, value, traceback)

    sys.excepthook = axioma_excepthook


def _insert_first_environ_path(key: str, environ_path: str) -> None:
    """Добавление строки в начало переменной окружения PATH, с обработкой разделителей и частных случаев."""
    pathsep = os.pathsep
    env_dict = os.environ

    value = env_dict.get(key, None)
    if value is None:
        env_dict[key] = environ_path
    elif not isinstance(value, str):
        raise TypeError("environ value not a string")
    elif value.startswith(pathsep):
        env_dict[key] = environ_path + env_dict[key]
    else:
        env_dict[key] = environ_path + pathsep + env_dict[key]


def _contains_core_binary(path: Path) -> bool:
    """
    Possible values:
        core.dll libcore.so.* libcore.*.dylib
    """
    if not path.is_dir():
        return False

    for arg in path.iterdir():
        name = arg.name
        if name.startswith("core.") or name.startswith("libcore."):
            return True
    return False


# function uses paths, relative to file location
def _setup_axioma_home() -> Tuple[Path, bool]:
    is_dev: bool = False
    axioma_home: Optional[Path] = None

    if os.name == "nt":
        # Linux
        install_path = Path(__file__).parents[4]
    elif sys.platform.startswith("darwin"):
        # Darwin
        install_path = Path(__file__).parents[8] / "Frameworks"
    else:
        # Windows
        install_path = Path(__file__).parents[5] / "bin"

    dev_path = Path(__file__).parents[2]

    if _contains_core_binary(install_path):
        axioma_home = install_path
    elif _contains_core_binary(dev_path):
        axioma_home = dev_path
        is_dev = True

    if axioma_home is None:
        logging.debug(f"{__file__=}")
        logging.debug(f"{install_path=}")
        logging.debug(f"{dev_path=}")
        raise RuntimeError("Can't find Axioma installation")

    os.environ["AXIOMA_HOME"] = str(axioma_home)
    _insert_first_environ_path("PATH", str(axioma_home))

    return axioma_home, is_dev


def _setup_environment(axioma_home: Path, is_dev: bool) -> None:
    _add_dll_directory(axioma_home, is_dev)

    if is_dev:
        return None
    if sys.platform.startswith("darwin"):
        os.environ["QT_PLUGIN_PATH"] = str(axioma_home.parents[1] / "PlugIns")
    else:
        os.environ["QT_PLUGIN_PATH"] = str(axioma_home / "qtplugins")


def _add_dll_directory(axioma_home: Path, is_dev: bool) -> None:
    if not (sys.platform == "win32" and sys.version_info >= (3, 8)):
        return None

    # Необходимо наличие директории в PATH, дополнительно к os.add_dll_directory, для правильной инициализации ядра,
    # и чтобы избежать ошибок при импорте модулей, например numpy.
    try:
        os.add_dll_directory(str(axioma_home))
    except (Exception,) as e:
        print(str(e))

    # dev

    if not is_dev:
        return None

    for elem in filter(lambda elem_: Path(elem_).is_dir(), os.environ.get("PATH", '').split(os.pathsep)):
        try:
            logging.debug(str(os.add_dll_directory(elem)))
        except OSError as e:
            print(str(e))


def _setup_linux() -> None:
    path = Path(__file__)
    if not (
            platform.system() != "Windows" and
            "AXIOMA_HOME" in os.environ and
            "site-packages" in path.parts
    ):
        return None

    axioma_base = str(Path(os.environ["AXIOMA_HOME"]).parent)
    os.environ["AXIOMA_PLUGINS"] = axioma_base + "/bin/plugins"
    os.environ["AXIOMA_DATA"] = axioma_base + "/share/axioma"
    os.environ["GDAL_DATA"] = axioma_base + "/share/gdal"
    os.environ["PROJ_LIB"] = axioma_base + "/share/proj"
    os.environ["GDAL_DRIVER_PATH"] = axioma_base + "/bin/gdalplugins"
    os.environ["QT_PLUGIN_PATH"] = axioma_base + "/bin/qtplugins"
    if platform.system() == "Darwin":
        axioma_python_home = axioma_base + "/Frameworks/Python.framework/Versions/Current"
    else:
        axioma_python_home = axioma_base + "/python"
    py_lib = axioma_python_home + "/lib/python3.8"
    os.environ["AXIOMA_PYTHON_BINDINGS"] = py_lib + "/site-packages"
    os.environ["AXIOMA_PYTHON_BASEPATH"] = \
        f"{py_lib}:{py_lib}/plat-linux:{py_lib}/lib-dynload:{py_lib}/site-packages"
    os.environ["AXIOMA_PYTHON_PLUGINS"] = axioma_base + "/bin/python_plugins"
    os.environ["AXIOMA_PYTHON_INTERPRETER"] = axioma_python_home + "/bin/python3"


@_try_except()
def remove_user_site() -> None:
    user_site = site.USER_SITE
    if not isinstance(user_site, str):
        return None

    p = Path(site.USER_SITE)

    sys_path_formatted = [Path(elem) for elem in sys.path]
    if p in sys_path_formatted:
        i = sys_path_formatted.index(p)
        sys.path.pop(i)


def _setup_all() -> None:
    # _install_excepthook()
    axioma_home, is_dev = _setup_axioma_home()
    _setup_environment(axioma_home, is_dev)
    _setup_linux()
    remove_user_site()
