"""Desktop-Look: GTK3-Theme, Icon-Theme, System-Font — wie nwg-look.

Alles User-Level (kein root). Robuster Doppel-Weg wie nwg-look: wir setzen
sowohl die gsettings-Keys (org.gnome.desktop.interface) ALS AUCH die
~/.config/gtk-{3,4}.0/settings.ini — so greift es unter GNOME (gsettings)
genauso wie unter niri (settings.ini), ohne laufenden Daemon.

Hinweis: libadwaita-Apps ignorieren das GTK-*Theme* bewusst (nur GTK3/alte
GTK4-Apps folgen). Icon-Theme und Font gelten dagegen auch für libadwaita.
"""
from __future__ import annotations

import configparser
import subprocess
from pathlib import Path

_IFACE = "org.gnome.desktop.interface"
_SKIP_ICONS = {"default", "hicolor", "locolor"}


# ------------------------------------------------------------- discovery

def _theme_dirs() -> list[Path]:
    return [
        Path("/usr/share/themes"),
        Path.home() / ".themes",
        Path.home() / ".local/share/themes",
    ]


def _icon_dirs() -> list[Path]:
    return [
        Path("/usr/share/icons"),
        Path.home() / ".local/share/icons",
        Path.home() / ".icons",
    ]


def list_gtk_themes() -> list[str]:
    """Theme-Verzeichnisse mit gtk-3.0/ oder gtk-4.0/ Unterordner."""
    names: set[str] = set()
    for d in _theme_dirs():
        if not d.is_dir():
            continue
        for t in d.iterdir():
            if t.is_dir() and any((t / s).is_dir() for s in ("gtk-3.0", "gtk-4.0")):
                names.add(t.name)
    return sorted(names, key=str.lower)


def list_icon_themes() -> list[str]:
    """Echte Icon-Themes (index.theme mit nicht-leerem Directories=)."""
    names: set[str] = set()
    for d in _icon_dirs():
        if not d.is_dir():
            continue
        for t in d.iterdir():
            if t.name in _SKIP_ICONS:
                continue
            idx = t / "index.theme"
            if not idx.is_file():
                continue
            try:
                text = idx.read_text(errors="ignore")
            except OSError:
                continue
            for line in text.splitlines():
                s = line.strip()
                if s.startswith("Directories=") and s.split("=", 1)[1].strip():
                    names.add(t.name)
                    break
    return sorted(names, key=str.lower)


def list_cursor_themes() -> list[str]:
    """Cursor-Themes = Verzeichnisse mit einem cursors/ Unterordner."""
    names: set[str] = set()
    for d in _icon_dirs():
        if not d.is_dir():
            continue
        for t in d.iterdir():
            if t.is_dir() and (t / "cursors").is_dir():
                names.add(t.name)
    return sorted(names, key=str.lower)


# ------------------------------------------------------------- read state

def _gget(key: str) -> str:
    try:
        r = subprocess.run(
            ["gsettings", "get", _IFACE, key],
            capture_output=True, text=True, check=False, timeout=5,
        )
    except (FileNotFoundError, subprocess.TimeoutExpired):
        return ""
    return r.stdout.strip().strip("'\"") if r.returncode == 0 else ""


def current() -> dict[str, str]:
    return {
        "gtk_theme": _gget("gtk-theme"),
        "icon_theme": _gget("icon-theme"),
        "font": _gget("font-name"),
        "cursor_theme": _gget("cursor-theme"),
        "cursor_size": _gget("cursor-size"),
    }


# ------------------------------------------------------------- apply

def _gset(key: str, value: str) -> None:
    try:
        subprocess.run(
            ["gsettings", "set", _IFACE, key, value],
            check=False, timeout=5,
        )
    except (FileNotFoundError, subprocess.TimeoutExpired):
        pass


def _write_ini(key: str, value: str) -> None:
    """Schreibt [Settings] key=value in gtk-3.0 und gtk-4.0 settings.ini."""
    for sub in ("gtk-3.0", "gtk-4.0"):
        d = Path.home() / ".config" / sub
        d.mkdir(parents=True, exist_ok=True)
        ini = d / "settings.ini"
        cp = configparser.ConfigParser()
        cp.optionxform = str  # Key-Case erhalten
        if ini.exists():
            try:
                cp.read(ini)
            except configparser.Error:
                pass
        if "Settings" not in cp:
            cp["Settings"] = {}
        cp["Settings"][key] = value
        try:
            with ini.open("w") as f:
                cp.write(f)
        except OSError:
            pass


