from pathlib import Path
from typing import List

from qgis.core import Qgis, QgsVectorLayer

from openlog.__about__ import DIR_PLUGIN_ROOT


class LayersInterface:
    """
    Interface for layers use.

    Get collar or collar trace layer :

    - :meth:`get_collar_layer`
    - :meth:`get_collar_trace_layer`

    Define path for default style for layers:

    - :meth:`get_collar_layer_style_file`
    - :meth:`get_collar_trace_layer_style_file`

    Get collar layer name:

    - :meth:`get_collar_layer_name`
    - :meth:`get_collar_trace_layer_name`

    Get selected collar :

    - :meth:`get_selected_collar_from_layer`

    Select collar :

    - :meth:`select_collar_by_id`

    By default, all functions are not implemented and raises :class:`InvalidInterface` exception.

    Unselect collar from current selection :

     - :meth:`unselect_collar_by_id`

    """

    class InvalidInterface(Exception):
        pass

    def __init__(self):
        # Store created collar layers, so we can access selected feature in widget only with OpenLogConnection interface
        self.collar_layer = None
        self.collar_trace_layer = None
        self.planned_trace_layer = None
        self.splitted_trace_layer = None

    def get_collar_layer(self) -> QgsVectorLayer:
        """
        Return collar QgsVectorLayer

        Returns:
            QgsVectorLayer: raises :class:`InvalidInterface` if not implemented
        """
        raise LayersInterface.InvalidInterface()

    def get_collar_trace_layer(self) -> QgsVectorLayer:
        """
        Return collar trace QgsVectorLayer

        Returns:
            QgsVectorLayer: raises :class:`InvalidInterface` if not implemented
        """
        raise LayersInterface.InvalidInterface()

    def get_planned_trace_layer(self) -> QgsVectorLayer:
        """
        Return planned trace QgsVectorLayer

        Returns:
            QgsVectorLayer: raises :class:`InvalidInterface` if not implemented
        """
        raise LayersInterface.InvalidInterface()

    def _set_clamping(self, layer: QgsVectorLayer) -> None:
        """
        Set elevation property to 'Absolute'
        """
        layer.elevationProperties().setClamping(Qgis.AltitudeClamping.Absolute)

    def is_trace_geometry_exist(self) -> bool:
        """
        Check if trace geometries exist.
        It could not exist if there are no surveys or desurveying have not been called.

        """
        lyr = self.get_collar_trace_layer()
        if lyr is None:
            return False
        """
        Avoid RuntimeError: wrapped C/C++ object of type QgsVectorLayer has been deleted.
        Raised when closing QGIS
        """
        try:
            valid = 0
            for ftr in lyr.getFeatures():
                valid += not ftr.geometry().isEmpty()
        except RuntimeError:
            valid = 0

        return valid > 0

    def get_splitted_trace_layer(self) -> QgsVectorLayer:
        """
        Return splitted trace QgsVectorLayer

        Returns:
            QgsVectorLayer: raises :class:`InvalidInterface` if not implemented
        """
        raise LayersInterface.InvalidInterface()

    def get_collar_layer_style_file(self) -> Path:
        """
        Get QGIS file style for collar layer

        Returns:
            Path: path to QGIS style file
        """
        return DIR_PLUGIN_ROOT / "resources" / "styles" / "collar.qml"

    def get_collar_trace_layer_style_file(self) -> Path:
        """
        Get QGIS file style for collar trace layer

        Returns:
            Path: path to QGIS style file
        """
        return DIR_PLUGIN_ROOT / "resources" / "styles" / "collar_trace.qml"

    def get_planned_trace_layer_style_file(self) -> Path:
        """
        Get QGIS file style for collar trace layer

        Returns:
            Path: path to QGIS style file
        """
        return DIR_PLUGIN_ROOT / "resources" / "styles" / "planned_trace.qml"

    def get_collar_layer_name(self) -> str:
        """
        Get collar layer name

        Returns:
            str: collar layer name
        """
        return self.tr("Collar")

    def get_collar_trace_layer_name(self) -> str:
        """
        Get collar trace layer name

        Returns:
            str: collar trace layer name
        """
        return self.tr("Effective trace")

    def get_planned_trace_layer_name(self) -> str:
        """
        Get planned trace layer name

        Returns:
            str: collar trace layer name
        """
        return self.tr("Planned trace")

    def get_selected_collar_from_layer(self) -> List[str]:
        """
        Get selected collar id from QGIS layer

        Returns:
            List[str]: selected collar id
        """
        collar_layer = self.get_collar_layer()
        if collar_layer is not None:
            res = [f["hole_id"] for f in collar_layer.selectedFeatures()]
        else:
            res = []
        return res

    def select_collar_by_id(self, hole_ids: List[str]) -> None:
        """
        Select collar by ID in collar layer

        Args:
            hole_ids: List[str]: selected collar id
        """
        collar_layer = self.get_collar_layer()
        if collar_layer is not None:
            in_ = ",".join(hole_ids)
            collar_layer.selectByExpression(f'"hole_id" IN ({in_})')

    def unselect_collar_by_id(self, hole_ids: List[str]) -> None:
        """
        Unselect collar by ID in collar layer

        Args:
            hole_ids: List[str]: selected collar id
        """
        collar_layer = self.get_collar_layer()
        if collar_layer is not None:
            in_ = ",".join(["'" + hole + "'" for hole in hole_ids])
            collar_layer.selectByExpression(
                f'"hole_id" IN ({in_})',
                behavior=Qgis.SelectBehavior.RemoveFromSelection,
            )
