import os
from typing import List

from qgis.core import QgsCoordinateReferenceSystem
from qgis.PyQt import uic
from qgis.PyQt.QtCore import QSize, Qt
from qgis.PyQt.QtGui import QIcon, QStandardItem, QStandardItemModel
from qgis.PyQt.QtWidgets import QDialog, QHeaderView, QMessageBox
from xplordb.datamodel.collar import Collar

from openlog.__about__ import DIR_PLUGIN_ROOT
from openlog.datamodel.connection.interfaces.write_interface import WriteInterface
from openlog.datamodel.connection.openlog_connection import OpenLogConnection
from openlog.toolbelt import PlgLogger


class CollarEditionModel(QStandardItemModel):
    HOLE_ID_COL = 0
    X_COL = 1
    Y_COL = 2
    Z_COL = 3
    EOH_COL = 4
    PLANNED_X_COL = 5
    PLANNED_Y_COL = 6
    PLANNED_Z_COL = 7
    PLANNED_EOH_COL = 8
    SRID_COL = 9

    def __init__(self, parent=None):
        super(QStandardItemModel, self).__init__(parent)
        self.setHorizontalHeaderLabels(
            [
                "HoleID",
                "Easting",
                "Northing",
                "Elevation",
                "EOH",
                "Pld. East.",
                "Pld. North.",
                "Pld. Eleva.",
                "Pld. EOH",
                "SRID",
            ]
        )
        self.log = PlgLogger().log
        self.itemChanged.connect(lambda item: self._item_changed(item))

    def _item_changed(self, item: QStandardItem):
        """
        Check incoherent values.
        """
        # eoh and planned_eoh > 0
        if item.column() in [self.EOH_COL, self.PLANNED_EOH_COL]:
            value = self.data(self.index(item.row(), item.column()))
            try:
                if value is not None and float(value) <= 0:
                    self.setData(self.index(item.row(), item.column()), "")
            except ValueError:
                self.setData(self.index(item.row(), item.column()), "")

        # valid srid
        if item.column() == self.SRID_COL:
            value = self.data(self.index(item.row(), item.column()))
            try:
                is_valid = QgsCoordinateReferenceSystem.fromEpsgId(int(value)).isValid()
                if not is_valid:
                    self.setData(self.index(item.row(), item.column()), "")
            except (TypeError, ValueError):
                self.setData(self.index(item.row(), item.column()), "")

    def _collar(self, row: int) -> Collar:
        """
        Get collar definition for specific row

        Args:
            row: model row

        Returns: Collar for specific row

        """
        collar = Collar(
            hole_id=self.data(self.index(row, self.HOLE_ID_COL)),
            data_set="",  # Data set must be defined by widget using model
            loaded_by="",  # Loaded by person must be defined by widget using model
            x=self.data(self.index(row, self.X_COL)),
            y=self.data(self.index(row, self.Y_COL)),
            z=self.data(self.index(row, self.Z_COL)),
            eoh=self.data(self.index(row, self.EOH_COL)),
            srid=self.data(self.index(row, self.SRID_COL)),
            planned_x=self.data(self.index(row, self.PLANNED_X_COL)),
            planned_y=self.data(self.index(row, self.PLANNED_Y_COL)),
            planned_z=self.data(self.index(row, self.PLANNED_Z_COL)),
            planned_eoh=self.data(self.index(row, self.PLANNED_EOH_COL)),
        )
        return collar

    def add_collar(self, collar: Collar) -> None:
        """
        Insert a row describing collar.
        """
        self.insertRow(self.rowCount())
        row = self.rowCount() - 1
        self.setData(self.index(row, self.HOLE_ID_COL), collar.hole_id)
        item = self.item(row, self.HOLE_ID_COL)
        item.setFlags(item.flags() & ~Qt.ItemIsEditable)
        self.setData(self.index(row, self.X_COL), collar.x)
        self.setData(self.index(row, self.Y_COL), collar.y)
        self.setData(self.index(row, self.Z_COL), collar.z)
        self.setData(self.index(row, self.EOH_COL), collar.eoh)
        self.setData(self.index(row, self.PLANNED_X_COL), collar.planned_x)
        self.setData(self.index(row, self.PLANNED_Y_COL), collar.planned_y)
        self.setData(self.index(row, self.PLANNED_Z_COL), collar.planned_z)
        self.setData(self.index(row, self.PLANNED_EOH_COL), collar.planned_eoh)
        self.setData(self.index(row, self.SRID_COL), collar.srid)

    def get_collar_list(self) -> List[Collar]:
        """
        Get collar list.

        Returns: collar list

        """
        result = []
        for i in range(0, self.rowCount()):
            result.append(self._collar(i))
        return result


