import sys
from importlib.machinery import ModuleSpec, PathFinder
from importlib.machinery import all_suffixes as module_suffixes
from importlib.util import spec_from_file_location
from itertools import chain
from pathlib import Path

MAPPING = {'src': 'C:\\Users\\Chris\\repos\\GameBotHelper\\src', 'test': 'C:\\Users\\Chris\\repos\\GameBotHelper\\test', 'venv': 'C:\\Users\\Chris\\repos\\GameBotHelper\\venv'}
NAMESPACES = {'src': ['C:\\Users\\Chris\\repos\\GameBotHelper\\src'], 'test': ['C:\\Users\\Chris\\repos\\GameBotHelper\\test'], 'venv': ['C:\\Users\\Chris\\repos\\GameBotHelper\\venv'], 'venv.Lib': ['C:\\Users\\Chris\\repos\\GameBotHelper\\venv\\Lib'], 'venv.Scripts': ['C:\\Users\\Chris\\repos\\GameBotHelper\\venv\\Scripts'], 'venv.Lib.site-packages': ['C:\\Users\\Chris\\repos\\GameBotHelper\\venv\\Lib\\site-packages'], 'venv.Lib.site-packages.jaraco': ['C:\\Users\\Chris\\repos\\GameBotHelper\\venv\\Lib\\site-packages\\jaraco'], 'venv.Lib.site-packages.docutils.parsers.rst.include': ['C:\\Users\\Chris\\repos\\GameBotHelper\\venv\\Lib\\site-packages\\docutils\\parsers\\rst\\include'], 'venv.Lib.site-packages.docutils.writers.s5_html.themes': ['C:\\Users\\Chris\\repos\\GameBotHelper\\venv\\Lib\\site-packages\\docutils\\writers\\s5_html\\themes'], 'venv.Lib.site-packages.docutils.writers.s5_html.themes.big-black': ['C:\\Users\\Chris\\repos\\GameBotHelper\\venv\\Lib\\site-packages\\docutils\\writers\\s5_html\\themes\\big-black'], 'venv.Lib.site-packages.docutils.writers.s5_html.themes.big-white': ['C:\\Users\\Chris\\repos\\GameBotHelper\\venv\\Lib\\site-packages\\docutils\\writers\\s5_html\\themes\\big-white'], 'venv.Lib.site-packages.docutils.writers.s5_html.themes.default': ['C:\\Users\\Chris\\repos\\GameBotHelper\\venv\\Lib\\site-packages\\docutils\\writers\\s5_html\\themes\\default'], 'venv.Lib.site-packages.docutils.writers.s5_html.themes.medium-black': ['C:\\Users\\Chris\\repos\\GameBotHelper\\venv\\Lib\\site-packages\\docutils\\writers\\s5_html\\themes\\medium-black'], 'venv.Lib.site-packages.docutils.writers.s5_html.themes.medium-white': ['C:\\Users\\Chris\\repos\\GameBotHelper\\venv\\Lib\\site-packages\\docutils\\writers\\s5_html\\themes\\medium-white'], 'venv.Lib.site-packages.docutils.writers.s5_html.themes.small-black': ['C:\\Users\\Chris\\repos\\GameBotHelper\\venv\\Lib\\site-packages\\docutils\\writers\\s5_html\\themes\\small-black'], 'venv.Lib.site-packages.docutils.writers.s5_html.themes.small-white': ['C:\\Users\\Chris\\repos\\GameBotHelper\\venv\\Lib\\site-packages\\docutils\\writers\\s5_html\\themes\\small-white']}
PATH_PLACEHOLDER = '__editable__.game_bot_helper-0.0.1.finder' + ".__path_hook__"


class _EditableFinder:  # MetaPathFinder
    @classmethod
    def find_spec(cls, fullname, path=None, target=None):
        extra_path = []

        # Top-level packages and modules (we know these exist in the FS)
        if fullname in MAPPING:
            pkg_path = MAPPING[fullname]
            return cls._find_spec(fullname, Path(pkg_path))

        # Handle immediate children modules (required for namespaces to work)
        # To avoid problems with case sensitivity in the file system we delegate
        # to the importlib.machinery implementation.
        parent, _, child = fullname.rpartition(".")
        if parent and parent in MAPPING:
            return PathFinder.find_spec(fullname, path=[MAPPING[parent], *extra_path])

        # Other levels of nesting should be handled automatically by importlib
        # using the parent path.
        return None

    @classmethod
    def _find_spec(cls, fullname, candidate_path):
        init = candidate_path / "__init__.py"
        candidates = (candidate_path.with_suffix(x) for x in module_suffixes())
        for candidate in chain([init], candidates):
            if candidate.exists():
                return spec_from_file_location(fullname, candidate)


class _EditableNamespaceFinder:  # PathEntryFinder
    @classmethod
    def _path_hook(cls, path):
        if path == PATH_PLACEHOLDER:
            return cls
        raise ImportError

    @classmethod
    def _paths(cls, fullname):
        # Ensure __path__ is not empty for the spec to be considered a namespace.
        return NAMESPACES[fullname] or MAPPING.get(fullname) or [PATH_PLACEHOLDER]

    @classmethod
    def find_spec(cls, fullname, target=None):
        if fullname in NAMESPACES:
            spec = ModuleSpec(fullname, None, is_package=True)
            spec.submodule_search_locations = cls._paths(fullname)
            return spec
        return None

    @classmethod
    def find_module(cls, fullname):
        return None


def install():
    if not any(finder == _EditableFinder for finder in sys.meta_path):
        sys.meta_path.append(_EditableFinder)

    if not NAMESPACES:
        return

    if not any(hook == _EditableNamespaceFinder._path_hook for hook in sys.path_hooks):
        # PathEntryFinder is needed to create NamespaceSpec without private APIS
        sys.path_hooks.append(_EditableNamespaceFinder._path_hook)
    if PATH_PLACEHOLDER not in sys.path:
        sys.path.append(PATH_PLACEHOLDER)  # Used just to trigger the path hook
