from enum import IntEnum
from typing import List, Optional

from qgis.core import QgsFeatureRequest, QgsVectorLayer
from qgis.PyQt import QtCore

from qgis.PyQt.QtCore import (
    QModelIndex,
)

from PreCourlis.core.interpolation_area import (
    InterpolationArea,
)


class Columns(IntEnum):
    FROM_PROFIL = 0
    TO_PROFIL_ID = 1
    LONGITUDINAL_STEP = 2


class InterpolationAreasTableModel(QtCore.QAbstractTableModel):
    """
    The table model for the interpolation areas table view
    """

    def __init__(self, parent=None):
        super().__init__(parent)

        self._interpolationAreas: List[InterpolationArea] = []
        self._layer: Optional[QgsVectorLayer] = None

    def interpolationAreas(self) -> List[InterpolationArea]:
        return self._interpolationAreas

    def setInterpolationAreas(self, interpolationAreas: List[InterpolationArea]):
        self.beginResetModel()
        self._interpolationAreas = interpolationAreas
        self.endResetModel()

    def setLayer(self, layer):
        self._layer = layer

    # QAbstractTableModel virtual methods
    def rowCount(self, parent=QtCore.QModelIndex()):
        return len(self._interpolationAreas)

    def columnCount(self, parent=QtCore.QModelIndex()):
        return len(Columns)

    def getProfilLabel(self, profil_id):
        if self._layer is None:
            return str(profil_id)

        request = QgsFeatureRequest()
        request.setFilterExpression(f'"sec_id" = {profil_id}')
        request.setFlags(QgsFeatureRequest.NoGeometry)

        try:
            feature = next(self._layer.getFeatures(request))
        except StopIteration:
            return str(profil_id)

        sec_id = feature.attribute("sec_id")
        name = feature.attribute("sec_name")
        abs_long = feature.attribute("abs_long")

        return f"{sec_id}: {name} ({round(abs_long, 3)})"

    def getFirstProfilSecId(self):
        if self._layer is None:
            return 0

        first_sec_id_request = (
            QgsFeatureRequest().addOrderBy('"sec_id"', ascending=True).setLimit(1)
        )
        first_feature_it = self._layer.getFeatures(first_sec_id_request)
        first_feature = next(first_feature_it)
        return first_feature["sec_id"]

    def getLastProfilSecId(self):
        if self._layer is None:
            return 0

        last_sec_id_request = (
            QgsFeatureRequest().addOrderBy('"sec_id"', ascending=False).setLimit(1)
        )
        last_feature_it = self._layer.getFeatures(last_sec_id_request)
        last_feature = next(last_feature_it)
        return last_feature["sec_id"]

    def data(self, index, role=QtCore.Qt.ItemDataRole.DisplayRole):
        if role == QtCore.Qt.ItemDataRole.DisplayRole:
            ia: InterpolationArea = self._interpolationAreas[index.row()]
            if index.column() == Columns.FROM_PROFIL:
                return self.getProfilLabel(ia.from_profil_id)
            elif index.column() == Columns.TO_PROFIL_ID:
                return self.getProfilLabel(ia.to_profil_id)
            elif index.column() == Columns.LONGITUDINAL_STEP:
                return str(ia.longitudinal_step)

        if role == QtCore.Qt.EditRole:
            ia: InterpolationArea = self._interpolationAreas[index.row()]
            if index.column() == Columns.FROM_PROFIL:
                return ia.from_profil_id
            elif index.column() == Columns.TO_PROFIL_ID:
                return ia.to_profil_id
            elif index.column() == Columns.LONGITUDINAL_STEP:
                return ia.longitudinal_step

    def headerData(self, section, orientation, role=QtCore.Qt.ItemDataRole.DisplayRole):
        if role == QtCore.Qt.ItemDataRole.DisplayRole:
            if orientation == QtCore.Qt.Horizontal:
                if section == Columns.FROM_PROFIL:
                    return self.tr("from_profil_id")
                elif section == Columns.TO_PROFIL_ID:
                    return self.tr("to_profil_id")
                elif section == Columns.LONGITUDINAL_STEP:
                    return self.tr("longitudinal_step")
            if orientation == QtCore.Qt.Vertical:
                return str(section)

    def setData(self, index, value, role):
        if index.isValid() and role == QtCore.Qt.EditRole:
            ia = self._interpolationAreas[index.row()]
            if index.column() == Columns.FROM_PROFIL:
                ia.from_profil_id = value
            elif index.column() == Columns.TO_PROFIL_ID:
                ia.to_profil_id = value
            elif index.column() == Columns.LONGITUDINAL_STEP:
                ia.longitudinal_step = value

            self.dataChanged.emit(index, index, [QtCore.Qt.EditRole])
            return True
        else:
            return False

    def flags(self, index):
        return (
            QtCore.Qt.ItemIsEnabled
            | QtCore.Qt.ItemIsEditable
            | QtCore.Qt.ItemIsSelectable
        )

    def insertRows(self, row: int, count: int, parent=QModelIndex()) -> bool:
        self.beginInsertRows(parent, row, row + count - 1)
        for i in range(row, count + row):
            from_profil_id = 0
            to_profil_id = 0
            value = 10.0

            if i == 0:
                from_profil_id = self.getFirstProfilSecId()
            else:
                from_profil_id = self._interpolationAreas[i - 1].to_profil_id

            if i == len(self._interpolationAreas):
                to_profil_id = self.getLastProfilSecId()
            else:
                to_profil_id = self._interpolationAreas[i].from_profil_id

            self._interpolationAreas.insert(
                i, InterpolationArea(from_profil_id, to_profil_id, value)
            )
        self.endInsertRows()

        return True

    def removeRows(self, row: int, count: int, parent=QModelIndex()) -> bool:
        self.beginRemoveRows(parent, row, row + count - 1)
        for i in range(count):
            self._interpolationAreas.pop(row + i)
        self.endRemoveRows()

        return True
