from collections.abc import Callable, Iterable
from typing import Any, NamedTuple, Type, Union

import pluggy  # type: ignore

# PyQGIS
from qgis.PyQt.QtWidgets import QAction, QMenu

# project
from openlog.datamodel.assay.generic_assay import AssayColumn
from openlog.datamodel.connection.openlog_connection import OpenLogConnection
from openlog.gui.assay_visualization.config.assay_column_visualization_config import (
    AssayColumnVisualizationConfig,
)
from openlog.plugins.plugin_assay_column import PluginAssayColumn

# APP_NAME = "openlog-qgis-plugin"
APP_NAME = "openlog_qgis_plugin"


hookspec = pluggy.HookspecMarker(APP_NAME)
hookimpl = pluggy.HookimplMarker(APP_NAME)


# connection hook
class ConnectionHook(NamedTuple):
    name: str

    # action added to OpenLog menu
    connection_action: Callable[[Any], Union[QAction, QMenu]]

    # OpenLogConnection subclass
    connection_class: Type[OpenLogConnection]


#
class SerieTypeHook(NamedTuple):
    # serie name : numerical, categorical ...etc
    name: str
    # function used in AssayColumnVisualizationConfigFactory
    create_assay_visualization_column_config: Callable[
        [Any], AssayColumnVisualizationConfig
    ]
    # function used in AssayPlotItemFactory
    create_plot_item: Callable[[Any], None]

    # function used to add a DiagramMaker instance in PlotWidgetContainer
    get_diagram_maker: Callable[[Any], tuple[Any]] = None


class CreatePluginAssayColumnHook(NamedTuple):
    """Hook for PluginAssayColumn creation:
    name(str) : name of the plugin
    assay_column (Callable[[Any], PluginAssayColumn]) : function called for PluginAssayColumn
                                                  Can use any parameter and return an PluginAssayColumn
    """

    name: str
    create_generic_assay_column: Callable[[Any], PluginAssayColumn]


class AppHookSpecs:
    """Holds all hookspecs for this application"""

    @hookspec
    def connection(self) -> Iterable[ConnectionHook]:
        """search_backend
        Hookspec for the search backend hook.

        Check out the [creating plugins][creating-plugins] guide for more information on
        using this plugin hook.
        """
        return None

    @hookspec
    def serie_type(self) -> Iterable[SerieTypeHook]:
        return None

    @hookspec
    def create_generic_assay_column(self) -> Iterable[CreatePluginAssayColumnHook]:
        """HookSpec for PluginAssayColumn creation
        This can be used to define a reader specific for AssayColumn

        Returns:
            Iterable[CreatePluginAssayColumnHook]: available PluginAssayColumn creation hook in plugin
        """
        return []
