#!/usr/bin/env python3
"""Pone las reglas de ventana de filmos en las del usuario, sin pisarle las suyas.

Por qué existe
--------------
En Wayland una aplicación **no puede** colocarse en pantalla, ponerse encima ni
quitarse de la barra de tareas: eso es del compositor y solo del compositor. El
botón del agente y su panel dependen por completo de estas reglas; sin ellas no
están mal puestos, están en mitad de la pantalla.

Dejarlas en `/etc/xdg/kwinrulesrc` no basta y esto se vio en la primera estación:
el fichero del sistema manda **solo mientras el usuario no tenga el suyo**, y en
cuanto Plasma escribe uno —abrir una vez el diálogo de reglas de KDE ya lo hace—
su `[General] count/rules` tapa la lista entera. Las reglas de filmos
desaparecían sin que nada lo dijera.

Qué se considera de filmos
--------------------------
Una regla es de filmos si **apunta a una ventana de filmos**: su `title` es uno
de los que trae la plantilla. Esas se rehacen en cada arranque. Todo lo demás
—las reglas que la persona haya escrito para SUS programas— se queda tal cual y
además **va primero**, que en KWin es quien gana: si alguien escribe a mano una
regla para el botón, manda la suya.
"""

from __future__ import annotations

import configparser
import os
import subprocess
import sys
from pathlib import Path

PLANTILLA = Path("/etc/xdg/kwinrulesrc")


def config() -> configparser.ConfigParser:
    cfg = configparser.ConfigParser(strict=False, interpolation=None)
    cfg.optionxform = str  # KWin distingue mayúsculas: Description no es description
    return cfg


def leer(ruta: Path) -> configparser.ConfigParser:
    cfg = config()
    if ruta.is_file():
        cfg.read(ruta, encoding="utf-8")
    return cfg


def ventanas_de_filmos(plantilla: configparser.ConfigParser) -> set[str]:
    return {
        plantilla[s]["title"]
        for s in plantilla.sections()
        if s != "General" and "title" in plantilla[s]
    }


def fusionar(plantilla: configparser.ConfigParser, usuario: configparser.ConfigParser) -> tuple[configparser.ConfigParser, bool]:
    """Devuelve la configuración fusionada y si ha cambiado algo."""

    nuestras = [s for s in plantilla.sections() if s != "General"]
    titulos = ventanas_de_filmos(plantilla)

    suyas = [
        s
        for s in usuario.sections()
        if s != "General"
        and s not in nuestras
        # Una regla que apunta a una ventana nuestra es nuestra, se llame como
        # se llame: así se recogen también las que dejaron versiones viejas.
        and usuario[s].get("title") not in titulos
    ]

    salida = config()
    orden = suyas + nuestras
    salida["General"] = {"count": str(len(orden)), "rules": ",".join(orden)}
    for seccion in suyas:
        salida[seccion] = dict(usuario[seccion])
    for seccion in nuestras:
        salida[seccion] = dict(plantilla[seccion])

    antes = {s: dict(usuario[s]) for s in usuario.sections()}
    despues = {s: dict(salida[s]) for s in salida.sections()}
    return salida, antes != despues


def escribir(cfg: configparser.ConfigParser, destino: Path) -> None:
    destino.parent.mkdir(parents=True, exist_ok=True)
    provisional = destino.with_suffix(destino.suffix + ".filmos-nuevo")
    with provisional.open("w", encoding="utf-8") as fichero:
        cfg.write(fichero, space_around_delimiters=False)
    # Un fichero de configuración a medias deja el escritorio sin reglas: se
    # cambia de golpe o no se cambia.
    os.replace(provisional, destino)


def avisar_a_kwin() -> None:
    for orden in (["qdbus6"], ["qdbus"]):
        try:
            subprocess.run(  # noqa: S603 - orden fija, sin shell
                [*orden, "org.kde.KWin", "/KWin", "reconfigure"],
                check=True,
                capture_output=True,
                timeout=10,
            )
            return
        except (OSError, subprocess.SubprocessError):
            continue


def main(argumentos: list[str]) -> int:
    plantilla_ruta = Path(argumentos[0]) if argumentos else PLANTILLA
    if not plantilla_ruta.is_file():
        return 0
    destino = Path(
        os.environ.get("XDG_CONFIG_HOME") or Path.home() / ".config"
    ) / "kwinrulesrc"

    plantilla = leer(plantilla_ruta)
    usuario = leer(destino)
    fusionada, ha_cambiado = fusionar(plantilla, usuario)
    if not ha_cambiado:
        return 0
    escribir(fusionada, destino)
    avisar_a_kwin()
    return 0


if __name__ == "__main__":
    raise SystemExit(main(sys.argv[1:]))
