import os

from qgis.PyQt import QtCore, uic
from qgis.PyQt.QtGui import QIcon
from qgis.PyQt.QtWidgets import QAbstractItemView, QWidget

from openlog.__about__ import DIR_PLUGIN_ROOT
from openlog.datamodel.assay.generic_assay import AssayDataExtent, AssaySeriesType
from openlog.gui.assay_visualization.composition.creation_table_model import (
    CreationTableModel,
)
from openlog.gui.assay_visualization.multiplot.multiplot_config import (
    MultiplotConfiguration,
)
from openlog.gui.assay_visualization.multiplot.multiplot_utils import (
    invalid_series_type,
)
from openlog.plugins.manager import get_plugin_manager


class ConfigCreationWidget(QWidget):
    add_multiplot_config = QtCore.pyqtSignal()
    create_cross_plot = QtCore.pyqtSignal()
    create_stacked_chart = QtCore.pyqtSignal()

    def __init__(self, parent=None) -> None:
        """
        QWidget to display multiplot configuration creation.

        Use StackConfigTableModel for table definition

        Args:
            parent: QWidget parent
        """
        super().__init__(parent)

        uic.loadUi(
            os.path.dirname(os.path.realpath(__file__)) + "/creation_widget.ui",
            self,
        )

        self.multiplot_config_table_model = CreationTableModel(self)
        self.tableView.setModel(self.multiplot_config_table_model)
        self.tableView.setEditTriggers(QAbstractItemView.NoEditTriggers)

        # Check configuration for creation button enable
        self.create_multiplot_btn.setEnabled(False)
        self.create_multiplot_btn.setIcon(
            QIcon(str(DIR_PLUGIN_ROOT / "resources" / "images" / "stacked_curves.svg"))
        )
        self.create_multiplot_btn.setIconSize(QtCore.QSize(40, 40))
        self.create_multiplot_btn.setToolTip("Create multiplot")
        self.multiplot_config_table_model.dataChanged.connect(
            self._check_multiplot_config
        )
        self.create_multiplot_btn.clicked.connect(
            lambda: self.add_multiplot_config.emit()
        )

        # cross plot button
        self.crossplot_enabled = get_plugin_manager().get_crossplots_plugin().enable
        self.create_crossplot_btn.setIcon(
            QIcon(str(DIR_PLUGIN_ROOT / "resources" / "images" / "scatterplot.svg"))
        )
        self.create_crossplot_btn.setIconSize(QtCore.QSize(40, 40))
        self.create_crossplot_btn.setToolTip("Create crossplot")
        self.create_crossplot_btn.setVisible(True)
        if not self.crossplot_enabled:
            self.create_crossplot_btn.setEnabled(False)
            self.create_crossplot_btn.setToolTip("Create crossplot (Premium only)")

        self.create_crossplot_btn.clicked.connect(lambda: self.create_cross_plot.emit())

        # stacked plot button
        self.stacked_enabled = get_plugin_manager().get_stacked_charts_plugin().enable
        self.create_stacked_btn.setIcon(
            QIcon(str(DIR_PLUGIN_ROOT / "resources" / "images" / "stacked_bars.svg"))
        )
        self.create_stacked_btn.setIconSize(QtCore.QSize(40, 40))
        self.create_stacked_btn.setToolTip("Create stacked plot")
        self.create_stacked_btn.setVisible(True)
        if not self.stacked_enabled:
            self.create_stacked_btn.setEnabled(False)
            self.create_stacked_btn.setToolTip("Create stacked plot (Premium only)")

        self.create_stacked_btn.clicked.connect(
            lambda: self.create_stacked_chart.emit()
        )

    def set_multiplot_configuration(self, config: MultiplotConfiguration) -> None:
        """
        Define displayed MultiplotConfiguration

        Args:
            config: MultiplotConfiguration
        """
        self.multiplot_config_table_model.dataChanged.disconnect(
            self._check_multiplot_config
        )
        self.multiplot_config_table_model.set_multiplot_config(config)
        self.multiplot_config_table_model.dataChanged.connect(
            self._check_multiplot_config
        )
        self._check_multiplot_config()

    def get_multiplot_configuration(self) -> MultiplotConfiguration:
        """
        Returns display MultiplotConfiguration

        Returns: MultiplotConfiguration

        """
        return self.multiplot_config_table_model.get_multiplot_config()

    def _check_multiplot_config(self) -> None:
        """
        Check if MultiplotConfiguration is valid and enable/disable create button

        """

        # enable/disable crossplot button
        self._enable_crossplot_btn()
        # enable/disable crossplot button
        self._enable_stacked_btn()

        multiplot_config = self.get_multiplot_configuration()
        self.create_multiplot_btn.setEnabled(multiplot_config.is_valid())
        if not multiplot_config.is_valid():
            invalid_series_name = ",".join([s.name for s in invalid_series_type()])
            self.create_multiplot_btn.setToolTip(
                f"Invalid multiplot. Check if multiple unit or series type are used. ({invalid_series_name}) series types are not supported"
            )
        else:
            self.create_multiplot_btn.setToolTip("Create multiplot")

    def _enable_crossplot_btn(self) -> None:
        """
        Check if a crossplot is possible.
        """
        if not self.crossplot_enabled:
            return

        s = self.get_multiplot_configuration()
        variables = list(
            set([config.assay.assay_definition.variable for config in s.config_list])
        )
        columns = list(set([config.column.name for config in s.config_list]))
        extents = list(
            set([config.assay.assay_definition.data_extent for config in s.config_list])
        )
        serie_type = list(set([config.column.series_type for config in s.config_list]))
        only_discrete = len(extents) == 1 and extents[0] == AssayDataExtent.DISCRETE
        only_numeric = (
            len(serie_type) == 1 and serie_type[0] == AssaySeriesType.NUMERICAL
        )
        valid = (
            len(s.config_list) > 1
            and len(variables) == 1
            and len(columns) == 2
            and only_numeric
            and only_discrete
        )
        self.create_crossplot_btn.setEnabled(valid)

    def _enable_stacked_btn(self) -> None:
        """
        Check if a stacked plot is possible.
        """
        if not self.crossplot_enabled:
            return

        s = self.get_multiplot_configuration()
        variables = list(
            set([config.assay.assay_definition.variable for config in s.config_list])
        )
        columns = list(set([config.column.name for config in s.config_list]))
        serie_type = list(set([config.column.series_type for config in s.config_list]))
        only_numeric = (
            len(serie_type) == 1 and serie_type[0] == AssaySeriesType.NUMERICAL
        )
        n_units = list(set([config.column.unit for config in s.config_list]))
        collars = list(
            set([config.hole_id for config in s.config_list if config.hole_id != ""])
        )
        valid = (
            len(s.config_list) > 1
            and len(variables) == 1
            and len(columns) > 1
            and only_numeric
            and len(n_units) == 1
            and len(collars) == 1
        )
        self.create_stacked_btn.setEnabled(valid)
