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, QWizard

# 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.extensions.ags_reader.classes import AgsImporter, AgsReader
from openlog.plugins.extensions.azimuth_sorting.classes import (
    AssaySorter,
    AzimuthAnnotation,
)
from openlog.plugins.extensions.barchart_projection.classes import BarTraceSymbology
from openlog.plugins.extensions.data_binning.classes import DataBinner
from openlog.plugins.extensions.depth_ticks.classes import DepthTicksHandler
from openlog.plugins.extensions.detection_limit.classes import DetectionLimitHandler
from openlog.plugins.extensions.inspector.classes import (
    InspectorHandler,
    InspectorProjector,
)
from openlog.plugins.plugin_assay_column import PluginAssayColumn

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]


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]


# premium extensions
class InspectorPluginHook(NamedTuple):
    name: str
    # get InspectorHandler class
    inspector_handler: InspectorHandler
    # enable button
    enable: bool


class ManageAssayHook(NamedTuple):
    name: str
    # Assay manager wizard
    wizard: QWizard
    # enable button
    enable: bool


class AzimuthSortingHook(NamedTuple):
    name: str
    enable: bool
    sorter: AssaySorter
    annoter: AzimuthAnnotation


# extensions for numerical data visualization
class DetectionLimitHook(NamedTuple):
    name: str
    enable: bool
    handler: DetectionLimitHandler


class DepthTicksHook(NamedTuple):
    name: str
    enable: bool
    handler: DepthTicksHandler


class HeterogenousPlotsHook(NamedTuple):

    name: str
    validator: Any
    widget_factory: Any


class CrossPlotsHook(NamedTuple):

    name: str
    enable: bool
    config_factory: Any


class FillCurveHook(NamedTuple):

    name: str
    enable: bool
    handler: Any


class CrossSymbologyHook(NamedTuple):

    name: str
    enable: bool


class BarChartProjectionHook(NamedTuple):

    name: str
    projector: BarTraceSymbology
    enable: bool


class DataBinningHook(NamedTuple):

    name: str
    binner: DataBinner
    enable: bool


class AgsReaderHook(NamedTuple):

    name: str
    wizard: AgsImporter
    reader: AgsReader
    enable: bool


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 detection_plugin(self) -> Iterable[DetectionLimitHook]:
        return None

    @hookspec
    def azimuth_sorting_plugin(self) -> Iterable[AzimuthSortingHook]:
        return None

    @hookspec
    def depth_ticks_plugin(self) -> Iterable[DepthTicksHook]:
        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 []

    @hookspec
    def inspector_plugin(self) -> Iterable[InspectorPluginHook]:

        return None

    @hookspec
    def manage_assay_plugin(self) -> Iterable[ManageAssayHook]:

        return None

    @hookspec
    def heterogenous_plots_plugin(self) -> Iterable[HeterogenousPlotsHook]:
        return None

    @hookspec
    def crossplots_plugin(self) -> Iterable[CrossPlotsHook]:
        return None

    @hookspec
    def fill_curve_plugin(self) -> Iterable[FillCurveHook]:
        return None

    @hookspec
    def cross_symbology_plugin(self) -> Iterable[CrossSymbologyHook]:
        return None

    @hookspec
    def barchart_projection_plugin(self) -> Iterable[BarChartProjectionHook]:
        return None

    @hookspec
    def data_binning_plugin(self) -> Iterable[DataBinningHook]:
        return None

    @hookspec
    def ags_reader_plugin(self) -> Iterable[AgsReaderHook]:
        return None
