"""
pyexcel.internal.source_plugin
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Second level abstraction

:copyright: (c) 2015-2025 by Onni Software Ltd.
:license: New BSD License
"""

from pyexcel import constants, exceptions
from lml.plugin import PluginManager
from pyexcel.internal.attributes import (
    register_book_attribute,
    register_sheet_attribute,
)

from pyexcel_io import constants as io_constants

REGISTRY_KEY_FORMAT = "%s-%s"
# ignore the following attributes
NO_DOT_NOTATION = (io_constants.DB_DJANGO, io_constants.DB_SQL)


class SourcePluginManager(PluginManager):
    """Data source plugin loader"""

    def __init__(self):
        PluginManager.__init__(self, "source")
        self.keywords = {}

    def load_me_later(self, plugin_info):
        PluginManager.load_me_later(self, plugin_info)
        self._register_a_plugin_info(plugin_info)

    def load_me_now(self, key, action=None, library=None, **keywords):
        """get source module into memory for use"""
        self._logger.debug("load me now:" + key)
        plugin = None
        for source in self.registry[key]:
            if source.is_my_business(action, **keywords):
                plugin = self.dynamic_load_library(source)
                module_name = _get_me_pypi_package_name(plugin.__module__)
                if library and module_name != library:
                    continue
                break

        else:
            # nothing is found, no break
            _error_handler(action, **keywords)
        return plugin

    def register_a_plugin(self, plugin_cls, plugin_info):
        """for dynamically loaded plugin"""
        PluginManager.register_a_plugin(self, plugin_cls, plugin_info)
        self._register_a_plugin_info(plugin_info)

    def get_a_plugin(
        self,
        target=None,
        action=None,
        source_library=None,
        **keywords,
    ):
        """obtain a source plugin for signature functions"""
        key = REGISTRY_KEY_FORMAT % (target, action)
        io_library = None
        # backward support pyexcel-io library parameter
        if "library" in keywords:
            io_library = keywords.pop("library")
        source_cls = self.load_me_now(
            key,
            action=action,
            library=source_library,
            **keywords,
        )
        if io_library is not None:
            keywords["library"] = io_library
        source_instance = source_cls(**keywords)
        return source_instance

    def get_source(self, **keywords):
        """obtain a sheet read source plugin for signature functions"""
        return self.get_a_plugin(
            target=constants.SHEET,
            action=constants.READ_ACTION,
            **keywords,
        )

    def get_book_source(self, **keywords):
        """obtain a book read source plugin for signature functions"""
        return self.get_a_plugin(
            target=constants.BOOK,
            action=constants.READ_ACTION,
            **keywords,
        )

    def get_writable_source(self, **keywords):
        """obtain a sheet write source plugin for signature functions"""
        return self.get_a_plugin(
            target=constants.SHEET,
            action=constants.WRITE_ACTION,
            **keywords,
        )

    def get_writable_book_source(self, **keywords):
        """obtain a book write source plugin for signature functions"""
        return self.get_a_plugin(
            target=constants.BOOK,
            action=constants.WRITE_ACTION,
            **keywords,
        )

    def get_keyword_for_parameter(self, key):
        """custom keyword for an attribute"""
        return self.keywords.get(key, None)

    def _register_a_plugin_info(self, plugin_info):
        debug_registry = "Source registry: "
        debug_attribute = "Instance attribute: "
        anything = False
        for key in plugin_info.tags():
            target, action = key.split("-")
            attributes = plugin_info.attributes
            if not isinstance(attributes, list):
                attributes = attributes()
            for attr in attributes:
                if attr in NO_DOT_NOTATION:
                    continue

                if target == "book":
                    register_book_attribute(target, action, attr)
                elif target == "sheet":
                    register_sheet_attribute(target, action, attr)
                else:
                    raise Exception(f"Known target: {target}")

                debug_attribute += f"{attr} "
                self.keywords[attr] = plugin_info.key
                anything = True
            debug_attribute += ", "
        if anything:
            self._logger.debug(debug_attribute)
            self._logger.debug(debug_registry)


def _error_handler(action, **keywords):
    if keywords:
        file_type = keywords.get("file_type", None)
        if file_type:
            raise exceptions.FileTypeNotSupported(
                constants.FILE_TYPE_NOT_SUPPORTED_FMT % (file_type, action),
            )

        if "on_demand" in keywords:
            keywords.pop("on_demand")
        msg = "Please check if there were typos in "
        msg += "function parameters: %s. Otherwise "
        msg += "unrecognized parameters were given."
        raise exceptions.UnknownParameters(msg % keywords)

    raise exceptions.UnknownParameters("No parameters found!")


def _get_me_pypi_package_name(module_name):
    root_module_name = module_name.split(".")[0]
    return root_module_name.replace("_", "-")


SOURCE = SourcePluginManager()
