__all__ = ['require']

import builtins
from functools import reduce
from importlib import import_module
from importlib.util import find_spec
import inspect
from itertools import product
from typing import Any

from .config import config


def require(name: str, cli=False) -> Any:
    if name == 'config':
        return config
    prefixes = config('require.prefix', default=[]) + ['']
    suffixes = ['.cli', ''] if cli else ['']
    for prefix, suffix in product(prefixes, suffixes):
        path = f"{prefix}{name}{suffix}".replace('-', '_').split('.')
        for i in [*range(len(path) - 1, 0, -1), len(path)]:
            module_name, attr_path = '.'.join(path[0:i]), path[i:]
            if _is_probably_missing(module_name):
                continue
            mod = import_module(module_name)
            try:
                r = reduce(getattr, attr_path, mod)
                if inspect.ismodule(r):
                    if attr_path and hasattr(r, attr_path[-1]):
                        return getattr(r, attr_path[-1])
                    if cli:
                        return getattr(r, 'main')
                return r
            except AttributeError:
                continue
    raise ImportError(f"require: '{name!r}' not found", name=name)


def _is_probably_missing(module_name: str) -> bool:
    try:
        return find_spec(module_name) is None
    except (ModuleNotFoundError, ValueError) as e:
        # If a parent package is missing, treat as "module not findable".
        # Example: module_name="x.y.z", e.name could be "x" or "x.y".
        if isinstance(e, ModuleNotFoundError):
            missing = e.name
            if missing == module_name or module_name.startswith(missing + "."):
                return True
            # Otherwise, a dependency failed inside an existing package: real error.
            raise
        # If it's a ValueError (module exists but has no spec), treat as not missing
        return False


# DEPRECATED
builtins.require = require
builtins.leptonix = require
builtins.I = require
