from qgis.core import (
    QgsCoordinateReferenceSystem,
    QgsField,
    QgsLayerTreeLayer,
    QgsProject,
    QgsVectorLayer,
)
from qgis.PyQt.QtCore import QMetaType


class HelmertGroup:
    """Manage the 'Helmert' group and its helper layers.

    Layers created/managed:
      - hel_monuments: NoGeometry layer holding homologous control point pairs
      - hel_gaps: Point layer holding per-point residual magnitude (gap)
    """

    def __init__(self):
        # Layer tree group name
        self.group_name = "Helmert"

        # Layer names
        self.monuments_layer_name = "hel_monuments"
        # Backward-compatibility (older name)
        self._legacy_coordinates_layer_name = "hel_coordinates"

        self.gaps_layer_name = "hel_gaps"

        # Default CRS (RGF93 / Lambert-93)
        self.crs = QgsCoordinateReferenceSystem("EPSG:2154")

        # Create or reset layers
        self.monuments_layer = self._create_or_reset_monuments_layer()
        self.gaps_layer = self._create_or_reset_gaps_layer()

        # Create the group and add layers
        self._create_or_reset_group()

    # ------------------------------------------------------------------
    # Group / layer creation
    # ------------------------------------------------------------------
    def _create_or_reset_group(self):
        root = QgsProject.instance().layerTreeRoot()
        group = root.findGroup(self.group_name)

        if group is None:
            group = root.addGroup(self.group_name)

        # supprime les noeuds racine “en trop”
        self._remove_top_level_node(self.monuments_layer)
        self._remove_top_level_node(self.gaps_layer)

        self._add_layer_to_group(group, self.monuments_layer)
        self._add_layer_to_group(group, self.gaps_layer)

    def _add_layer_to_group(self, group, layer):
        # Prevent duplicates
        for child in group.children():
            if isinstance(child, QgsLayerTreeLayer) and child.layer() == layer:
                return
        group.addLayer(layer)

    def _create_or_reset_monuments_layer(self) -> QgsVectorLayer:
        # 1) Prefer new name
        existing = QgsProject.instance().mapLayersByName(self.monuments_layer_name)
        if existing:
            layer = existing[0]
            self._ensure_monuments_schema(layer)
            self._ensure_virtual_fid(layer)
            layer.updateFields()
            self._clear_layer(layer)
            return layer

        # 2) Migrate legacy layer if present
        legacy = QgsProject.instance().mapLayersByName(
            self._legacy_coordinates_layer_name
        )
        if legacy:
            layer = legacy[0]
            layer.setName(self.monuments_layer_name)
            self._ensure_monuments_schema(layer)
            self._ensure_virtual_fid(layer)
            layer.updateFields()
            self._clear_layer(layer)
            return layer

        # 3) Create a new NoGeometry layer
        layer = QgsVectorLayer(
            f"NoGeometry?crs={self.crs.authid()}",
            self.monuments_layer_name,
            "memory",
        )
        provider = layer.dataProvider()

        provider.addAttributes(
            [
                QgsField("used", QMetaType.Int),  # 1=active, 0=neutralized
                QgsField("x", QMetaType.Double),  # local X
                QgsField("y", QMetaType.Double),  # local Y
                QgsField("new_x", QMetaType.Double),  # georef X
                QgsField("new_y", QMetaType.Double),  # georef Y
                QgsField(
                    "err", QMetaType.Double
                ),  # residual magnitude (None if indeterminate)
            ]
        )
        layer.updateFields()

        # Don't add to legend (we'll manage it in the group)
        QgsProject.instance().addMapLayer(layer, False)
        self._ensure_virtual_fid(layer)
        return layer

    def _ensure_monuments_schema(self, layer: QgsVectorLayer) -> None:
        """Ensure hel_monuments has required fields (migrates legacy layers)."""
        required = [
            ("used", QMetaType.Int),
            ("x", QMetaType.Double),
            ("y", QMetaType.Double),
            ("new_x", QMetaType.Double),
            ("new_y", QMetaType.Double),
            ("err", QMetaType.Double),
        ]
        existing_names = {f.name() for f in layer.fields()}
        missing = [(n, t) for (n, t) in required if n not in existing_names]
        if not missing:
            return

        provider = layer.dataProvider()
        provider.addAttributes([QgsField(n, t) for (n, t) in missing])
        layer.updateFields()

    def _create_or_reset_gaps_layer(self) -> QgsVectorLayer:
        existing_layers = QgsProject.instance().mapLayersByName(self.gaps_layer_name)
        if existing_layers:
            layer = existing_layers[0]
            self._clear_layer(layer)
            return layer

        layer = QgsVectorLayer(
            f"Point?crs={self.crs.authid()}",
            self.gaps_layer_name,
            "memory",
        )
        provider = layer.dataProvider()
        provider.addAttributes(
            [
                QgsField("fid", QMetaType.Int),
                QgsField("gap", QMetaType.Double),
            ]
        )
        layer.updateFields()
        # Don't add to legend (we'll manage it in the group)
        QgsProject.instance().addMapLayer(layer, False)
        return layer

    # ------------------------------------------------------------------
    # Utilities
    # ------------------------------------------------------------------
    def _clear_layer(self, layer: QgsVectorLayer) -> None:
        if layer.isEditable():
            layer.rollback()
        layer.startEditing()
        layer.deleteFeatures([f.id() for f in layer.getFeatures()])
        layer.commitChanges()

    def _remove_top_level_node(self, layer):
        root = QgsProject.instance().layerTreeRoot()
        for child in list(root.children()):
            if isinstance(child, QgsLayerTreeLayer) and child.layer() == layer:
                root.removeChildNode(child)

    # ------------------------------------------------------------------
    # Public API
    # ------------------------------------------------------------------
    def get_monuments_layer(self) -> QgsVectorLayer:
        return self.monuments_layer

    # Backward compatible name used by older code
    def get_coordinates_layer(self) -> QgsVectorLayer:
        return self.monuments_layer

    def get_gaps_layer(self) -> QgsVectorLayer:
        return self.gaps_layer

    def _ensure_virtual_fid(self, layer):
        # Evite conflit si un champ "qgis_id" existe déjà
        if layer.fields().indexOf("qgis_id") == -1:
            layer.addExpressionField("$id", QgsField("qgis_id", QMetaType.Int))
