# SPDX-FileCopyrightText: 2025 XLeitstelle Planen und Bauen <xleitstelle@gv.hamburg.de>
# SPDX-FileContributor: Tobias Kraft <tobias.kraft@gv.hamburg.de>
#
# SPDX-License-Identifier: EUPL-1.2

from qgis.analysis import QgsGeometrySnapper
from qgis.core import (
    Qgis,
    QgsApplication,
    QgsFeature,
    QgsMapLayer,
    QgsVectorLayer,
)
from qgis.gui import (
    QgsMapLayerAction,
    QgsMapLayerActionContext,
)
from qgis.PyQt.QtCore import QObject, pyqtSlot
from qgis.PyQt.QtWidgets import QInputDialog
from qgis.utils import iface

from xmas_plugin.util.metadata import PLUGIN_DIR_NAME, PLUGIN_NAME


class SnapGeometry(QgsMapLayerAction):
    def __init__(self, parent: QObject | None = None):
        super().__init__(
            "Snap auf aktiven Layer",
            parent,
            Qgis.MapLayerActionTarget.SingleFeature,
            QgsApplication.instance().getThemeIcon("mIconTopologicalEditing.svg"),
            Qgis.MapLayerActionFlags(),
        )
        self.triggeredForFeatureV2.connect(self._trigger_for_feature)

    def canRunUsingLayer(
        self,
        layer: QgsMapLayer | None,
        _: QgsMapLayerActionContext = QgsMapLayerActionContext(),
    ) -> bool:
        return (
            layer.customProperty(f"{PLUGIN_DIR_NAME}/layer_type") is not None
            and layer.isEditable()
            if layer
            else False
        )

    @pyqtSlot("QgsMapLayer*", QgsFeature, QgsMapLayerActionContext)
    def _trigger_for_feature(
        self,
        layer: QgsMapLayer,
        feature: QgsFeature,
        _: QgsMapLayerActionContext,
    ) -> None:
        snap_layer = iface.activeLayer()
        if not isinstance(snap_layer, QgsVectorLayer):
            iface.messageBar().pushWarning(
                PLUGIN_NAME, "Snap erfordert aktiven Vektorlayer"
            )
            return
        snapper = QgsGeometrySnapper(snap_layer)
        try:
            uom = snap_layer.crs().mapUnits().name
            tolerance, ok = QInputDialog().getDouble(
                iface.mainWindow(),
                PLUGIN_NAME,
                f"Snapping-Toleranz [{uom}]:",
                value=1,
                min=0,
                max=100,
                decimals=3,
            )
            if not ok:
                return
            geometries = [
                snap_feature.geometry()
                for snap_feature in snap_layer.getFeatures(
                    feature.geometry().boundingBox()
                )
                if snap_feature.attributeMap().get("id") != feature["id"]
            ]
            snapped = snapper.snapGeometry(
                feature.geometry(),
                tolerance,
                geometries,
                QgsGeometrySnapper.SnapMode.PreferNodesNoExtraVertices,
            )
            if not feature.geometry().equals(snapped):
                layer.beginEditCommand("geometry snapped")
                try:
                    layer.changeGeometry(feature.id(), snapped)
                finally:
                    layer.endEditCommand()

                msg = f"Feature auf Layer {snap_layer.name()} gesnappt."
                iface.messageBar().pushSuccess(
                    PLUGIN_NAME,
                    msg,
                )
                QgsApplication.messageLog().logMessage(
                    f"{msg}\nFeature-ID: {feature['id']}\nToleranz: {tolerance} [{uom}]",
                    PLUGIN_NAME,
                    Qgis.Info,
                )
            else:
                iface.messageBar().pushInfo(
                    PLUGIN_NAME,
                    f"Keine snapbaren Geometrien innerhalb der Toleranz {tolerance} gefunden",
                )
        except Exception as e:
            iface.messageBar().pushCritical(PLUGIN_NAME, f"Fehler beim Snappen: {e!r}")
