# Copyright (C) 2026 Wilhelmus Dingenus de Pooter
# SPDX-License-Identifier: GPL-3.0-or-later
"""
enc_qt_compat.py — one-shot Qt5/Qt6 compatibility layer (QGIS 3 and QGIS 4).

QGIS 4 is built on Qt6/PyQt6, where enum members are only reachable through
their scoped enum class (``Qt.DockWidgetArea.LeftDockWidgetArea``); the Qt5-era
unscoped spelling (``Qt.LeftDockWidgetArea``) raises AttributeError. Rather
than rewrite every enum reference — and risk missing one in a rarely-exercised
GUI path — this module *promotes* every enum member of every class in
qgis.PyQt's QtCore/QtGui/QtWidgets back onto its owning class, restoring the
unscoped spelling. (The scoped spelling always works too, in both Qt versions.)

It also restores the classes Qt6 moved between modules (``QAction`` et al.,
QtWidgets → QtGui), so legacy imports keep resolving.

On Qt5 (QGIS 3) this module is a NO-OP, so QGIS 3 behaviour is untouched.
Importing it more than once is harmless. QGIS's own enums (Qgis.*, Qgs*) are
not touched: QGIS exposes those both scoped and unscoped in its Qt6 builds.
"""
import enum

_applied = False


def _promote(klass):
    """Copy every member of every Enum nested in ``klass`` onto ``klass``.

    Promote from ``__members__``, NOT by iterating the enum: iterating an
    ``enum.Flag`` yields only its canonical single-bit members and silently
    skips the ZERO member and composites — which is exactly what bit us:
    ``Qt.FocusPolicy`` iterates as [TabFocus, ClickFocus] only, so
    ``Qt.NoFocus`` (0), ``Qt.StrongFocus`` and ``Qt.WheelFocus`` (composites)
    never got promoted (same for ``Qt.AlignCenter``, ``Qt.NoItemFlags``, …).
    ``__members__`` lists every NAMED member, including zero, composites and
    aliases."""
    for name in dir(klass):
        try:
            attr = getattr(klass, name)
        except Exception:                               # noqa: BLE001
            continue
        if isinstance(attr, type) and issubclass(attr, enum.Enum):
            for mname, member in attr.__members__.items():
                if not hasattr(klass, mname):
                    try:
                        setattr(klass, mname, member)
                    except Exception:                   # noqa: BLE001
                        pass


def apply():
    """Apply the compatibility layer (idempotent; no-op on Qt5 / headless)."""
    global _applied
    if _applied:
        return
    try:
        from qgis.PyQt import QtCore
    except Exception:                                   # noqa: BLE001
        return                                          # headless / no Qt: no-op
    if not QtCore.QT_VERSION_STR.startswith("6"):
        _applied = True                                 # Qt5: nothing to do
        return
    try:
        from qgis.PyQt import QtGui, QtWidgets
    except Exception:                                   # noqa: BLE001
        return
    for mod in (QtCore, QtGui, QtWidgets):
        for name in dir(mod):
            obj = getattr(mod, name, None)
            if isinstance(obj, type):
                _promote(obj)
    # Qt6 moved these from QtWidgets to QtGui; alias them back so Qt5-era
    # ``from qgis.PyQt.QtWidgets import QAction`` keeps working.
    for name in ("QAction", "QActionGroup", "QShortcut", "QFileSystemModel",
                 "QUndoCommand", "QUndoStack", "QUndoGroup"):
        if not hasattr(QtWidgets, name) and hasattr(QtGui, name):
            setattr(QtWidgets, name, getattr(QtGui, name))
    _applied = True


apply()
