import os
import sys
from pathlib import Path
from types import ModuleType
# mypy: disable-error-code="name-defined"

# __all__ is computed below.
__pre_all__ = [@init_modules@]
__version__ = "@FINAL_PACKAGE_VERSION@"
__version_info__ = (@BINDING_API_MAJOR_VERSION@, @BINDING_API_MINOR_VERSION@, @BINDING_API_MICRO_VERSION@, "@BINDING_API_PRE_RELEASE_VERSION_TYPE@", "@BINDING_API_PRE_RELEASE_VERSION@")

SKIP_MYPY_TEST = bool("@SKIP_MYPY_TEST@")


def _additional_dll_directories(package_dir):
    # Find shiboken6 relative to the package directory.
    root = Path(package_dir).parent
    # Check for a flat .zip as deployed by cx_free(PYSIDE-1257)
    if root.suffix == '.zip':
        return []
    shiboken6 = root / 'shiboken6'
    if shiboken6.is_dir():  # Standard case, only shiboken6 is needed
        return [shiboken6]
    # The below code is for the build process when generate_pyi.py
    # is executed in the build directory. We need libpyside and Qt in addition.
    shiboken6 = Path(root).parent / 'shiboken6' / 'libshiboken'
    if not shiboken6.is_dir():
        raise ImportError(str(shiboken6) + ' does not exist')
    result = [shiboken6, root / 'libpyside']
    libpysideqml = root / 'libpysideqml'
    if libpysideqml.is_dir():
        result.append(libpysideqml)
    for path in os.environ.get('PATH').split(';'):
        if path:
            if (Path(path) / 'qmake.exe').exists():
                result.append(path)
                break
    return result


def _setupQtDirectories():
    # On Windows we need to explicitly import the shiboken6 module so
    # that the libshiboken.dll dependency is loaded by the time a
    # Qt module is imported. Otherwise due to PATH not containing
    # the shiboken6 module path, the Qt module import would fail
    # due to the missing libshiboken dll.
    # In addition, as of Python 3.8, the shiboken package directory
    # must be added to the DLL search paths so that shiboken6.dll
    # is found.
    # We need to do the same on Linux and macOS, because we do not
    # embed rpaths into the PySide6 libraries that would point to
    # the libshiboken library location. Importing the module
    # loads the libraries into the process memory beforehand, and
    # thus takes care of it for us.

    pyside_package_dir = Path(__file__).parent.resolve()

    if sys.platform == 'win32':
        for dir in _additional_dll_directories(pyside_package_dir):
            os.add_dll_directory(os.fspath(dir))

    try:
        # PYSIDE-1497: we use the build dir or install dir or site-packages, whatever the path
        #              setting dictates. There is no longer a difference in path structure.
        global Shiboken
        from shiboken6 import Shiboken
    except Exception as e:
        paths = ', '.join(sys.path)
        print(f"PySide6/__init__.py: Unable to import Shiboken from {paths}: {e}",
              file=sys.stderr)
        raise

    if sys.platform == 'win32':
        # PATH has to contain the package directory, otherwise plugins
        # won't be able to find their required Qt libraries (e.g. the
        # svg image plugin won't find Qt5Svg.dll).
        os.environ['PATH'] = os.fspath(pyside_package_dir) + os.pathsep + os.environ['PATH']

        # On Windows, add the PySide6\openssl folder (created by setup.py's
        # --openssl option) to the PATH so that the SSL DLLs can be found
        # when Qt tries to dynamically load them. Tell Qt to load them and
        # then reset the PATH.
        openssl_dir = pyside_package_dir / 'openssl'
        if openssl_dir.exists():
            path = os.environ['PATH']
            try:
                os.environ['PATH'] = os.fspath(openssl_dir) + os.pathsep + path
                try:
                    from . import QtNetwork
                except ImportError:
                    pass
                else:
                    QtNetwork.QSslSocket.supportsSsl()
            finally:
                os.environ['PATH'] = path


def _find_all_qt_modules():
    # Since the wheel split, the __all__ variable cannot be computed statically,
    # because we don't know all modules in advance.

    # Instead, we use __getattr__ which is supported since Python 3.7
    # and create the __all__ list on demand when needed.
    unordered = set()
    pattern = "Qt*.pyd" if sys.platform == "win32" else "Qt*.so"
    for module in Path(__file__).resolve().parent.glob(pattern):
        name = module.name[:module.name.find(".")]
        if name.endswith("_d"):  # Windows debug suffix?
            name = name[:-2]
        unordered.add(name)
    ordered_part = __pre_all__
    result = []
    for name in ordered_part:
        if name in unordered:
            result.append(name)
            unordered.remove(name)
    result.extend(unordered)
    return result


# Provide the __all__ variable only on access.
def __getattr__(name: str) -> list[str]:
    if name == "__all__":
        global __all__
        __all__ = _find_all_qt_modules()
        return __all__
    raise AttributeError(f"module '{__name__}' has no attribute '{name}' :)")


# Be prepared that people can access the module dict instead.
class ModuleDict(dict):
    def __missing__(self, key):
        if key == "__all__":
            self[key] = __all__ if "__all__" in globals() else __getattr__("__all__")
            return __all__
        raise KeyError(f"dict of module '{__name__}' has no key '{key}' :)")


class SubModule(ModuleType):
    pass


_setupQtDirectories()
Shiboken.replaceModuleDict(sys.modules["PySide6"], SubModule, ModuleDict(globals()))
