# SPDX-FileCopyrightText: 2025 XLeitstelle Planen und Bauen <xleitstelle@gv.hamburg.de>
# SPDX-FileContributor: Tobias Kraft <tobias.kraft@gv.hamburg.de>
# SPDX-FileContributor: Anton Jacobsson <anton.jacobsson@init.de>
#
# SPDX-License-Identifier: EUPL-1.2
import logging
from typing import cast

from qgis.core import (
    Qgis,
    QgsApplication,
    QgsEditError,
    QgsFeature,
    QgsGeometry,
    QgsMapLayer,
    QgsProject,
    QgsVectorLayer,
    QgsVectorLayerUtils,
    edit,
)
from qgis.gui import (
    QgsAttributeEditorContext,
    QgsMapLayerAction,
    QgsMapLayerActionContext,
    QgsMapToolCapture,
)
from qgis.PyQt.QtCore import QObject, pyqtSlot
from qgis.utils import iface

from xmas_plugin.actions.common import GeometryCaptureTool
from xmas_plugin.util.metadata import PLUGIN_DIR_NAME, PLUGIN_NAME

logger = logging.getLogger(PLUGIN_DIR_NAME)


class AddPO(QgsMapLayerAction):
    def __init__(self, parent: QObject | None = None):
        super().__init__(
            "Präsentationsobjekt hinzufügen",
            parent,
            Qgis.MapLayerActionTarget.SingleFeature,
            QgsApplication.instance().getThemeIcon("title_label.svg"),
            Qgis.MapLayerActionFlags(),
        )
        self.current_tool = None
        self.layer = None
        self.feature = None
        self.target_layer = None
        self.dlg = None
        self.digit_tool = None
        self.triggeredForFeatureV2.connect(self._setup)

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

    @pyqtSlot("QgsMapLayer*", QgsFeature, QgsMapLayerActionContext)
    def _setup(
        self,
        layer: QgsMapLayer,
        feature: QgsFeature,
        _: QgsMapLayerActionContext,
    ) -> None:
        if not layer:
            logger.debug(f"Layer {layer} None, aborting adding po.")
            return
        self.layer = cast(QgsVectorLayer, layer)

        if not feature:
            logger.debug(f"Feature {feature} None, aborting adding po.")
            return
        self.feature = feature

        for map_layer in QgsProject.instance().mapLayers().values():
            if (
                map_layer.customProperty(f"{PLUGIN_DIR_NAME}/layer_type")
                == "presentation"
                and map_layer.geometryType().name == "Point"
                and map_layer.customProperty(f"{PLUGIN_DIR_NAME}/bereich_id")
                == self.layer.customProperty(f"{PLUGIN_DIR_NAME}/bereich_id")
            ):
                self.target_layer = cast(QgsVectorLayer, map_layer)
                break

        if not self.target_layer:
            return iface.pushCritical(
                PLUGIN_NAME,
                "kein passender Präsentationsobjekte-Layer gefunden",
            )

        canvas = iface.mapCanvas()
        self.current_tool = canvas.mapTool()

        # Instantiate a new tool each time — avoids stale C++ pointers to canvas/CAD dock
        self.digit_tool = GeometryCaptureTool(
            canvas,
            iface.cadDockWidget(),
            QgsMapToolCapture.CaptureMode.CapturePoint,
            self._handle_digitizing,
        )

        canvas.setMapTool(self.digit_tool)
        iface.messageBar().pushInfo(
            PLUGIN_NAME, "Geometrie für Präsentationsobjekt erfassen"
        )

    @pyqtSlot(QgsGeometry)
    def _handle_digitizing(
        self,
        digit_geom: QgsGeometry | None,
    ):
        if not digit_geom:
            logger.debug(
                f"AddPo._handle_digitizing(): digit_feature {digit_geom} None, aborting adding PO."
            )
            self._cleanup()
            return

        try:
            feature = QgsVectorLayerUtils.createFeature(
                self.target_layer,
                digit_geom,
                {
                    self.target_layer.fields().indexFromName("properties"): {
                        "dientZurDarstellungVon": [self.feature["id"]],
                    },
                },
            )
            self.dlg = iface.getFeatureForm(self.target_layer, feature)
            self.dlg.setMode(QgsAttributeEditorContext.Mode.AddFeatureMode)

            try:
                if self.target_layer.isEditable():
                    accepted = self.dlg.exec()
                else:
                    with edit(self.target_layer):
                        accepted = self.dlg.exec()
            except QgsEditError as e:
                msg = "Fehler beim Speichern des Präsentationsobjekts"
                iface.messageBar().pushCritical(PLUGIN_NAME, msg)
                QgsApplication.messageLog().logMessage(
                    f"{msg}: {e!r}",
                    PLUGIN_NAME,
                    Qgis.Critical,
                )
                return
            if accepted:
                iface.messageBar().pushSuccess(
                    PLUGIN_NAME, "Präsentationsobjekt hinzugefügt"
                )
        except Exception as e:
            msg = "Fehler beim Hinzufügen des Präsentationsobjekts"
            iface.messageBar().pushCritical(PLUGIN_NAME, msg)
            QgsApplication.messageLog().logMessage(
                f"{msg}: {e!r}",
                PLUGIN_NAME,
                Qgis.Critical,
            )
        finally:
            self._cleanup()

    def _cleanup(self):
        if self.current_tool:
            iface.mapCanvas().setMapTool(self.current_tool)
            self.current_tool = None

        if self.digit_tool:
            self.digit_tool.deleteLater()
            self.digit_tool = None

        if self.dlg:
            self.dlg.deleteLater()
            self.dlg = None

        self.layer = None
        self.feature = None
        self.target_layer = None
