import json

from qgis.PyQt.QtCore import QObject

from qgis.core import (
    QgsVectorLayer,
    QgsFeatureRequest,
    QgsFeature,
)
from dataclasses import dataclass

from typing import List


@dataclass
class InterpolationArea:
    """InterpolationArea data class, used for partial interpolation of a reach"""

    from_profil_id: int = 0
    to_profil_id: int = 0
    longitudinal_step: float = 1.0

    def toDict(self):
        dict_ = self.__dict__.copy()
        return dict_

    @classmethod
    def fromDict(cls, dict_):
        return cls(**dict_)

    def copy(self):
        return InterpolationArea.fromDict(self.toDict())


class InterpolationAreaManager(QObject):
    """InterpolationArea manager class allowing to read and write interpolation_areas
    custom property of a layer"""

    INTERPOLATION_AREAS_KEY = "interpolation_areas"

    @classmethod
    def readInterpolationAreasFromLayer(
        cls, layer: QgsVectorLayer
    ) -> List[InterpolationArea]:
        if layer is None:
            return []

        str_value = layer.customProperty(cls.INTERPOLATION_AREAS_KEY)

        interpolation_areas = []
        if str_value:
            for dict_ in json.loads(str_value):
                interpolation_areas.append(InterpolationArea.fromDict(dict_))
        return interpolation_areas

    @classmethod
    def writeInterpolationAreasOfLayer(
        cls, layer: QgsVectorLayer, interpolation_areas: List[InterpolationArea]
    ) -> bool:
        if layer is None:
            return False

        # convert List[List[float]] to List[InterpolationArea]
        layer.setCustomProperty(
            cls.INTERPOLATION_AREAS_KEY,
            json.dumps([ia.toDict() for ia in interpolation_areas]),
        )

        return True

    @classmethod
    def validateInterpolationAreasOfLayer(cls, interpolation_areas, layer):
        COUNT = len(interpolation_areas)
        if COUNT == 0:
            return (
                False,
                "There is no interpolation areas!",
            )

        # Check first id
        first_sec_id_request = (
            QgsFeatureRequest().addOrderBy('"sec_id"', ascending=True).setLimit(1)
        )
        first_feature_it = layer.getFeatures(first_sec_id_request)
        first_feature = QgsFeature()
        first_feature_it.nextFeature(first_feature)

        if interpolation_areas[0].from_profil_id != first_feature["sec_id"]:
            return (
                False,
                f"First area's from_profil_id ({interpolation_areas[0].from_profil_id})"
                + f" should be the first section id: {first_feature['sec_id']}",
            )

        for i in range(0, COUNT):
            # Check from_profil_id lower than to_profil_id
            if (
                interpolation_areas[i].from_profil_id
                > interpolation_areas[i].to_profil_id
            ):
                return (
                    False,
                    f"Area number {i} has a lower to_profil_id than from_profil_id.",
                )

            # Check there is "hole" between two areas
            if (i < (COUNT - 1)) and (
                interpolation_areas[i].to_profil_id
                != interpolation_areas[i + 1].from_profil_id
            ):
                return (
                    False,
                    f"Area number {i + 1} has a from_profil_id different than the "
                    + "previous area's to_profil_id.",
                )

        # Check last id
        last_sec_id_request = (
            QgsFeatureRequest().addOrderBy('"sec_id"', ascending=False).setLimit(1)
        )
        last_feature_it = layer.getFeatures(last_sec_id_request)
        last_feature = QgsFeature()
        last_feature_it.nextFeature(last_feature)

        if interpolation_areas[COUNT - 1].to_profil_id != last_feature["sec_id"]:
            return (
                False,
                f"Last area's to_profil_id should be the last section id: {last_feature['sec_id']}",
            )

        return True, ""
