# SPDX-License-Identifier: GPL-3.0-or-later
"""'Route bind-width' map layer — the per-route binding corridor drawn as two thin
dashed lines offset ±(bind width) from each fairway centreline, so the operator can
see how wide the tolerance they edit in the Routes tab actually is.

No geometry offsetting is done: each route's centreline is drawn once and a single
symbol carries two dashed simple-line layers whose perpendicular OFFSET is data-
defined to ``± "bind_m"`` in **true metres** (RenderMetersInMapUnits handles the map
CRS). Editing a route's bind width just updates its ``bind_m`` attribute and repaints
— no rebuild. Added switched off (like coverage); the user toggles it in Layers.
"""
LAYER_NAME = "Route bind-width"
_COLOR = (0, 150, 136)          # teal — matches the marker accent


def _prop_offset():
    """Qt5/Qt6-safe QgsSymbolLayer data-defined 'offset' property key."""
    from qgis.core import QgsSymbolLayer
    for path in (("Property", "Offset"), ("PropertyOffset",)):
        obj = QgsSymbolLayer
        try:
            for part in path:
                obj = getattr(obj, part)
            return obj
        except AttributeError:
            continue
    return None


def _corridor_symbol():
    """A line symbol with two thin dashed edges offset to ±"bind_m" metres."""
    from qgis.PyQt.QtGui import QColor
    from qgis.PyQt.QtCore import Qt
    from qgis.core import (QgsLineSymbol, QgsSimpleLineSymbolLayer, QgsProperty,
                           QgsUnitTypes)
    sym = QgsLineSymbol()
    while sym.symbolLayerCount():                  # start empty
        sym.deleteSymbolLayer(0)
    prop_off = _prop_offset()
    for sign in (1, -1):
        sl = QgsSimpleLineSymbolLayer(QColor(*_COLOR))
        sl.setWidth(0.3)                           # thin (mm)
        try:
            sl.setPenStyle(Qt.DashLine)
        except Exception:                          # noqa: BLE001  # nosec B110
            pass
        try:
            sl.setOffsetUnit(QgsUnitTypes.RenderMetersInMapUnits)
            if prop_off is not None:
                sl.dataDefinedProperties().setProperty(
                    prop_off, QgsProperty.fromExpression('%d * "bind_m"' % sign))
        except Exception:                          # noqa: BLE001  # nosec B110
            pass
        sym.appendSymbolLayer(sl)
    return sym


def build_layer(gpkg_path, route_tol, default_tol=150.0):
    """Build the in-memory 'Route bind-width' layer from routes.gpkg's 'route'
    table, one feature per route carrying its bind_m (its override or the default).
    Returns the styled layer, or None on any problem (best-effort)."""
    try:
        from qgis.core import (QgsVectorLayer, QgsFeature, QgsField,
                               QgsSingleSymbolRenderer)
        from qgis.PyQt.QtCore import QVariant
    except Exception:                              # noqa: BLE001
        return None
    src = QgsVectorLayer("%s|layername=route" % gpkg_path, "route_src", "ogr")
    if not src.isValid():
        return None
    crs = src.crs().authid() or "EPSG:4326"
    mem = QgsVectorLayer("LineString?crs=%s" % crs, LAYER_NAME, "memory")
    if not mem.isValid():
        return None
    dp = mem.dataProvider()
    dp.addAttributes([QgsField("routeid", QVariant.Int),
                      QgsField("bind_m", QVariant.Double)])
    mem.updateFields()
    fid_route = src.fields().indexOf("routeid")
    feats = []
    for f in src.getFeatures():
        g = f.geometry()
        if g is None or g.isEmpty():
            continue
        try:
            rid = int(f["routeid"]) if fid_route >= 0 else None
        except (TypeError, ValueError):
            rid = None
        bind_m = float(route_tol.get(rid, default_tol)) if rid is not None \
            else float(default_tol)
        nf = QgsFeature(mem.fields())
        nf.setGeometry(g)
        nf.setAttribute("routeid", rid if rid is not None else 0)
        nf.setAttribute("bind_m", bind_m)
        feats.append(nf)
    if not feats:
        return None
    dp.addFeatures(feats)
    mem.updateExtents()
    try:
        mem.setRenderer(QgsSingleSymbolRenderer(_corridor_symbol()))
    except Exception:                              # noqa: BLE001  # nosec B110
        pass
    try:                                           # only when zoomed in a bit
        mem.setScaleBasedVisibility(True)
        mem.setMinimumScale(400000.0)              # visible below ~1:400000
        mem.setMaximumScale(0.0)
    except Exception:                              # noqa: BLE001  # nosec B110
        pass
    return mem


def update_route(layer, routeid, meters):
    """Update one route's bind_m attribute in the corridor layer and repaint."""
    if layer is None:
        return
    try:
        idx = layer.fields().indexOf("bind_m")
        rid_idx = layer.fields().indexOf("routeid")
        if idx < 0 or rid_idx < 0:
            return
        for f in layer.getFeatures():
            if f["routeid"] == routeid:
                layer.dataProvider().changeAttributeValues(
                    {f.id(): {idx: float(meters)}})
        layer.triggerRepaint()
    except Exception:                              # noqa: BLE001  # nosec B110
        pass