class CollarEditionDialog(QDialog):
    def __init__(self, openlog_connection: OpenLogConnection, parent=None) -> None:
        """
        QDialog to edit existing collar

        Args:
            openlog_connection: OpenLogConnection interface for data import
            parent: QWidget parent
        """
        super(CollarEditionDialog, self).__init__(parent)
        uic.loadUi(
            os.path.join(os.path.dirname(__file__), "collar_edition_dialog.ui"), self
        )

        self._openlog_connection = openlog_connection

        # translation

        self.setWindowTitle("Collar edition")

        self._model = CollarEditionModel()
        self.collar_edition_view.setModel(self._model)
        self.collar_edition_view.horizontalHeader().setSectionResizeMode(
            QHeaderView.Stretch
        )
        self.selection_model = self.collar_edition_view.selectionModel()
        self.selection_model.selectionChanged.connect(self._on_selection)
        self.remove_btn.setIcon(
            QIcon(str(DIR_PLUGIN_ROOT / "resources" / "images" / "icon_db_delete.svg"))
        )
        self.remove_btn.setFixedSize(30, 30)
        self.remove_btn.setIconSize(QSize(22, 22))
        self.remove_btn.pressed.connect(self._on_remove)
        self.remove_btn.setToolTip("Remove selected collar(s) from database")
        self.remove_btn.setEnabled(False)
        self._selected_collars = []
        self.updated_collars = []

    def _on_selection(self):
        """
        Disable/enable remove button.
        """
        selected = self.selection_model.selectedRows()
        self.remove_btn.setEnabled(len(selected) > 0)
        self.status.setText("")

    def _on_remove(self):
        """
        Slot for collar deletion.
        """
        selected = self.selection_model.selectedRows()
        collar_ids = [index.data() for index in selected]
        if len(collar_ids) == 0:
            return

        warning = QMessageBox(self)
        warning.setWindowTitle("Collar deletion")
        warning.setText("Deleted collar(s) will be lost forever.")
        warning.setInformativeText("Do you want to continue ?")
        warning.setStandardButtons(QMessageBox.No | QMessageBox.Yes)
        warning.setIcon(QMessageBox.Warning)
        res = warning.exec()
        if res == QMessageBox.No:
            return

        self.status.setText(f"Deleting collars : {', '.join(collar_ids)}")
        delete = self._openlog_connection.get_write_iface().remove_collars(collar_ids)

        if delete:
            self.status.setText(
                f"Collars : {', '.join(collar_ids)} have been successfully deleted from database."
            )
            # refresh table
            rows = [index.row() for index in selected]
            for row in sorted(rows, reverse=True):
                self._model.removeRow(row)
        else:
            self.status.setText(
                f"Collars : {', '.join(collar_ids)} have not been successfully deleted."
            )

    def _get_edited_collars(self, collar_list: list) -> List[Collar]:
        """
        Compare collar_list with selected_collars and return only edited collars.
        """

        result = []
        attributes = [
            "x",
            "y",
            "z",
            "eoh",
            "planned_x",
            "planned_y",
            "planned_z",
            "planned_eoh",
            "srid",
        ]
        for collar in collar_list:
            original_collar = [
                col for col in self._selected_collars if col.hole_id == collar.hole_id
            ][0]
            for attr in attributes:
                if getattr(original_collar, attr) != getattr(collar, attr):
                    result.append(collar)
                    break

        return result

    def set_selected_collar(self, collar_str: List[str]):

        collar_list = [
            self._openlog_connection.get_read_iface().get_collar(id)
            for id in collar_str
        ]
        self._selected_collars = collar_list

        for collar in collar_list:
            self._model.add_collar(collar)

    def accept(self):
        """
        Update collar from current collar edition widget

        """
        valid = True
        try:
            collars = self._model.get_collar_list()
            updated_collars = self._get_edited_collars(collars)
            self.updated_collars = updated_collars
            self._openlog_connection.get_write_iface().update_collars(updated_collars)
            self._openlog_connection.commit()
        except WriteInterface.ImportException as exc:
            valid = False
            QMessageBox.warning(self, "Import exception", str(exc))
        if valid:
            super(CollarEditionDialog, self).accept()
