# standard
from datetime import datetime
from typing import Any

# project
from openlog.datamodel.assay.generic_assay import AssayDefinition, GenericAssay
from openlog.plugins.manager import get_plugin_manager
from openlog.plugins.plugin_assay_column import PluginAssayColumn


class GenericAssayPluginColumnReader(GenericAssay):
    def __init__(
        self,
        hole_id: str,
        assay_definition: AssayDefinition,
        **kwargs,
    ):
        """GenericAssay only for data read that use plugin to read column data

        Args:
            hole_id (str): collar hole_id
            assay_definition (AssayDefinition): definition of assay (variable, domain, ...)
            **kwargs: Arbitrary keyword arguments. Define option for plugin use
        """
        super().__init__(
            hole_id=hole_id,
            assay_definition=assay_definition,
            use_assay_column_plugin_reader=True,
        )
        self.kwargs = kwargs

    def _get_generic_assay_column(self, column_str: str) -> PluginAssayColumn:
        """Get PluginAssayColumn to read data for a column

        Args:
            column_str (str): column to read

        Returns:
            PluginAssayColumn: used to read data
        """
        assay_column = self.assay_definition.columns[column_str]
        if assay_column.read_plugin_name is not None:
            plugin_manager = get_plugin_manager()
            return plugin_manager.create_generic_assay_column(
                plugin_name=assay_column.read_plugin_name,
                hole_id=self.hole_id,
                assay_definition=self.assay_definition,
                column_str=column_str,
                **self.kwargs,
            )

    def get_distinct_col_values(self, column: str, related_column: str = "") -> Any:
        """
        Returns distinct available value for assay column with type depending on the plugin used

        Args:
            column (str): assay column
            related_column:  (str) related assay column (default ""). Used if an assay column is stored as row
        Returns:
            Any: distinct assay column value, type depends on the plugin used
        """
        return self._get_generic_assay_column(column).get_distinct_col_values(
            related_column=related_column
        )

    def get_all_values(
        self, column: str, remove_none: bool = False, related_column: str = ""
    ) -> Any:
        """
        Returns all values available for assay with type depending on the plugin used

        Args:
            column (str): assay column
            remove_none: (bool) remove none values (default False)
            related_column:  (str) related assay column (default ""). Used if an assay column is stored as row
        Returns:
            Any: assay values, type depends on the plugin used
        """
        return self._get_generic_assay_column(column).get_all_values(
            remove_none=remove_none, related_column=related_column
        )

    def get_values_from_time(
        self,
        column: str,
        date_min: datetime,
        date_max: datetime,
        remove_none: bool = False,
        related_column: str = "",
    ) -> Any:
        """
        Returns available values for assay with type depending on the plugin used from a time interval.

        Args:
            column (str): assay column
            date_min (datetime): minimum datetime
            date_max (datetime): maximum datetime
            remove_none: (bool) remove none values (default False)
            related_column:  (str) related assay column (default ""). Used if an assay column is stored as row
        Returns:
            Any: assay values, type depends on the plugin used
        """
        return (
            self._get_generic_assay_column(column)
            .get_values_from_time(column)
            .get_all_values(
                date_min=date_min,
                date_max=date_max,
                remove_none=remove_none,
                related_column=related_column,
            )
        )

    def get_values_from_depth(
        self,
        column: str,
        depth_min: float,
        depth_max: float,
        remove_none: bool = False,
        related_column: str = "",
    ) -> Any:
        """
        Returns available values for assay with type depending on the plugin useds from a depth interval

        Args:
            column (str): assay column
            depth_min (float): minimum depth
            depth_max (float): maximum depth
            remove_none (bool): remove none values (default False)
            related_column:  (str) related assay column (default ""). Used if an assay column is stored as row
        related_column: str = ""
        Returns:
            Any: assay values, type depends on the plugin used
        """
        return (
            self._get_generic_assay_column(column)
            .get_values_from_depth(column)
            .get_all_values(
                date_min=depth_min,
                depth_max=depth_max,
                remove_none=remove_none,
                related_column=related_column,
            )
        )
