from qgis.PyQt import QtCore
from qgis.PyQt.QtCore import QVariant
from qgis.PyQt.QtGui import QFont, QStandardItemModel

from openlog.datamodel.assay.uncertainty import AssayColumnUncertainty, UncertaintyType


class AssayColumnUncertaintyTableModel(QStandardItemModel):
    COLUMN_COL = 0
    UPPER_WHISKER_BOX_COLUMN = 1
    LOWER_WHISKER_BOX_COLUMN = 2
    UPPER_BOX_COLUMN = 3
    LOWER_BOX_COLUMN = 4

    def __init__(self, parent=None) -> None:
        """
        QStandardItemModel for column definition display

        Args:
            parent: QWidget
        """
        super().__init__(parent)
        self.set_uncertainty_type(UncertaintyType.ONE_COLUMN)

    def set_uncertainty_type(self, uncertainty_type: UncertaintyType) -> None:
        """
        Define columns name depending on uncertainty type

        Args:
            uncertainty_type: UncertaintyType
        """
        if uncertainty_type == UncertaintyType.ONE_COLUMN:
            self.setHorizontalHeaderLabels(
                [
                    self.tr("Column"),
                    self.tr("Wide interval (Δ)"),
                ]
            )
        elif uncertainty_type == UncertaintyType.TWO_COLUMN:
            self.setHorizontalHeaderLabels(
                [
                    self.tr("Column"),
                    self.tr("Max wide interval (Δmax)"),
                    self.tr("Min wide interval (Δmin)"),
                ]
            )
        elif uncertainty_type == UncertaintyType.FOUR_COLUMN:
            self.setHorizontalHeaderLabels(
                [
                    self.tr("Column"),
                    self.tr("Max wide interval (Δmax)"),
                    self.tr("Min wide interval (Δmin)"),
                    self.tr("Max narrow interval (δmax)"),
                    self.tr("Min narrow interval (δmin)"),
                ]
            )

    def flags(self, index: QtCore.QModelIndex) -> QtCore.Qt.ItemFlags:
        """
        Override QStandardItemModel flags to remove edition for column col

        Args:
            index: QModelIndex

        Returns: index flags

        """
        flags = super().flags(index)
        if index.column() == self.COLUMN_COL:
            flags = flags & QtCore.Qt.ItemIsEditable
        return flags

    def data(
        self, index: QtCore.QModelIndex, role: int = QtCore.Qt.DisplayRole
    ) -> QVariant:
        """
        Override QStandardItemModel data() for font of expected column

        Args:
            index: QModelIndex
            role: Qt role

        Returns: QVariant

        """
        result = super().data(index, role)
        if role == QtCore.Qt.FontRole and index.column() == self.COLUMN_COL:
            result = QFont()
            result.setBold(True)

        return result

    def add_assay_column_uncertainty(
        self, column: str, uncertainty: AssayColumnUncertainty
    ) -> None:
        """
        Add a row in QStandardItemModel

        Args:
            column : (str) column name
            uncertainty: AssayColumnUncertainty
        """
        self.insertRow(self.rowCount())
        row = self.rowCount() - 1
        self._set_assay_column_uncertainty(row, column, uncertainty)

    def remove_column(self, column: str) -> None:
        """
        Remove a column uncertainty definition

        Args:
            column: (str) column name

        """
        for i in range(self.rowCount()):
            column_row = str(self.data(self.index(i, self.COLUMN_COL)))
            if column_row == column:
                self.removeRow(i)
                return

    def set_assay_column_uncertainty(
        self, column_uncertainty: {str: AssayColumnUncertainty}
    ) -> None:
        """
        Define expected column and remove not used column

        Args:
            column_uncertainty:
        """
        columns_name = column_uncertainty.keys()
        for i in reversed(range(self.rowCount())):
            expected_col = str(self.data(self.index(i, self.COLUMN_COL)))
            if expected_col not in columns_name:
                self.removeRow(i)

        for col, uncertainty in column_uncertainty.items():
            row_index = self._get_column_row_index(col)
            if row_index != -1:
                self._set_assay_column_uncertainty(row_index, col, uncertainty)
            else:
                self.add_assay_column_uncertainty(col, uncertainty)

    def get_assay_column_uncertainty(self) -> {str: AssayColumnUncertainty}:
        """
        Return dict of AssayColumnUncertainty

        Returns: {str: AssayColumnUncertainty}

        """
        result = {}
        for i in range(self.rowCount()):
            expected_col = str(self.data(self.index(i, self.COLUMN_COL)))
            if expected_col:
                upper_whisker_box_colum = self.data(
                    self.index(i, self.UPPER_WHISKER_BOX_COLUMN)
                )
                lower_whisker_box_colum = self.data(
                    self.index(i, self.LOWER_WHISKER_BOX_COLUMN)
                )
                upper_box_colum = self.data(self.index(i, self.UPPER_BOX_COLUMN))
                lower_box_colum = self.data(self.index(i, self.LOWER_BOX_COLUMN))
                column = AssayColumnUncertainty(
                    upper_whisker_column=str(upper_whisker_box_colum)
                    if upper_whisker_box_colum
                    else "",
                    lower_whisker_column=str(lower_whisker_box_colum)
                    if lower_whisker_box_colum
                    else "",
                    upper_box_column=str(upper_box_colum) if upper_box_colum else "",
                    lower_box_column=str(lower_box_colum) if lower_box_colum else "",
                )
                result[expected_col] = column
        return result

    def _set_assay_column_uncertainty(
        self, row: int, column: str, uncertainty: AssayColumnUncertainty
    ) -> None:
        self.setData(self.index(row, self.COLUMN_COL), column)

        self.setData(
            self.index(row, self.UPPER_WHISKER_BOX_COLUMN),
            uncertainty.upper_whisker_column,
        )
        self.setData(
            self.index(row, self.LOWER_WHISKER_BOX_COLUMN),
            uncertainty.lower_whisker_column,
        )
        self.setData(
            self.index(row, self.UPPER_BOX_COLUMN), uncertainty.upper_box_column
        )
        self.setData(
            self.index(row, self.LOWER_BOX_COLUMN), uncertainty.lower_box_column
        )

    def _get_column_row_index(self, col: str) -> int:
        """
        Get expected column column index (-1 if expected column not available)

        Args:
            col: (str) expected column

        Returns: expected column column index (-1 if expected column not available)

        """
        row = -1
        for i in range(self.rowCount()):
            val = str(self.data(self.index(i, self.COLUMN_COL)))
            if val == col:
                row = i
        return row

    def _get_or_create_column(self, col: str) -> int:
        """
        Get or create column

        Args:
            col: expected column name

        Returns: column index

        """
        row = self._get_column_row_index(col)
        if row == -1:
            self.add_assay_column_uncertainty(
                column=col, uncertainty=AssayColumnUncertainty()
            )
            row = self.rowCount() - 1
        return row
