import logging
from PySide6.QtCore import QByteArray, QEvent, QFile, Qt
from PySide6.QtGui import QIcon, QPainter, QPixmap, QPalette
from PySide6.QtSvg import QSvgRenderer
from PySide6.QtWidgets import QApplication
import res.resources_rc  # noqa: F401  # registriert die Qt-Ressourcen (Icons) beim Import

logger = logging.getLogger(__name__)

# Basisgröße für das einmalige Rendern; Qt skaliert daraus die benötigten Icon-Größen
# (16/24/32 px in Menüs, Bäumen, Buttons). 64 px bietet genug Reserve auch für HiDPI.
_RENDER_SIZE = 64

# Cache: (Icon-Name, Theme-Textfarbe) → fertig gerendertes QIcon.
# Der Farb-Anteil im Schlüssel sorgt dafür, dass ein Theme-Wechsel automatisch
# neue Einträge erzeugt, ohne dass der Cache explizit geleert werden muss.
_ICON_CACHE: dict[tuple[str, bytes], QIcon] = {}


def icon(name: str) -> QIcon:
    """
    Lädt ein Feather-Icon aus dem Qt-Ressource-System und färbt es mit der aktuellen Palette-Farbe.

    Args:
        name: Feather-Icon-Name (z.B. "folder-plus", "settings")

    Returns:
        QIcon in der Textfarbe des aktiven Themes, oder leerer QIcon bei unbekanntem Namen
    """
    path = f":/icons/{name}.svg"

    app = QApplication.instance()
    if app is None:
        return QIcon(path)

    color = app.palette().color(QPalette.ColorRole.WindowText).name().encode()

    cache_key = (name, color)
    if (cached := _ICON_CACHE.get(cache_key)) is not None:
        return cached

    f = QFile(path)
    if not f.open(QFile.OpenModeFlag.ReadOnly):
        logger.warning(f"Icon konnte nicht geöffnet werden: {path}")
        return QIcon(path)

    svg_data = QByteArray(bytes(f.readAll()).replace(b"currentColor", color))
    f.close()

    renderer = QSvgRenderer(svg_data)
    pixmap = QPixmap(_RENDER_SIZE, _RENDER_SIZE)
    pixmap.fill(Qt.GlobalColor.transparent)
    painter = QPainter(pixmap)
    renderer.render(painter)
    painter.end()

    result = QIcon(pixmap)
    _ICON_CACHE[cache_key] = result
    return result


class IconRefreshMixin:
    """
    Mischklasse für Top-Level-Widgets, deren Icons sich beim Theme-Wechsel automatisch neu einfärben.

    Voraussetzungen für die nutzende Klasse:
    - Sie stellt eine Methode ``_setup_icons()`` bereit, die alle ihre Icons neu setzt.
    - Nach abgeschlossener Initialisierung setzt sie ``self._icons_ready = True``
      (idealerweise zusammen mit ``self._last_icon_color``, siehe ``mark_icons_ready()``).

    Bei einem Style-/Palette-Wechsel wird – nur wenn sich die Theme-Textfarbe tatsächlich
    geändert hat – ``_on_icon_theme_changed()`` aufgerufen (Standard: ``_setup_icons()``).
    Unterklassen können ``_on_icon_theme_changed()`` überschreiben, um zusätzlich z.B.
    Baum-Icons neu zu zeichnen.

    Hinweis: ``IconRefreshMixin`` muss in der Klassenbasis VOR der Qt-Basisklasse
    (``QMainWindow``/``QDialog``) stehen, damit das überschriebene ``changeEvent`` greift.
    """

    _icons_ready: bool = False
    _last_icon_color: str | None = None

    @staticmethod
    def _current_text_color() -> str | None:
        app = QApplication.instance()
        if app is None:
            return None
        return app.palette().color(QPalette.ColorRole.WindowText).name()

    def mark_icons_ready(self):
        """Schaltet die Theme-Reaktivität scharf (nach dem ersten ``_setup_icons()`` aufrufen)."""
        self._last_icon_color = self._current_text_color()
        self._icons_ready = True

    def changeEvent(self, event):
        if event.type() in (QEvent.Type.PaletteChange, QEvent.Type.StyleChange) and self._icons_ready:
            color = self._current_text_color()
            if color is not None and color != self._last_icon_color:
                self._last_icon_color = color
                self._on_icon_theme_changed()
        super().changeEvent(event)

    def _on_icon_theme_changed(self):
        """Reaktion auf eine geänderte Theme-Textfarbe. Standard: alle Icons neu setzen."""
        self._setup_icons()
