import os
import sys
import types
import pkgutil
import importlib

from wurker.settings import conf
from wurker.util import init_logger


def load_plugins(logger=None):
    """
    For each plugin group (like "bees", "lib", "lib.mail", etc),
    add the corresponding directory from the opt_root to sys.path
    (if not already added) and then dynamically import all modules in that subdirectory,
    merging them into the corresponding package namespace.
    """

    logger = logger or init_logger()

    # Define the allowed plugin groups and their target namespaces.
    # The keys here are the package names in which the modules should reside.
    plugin_groups = {
        "wurker.settings": "settings",
        "wurker.settings.bees": "settings/bees",
        "wurker.lib": "lib",
        "wurker.lib.mail": "lib/mail",
        "wurker.models": "models",
        "wurker.bees": "bees",
        "wurker.bees.dispatchers": "bees/dispatchers",
    }

    # Pre-import all built‐in packages for plugin groups.
    for pkg_name in plugin_groups.keys():
        try:
            # This ensures the built-in package is loaded.
            importlib.import_module(pkg_name)
            logger.log(f"Pre-imported built-in package: {pkg_name}", prefix='load_plugins', level="debug")
        except Exception as e:
            logger.log(f"Could not pre-import built-in package: {pkg_name}: {e}", prefix='load_plugins', level="debug")

    base_opt = conf.opt_root

    if base_opt not in sys.path:
        sys.path.insert(0, base_opt)
        logger.log(f"Loading plugins to sys.path from: '{base_opt}'...", prefix='load_plugins', level="debug")

    for full_pkg_name, rel_path in plugin_groups.items():
        group_path = os.path.join(base_opt, *rel_path.split('/'))
        if not os.path.isdir(group_path):
            logger.log(f"No plugin directory for plugin group '{rel_path}' at: '{group_path}'",
                prefix='load_plugins', level="debug")
            continue

        # For a package like "bees", if it already exists, merge __path__.
        if full_pkg_name in sys.modules:
            pkg = sys.modules[full_pkg_name]
            # Preserve the existing __path__ (the built-in location)
            original_paths = list(pkg.__path__)
            if group_path not in original_paths:
                # Merge the existing paths with the custom one.
                new_paths = original_paths + [group_path]
                pkg.__path__ = new_paths
                logger.log(f"Updated package {full_pkg_name} __path__: {pkg.__path__}",
                    prefix='load_plugins', level="debug")
        else:
            # If the package doesn't exist, create it with __path__ set to group_path.
            pkg = types.ModuleType(full_pkg_name)
            pkg.__path__ = [group_path]
            sys.modules[full_pkg_name] = pkg
            logger.log(f"Created module namespace: '{full_pkg_name}' with __path__: {pkg.__path__}",
                prefix='load_plugins', level="debug")

        # Iterate over .py files in group_path.
        for finder, name, ispkg in pkgutil.iter_modules([group_path]):
            if name.startswith("__"):
                continue

            # Construct the expected full module name, e.g., "bees.dummy"
            full_module_name = f"{full_pkg_name}.{name}"
            try:
                # Import the module using the full dotted name.
                module = importlib.import_module(full_module_name)
                # Inject the module as an attribute of the existing package.
                setattr(pkg, name, module)
                # Ensure it's also registered in sys.modules.
                sys.modules[full_module_name] = module

                logger.log(f"Loaded custom plugin module: '{full_module_name}'",
                    prefix='load_plugins', level="debug")
            except Exception as e:
                logger.log(f"Error loading custom module: '{full_module_name}': {e}",
                    prefix='load_plugins', level="debug")
                raise e
