"""Import modules on demand.

See https://bugs.python.org/issue17621 for background.
"""

import contextlib
import importlib.machinery
import os
import sys
from importlib.util import LazyLoader

# global flag controlling lazy import support
_disabled = False

# modules that have issues when lazily imported
_skip = frozenset(
    [
        "__builtin__",
        "__future__",
        "builtins",
        "grp",
        "pwd",
        "OpenSSL.SSL",  # pyopenssl
    ]
)


class _LazyLoader(LazyLoader):
    """LazyLoader with extended support for disabling and skipping modules."""

    def exec_module(self, module):
        """Make the module load lazily."""
        if _disabled or module.__name__ in _skip:
            self.loader.exec_module(module)
        else:
            super().exec_module(module)


# custom loaders using our extended LazyLoader
_extensions_loader = _LazyLoader.factory(importlib.machinery.ExtensionFileLoader)
_bytecode_loader = _LazyLoader.factory(importlib.machinery.SourcelessFileLoader)
_source_loader = _LazyLoader.factory(importlib.machinery.SourceFileLoader)


def _filefinder(path):
    """Return a custom FileFinder using our lazy loaders."""
    return importlib.machinery.FileFinder(
        path,
        (_extensions_loader, importlib.machinery.EXTENSION_SUFFIXES),
        (_source_loader, importlib.machinery.SOURCE_SUFFIXES),
        (_bytecode_loader, importlib.machinery.BYTECODE_SUFFIXES),
    )


def enable():
    """Enable lazy loading for all future module imports."""
    if os.environ.get("SNAKEOIL_DEMANDIMPORT", "y").lower() not in (
        "n",
        "no",
        "0",
        "false",
    ):
        sys.path_hooks.insert(0, _filefinder)


def is_enabled():
    """Determine if lazy loading is currently enabled."""
    return _filefinder in sys.path_hooks and not _disabled


def disable():
    """Disable lazy loading for all future module imports."""
    try:
        while True:
            sys.path_hooks.remove(_filefinder)
    except ValueError:
        pass


@contextlib.contextmanager
def disabled():
    """Context manager for temporarily disabling lazy imports.

    Example usage:
    >>>   from importlib.util import _LazyModule
    >>>   from snakeoil import demandimport
    >>>   demandimport.enable()
    >>>
    >>>   with demandimport.disabled():
    >>>      from module import submodule
    >>>   assert not isinstance(submodule, _LazyModule)
    """
    global _disabled
    enabled = is_enabled()
    if enabled:
        _disabled = True
    try:
        yield
    finally:
        if enabled:
            _disabled = False


@contextlib.contextmanager
def enabled():
    """Context manager for temporarily enabling lazy imports.

    Useful as a workaround for avoiding circular import issues.

    Example usage:
    >>>   from importlib.util import _LazyModule
    >>>   from snakeoil import demandimport
    >>>
    >>>   with demandimport.enabled():
    >>>      from module import submodule
    >>>   assert isinstance(submodule, _LazyModule)
    >>>   from module2 import submodule2
    >>>   assert not isinstance(submodule2, _LazyModule)
    """
    global _disabled
    enabled = is_enabled()
    if not enabled:
        _disabled = False
        enable()
    try:
        yield
    finally:
        if not enabled:
            _disabled = True