def apply_gtk_theme(name: str) -> None:
    _gset("gtk-theme", name)
    _write_ini("gtk-theme-name", name)


def apply_icon_theme(name: str) -> None:
    _gset("icon-theme", name)
    _write_ini("gtk-icon-theme-name", name)


def apply_font(name: str) -> None:
    _gset("font-name", name)
    _write_ini("gtk-font-name", name)


def apply_cursor_theme(name: str) -> None:
    _gset("cursor-theme", name)
    _write_ini("gtk-cursor-theme-name", name)


def apply_cursor_size(size: int) -> None:
    _gset("cursor-size", str(size))
    _write_ini("gtk-cursor-theme-size", str(size))


# ------------------------------------------------------------- titlebar buttons
#
# button-layout (Schema org.gnome.desktop.wm.preferences) bestimmt, welche
# Knöpfe in der Titlebar erscheinen und auf welcher Seite. Format "<links>:<rechts>",
# Buttons komma-getrennt aus {minimize,maximize,close,appmenu,icon}.
# Beispiele: ":minimize,maximize,close" (alle rechts), "close:" (nur Close links).
# libadwaita respektiert diesen Key (auch diese App).

_WM = "org.gnome.desktop.wm.preferences"


def _wm_get(key: str) -> str:
    try:
        r = subprocess.run(
            ["gsettings", "get", _WM, key],
            capture_output=True, text=True, check=False, timeout=5,
        )
    except (FileNotFoundError, subprocess.TimeoutExpired):
        return ""
    return r.stdout.strip().strip("'\"") if r.returncode == 0 else ""


def _wm_set(key: str, value: str) -> None:
    try:
        subprocess.run(
            ["gsettings", "set", _WM, key, value],
            check=False, timeout=5,
        )
    except (FileNotFoundError, subprocess.TimeoutExpired):
        pass


def _read_ini(key: str) -> str:
    """Liest [Settings] key aus gtk-4.0 (bevorzugt) bzw. gtk-3.0 settings.ini."""
    for sub in ("gtk-4.0", "gtk-3.0"):
        ini = Path.home() / ".config" / sub / "settings.ini"
        if not ini.is_file():
            continue
        cp = configparser.ConfigParser()
        cp.optionxform = str
        try:
            cp.read(ini)
        except configparser.Error:
            continue
        if cp.has_option("Settings", key):
            return cp.get("Settings", key)
    return ""


def _parse_layout(raw: str) -> dict:
    left_part, _, right_part = raw.partition(":")
    left = {b.strip() for b in left_part.split(",") if b.strip()}
    right = {b.strip() for b in right_part.split(",") if b.strip()}
    wanted = ("minimize", "maximize", "close")
    side = "left" if sum(b in left for b in wanted) > sum(b in right for b in wanted) else "right"
    present = left | right
    return {
        "minimize": "minimize" in present,
        "maximize": "maximize" in present,
        "close": "close" in present,
        "side": side,
    }


def window_buttons() -> dict:
    """Effektives Decoration-Layout → {minimize, maximize, close, side}.

    Quelle ist `gtk-decoration-layout` aus settings.ini (das, was GTK wirklich
    nutzt — der gnome-gsettings-Key allein greift auf portal-losen Sessions
    NICHT). Wenn noch nichts gesetzt: GTK-Default (alle drei, rechts).
    """
    raw = _read_ini("gtk-decoration-layout")
    if not raw or ":" not in raw:
        return {"minimize": True, "maximize": True, "close": True, "side": "right"}
    return _parse_layout(raw)


def set_window_buttons(minimize: bool, maximize: bool, close: bool, side: str) -> None:
    """Schreibt das Decoration-Layout. WIRKSAM ist `gtk-decoration-layout` in
    settings.ini (greift bei NEU geöffneten Fenstern). Zusätzlich der
    gnome-gsettings-Key, falls ein Settings-Portal ihn live übersetzt."""
    joined = ",".join(
        b for b, on in (("minimize", minimize), ("maximize", maximize), ("close", close)) if on
    )
    value = joined + ":" if side == "left" else ":" + joined
    _write_ini("gtk-decoration-layout", value)   # wirksam (settings.ini)
    _wm_set("button-layout", value)               # zusätzlich (gsettings)
