"""Kuratierter App-Katalog für Endnutzer (GNOME-Software-Stil). UI-frei.

Beliebte Desktop-Apps, die ein normaler User typisch braucht — gruppiert nach
Kategorie. Quelle ist entweder ein Arch-Repo-Paket (via paru) oder Flathub.
Empfohlen wird, was noch nicht installiert ist.
"""
from __future__ import annotations

import shutil
import subprocess
from dataclasses import dataclass


@dataclass(frozen=True)
class App:
    id: str           # stabiler Key
    name: str
    summary: str
    category: str
    source: str       # 'repo' | 'flatpak'
    pkg: str          # pacman-Name bzw. Flatpak-App-ID
    icon: str         # bevorzugter Icon-Name (Fallback = Kategorie-Icon)


CATEGORY_ORDER = [
    "Internet", "Office", "Communication", "Multimedia",
    "Graphics", "Games", "Productivity", "Utilities",
]

CATEGORY_ICON = {
    "Internet": "web-browser-symbolic",
    "Office": "x-office-document-symbolic",
    "Communication": "mail-send-symbolic",
    "Multimedia": "multimedia-player-symbolic",
    "Graphics": "applications-graphics-symbolic",
    "Games": "applications-games-symbolic",
    "Productivity": "view-list-symbolic",
    "Utilities": "applications-utilities-symbolic",
}

CATALOG: list[App] = [
    # Internet
    App("firefox", "Firefox", "Fast, private web browser", "Internet", "repo", "firefox", "firefox"),
    App("chromium", "Chromium", "Open-source web browser", "Internet", "repo", "chromium", "chromium"),
    App("qbittorrent", "qBittorrent", "Lightweight BitTorrent client", "Internet", "repo", "qbittorrent", "qbittorrent"),
    # Office
    App("libreoffice", "LibreOffice", "Word processor, spreadsheets and slides", "Office", "repo", "libreoffice-fresh", "libreoffice-startcenter"),
    App("okular", "Okular", "View PDFs, comics and e-books", "Office", "repo", "okular", "okular"),
    # Communication
    App("thunderbird", "Thunderbird", "Email, calendar and contacts", "Communication", "repo", "thunderbird", "thunderbird"),
    App("signal", "Signal", "Private encrypted messaging", "Communication", "repo", "signal-desktop", "signal-desktop"),
    App("telegram", "Telegram", "Cloud-based messaging", "Communication", "repo", "telegram-desktop", "telegram"),
    App("discord", "Discord", "Voice and text chat for communities", "Communication", "repo", "discord", "discord"),
    # Multimedia
    App("vlc", "VLC", "Plays nearly any audio or video file", "Multimedia", "repo", "vlc", "vlc"),
    App("spotify", "Spotify", "Music streaming", "Multimedia", "flatpak", "com.spotify.Client", "com.spotify.Client"),
    App("obs", "OBS Studio", "Screen recording and live streaming", "Multimedia", "repo", "obs-studio", "com.obsproject.Studio"),
    App("kdenlive", "Kdenlive", "Non-linear video editor", "Multimedia", "repo", "kdenlive", "kdenlive"),
    # Graphics
    App("gimp", "GIMP", "Powerful image editor", "Graphics", "repo", "gimp", "gimp"),
    App("inkscape", "Inkscape", "Vector graphics editor", "Graphics", "repo", "inkscape", "org.inkscape.Inkscape"),
    App("krita", "Krita", "Digital painting studio", "Graphics", "repo", "krita", "krita"),
    App("blender", "Blender", "3D modelling and animation", "Graphics", "repo", "blender", "blender"),
    # Games
    App("steam", "Steam", "The biggest PC game store", "Games", "repo", "steam", "steam"),
    App("lutris", "Lutris", "Manage and run games from anywhere", "Games", "repo", "lutris", "lutris"),
    App("heroic", "Heroic", "Epic, GOG and Amazon games launcher", "Games", "flatpak", "com.heroicgameslauncher.hgl", "com.heroicgameslauncher.hgl"),
    # Productivity
    App("obsidian", "Obsidian", "Markdown notes and knowledge base", "Productivity", "flatpak", "md.obsidian.Obsidian", "md.obsidian.Obsidian"),
    App("keepassxc", "KeePassXC", "Secure offline password manager", "Productivity", "repo", "keepassxc", "keepassxc"),
    # Utilities
    App("bottles", "Bottles", "Run Windows software easily", "Utilities", "flatpak", "com.usebottles.bottles", "com.usebottles.bottles"),
    App("flatseal", "Flatseal", "Manage Flatpak app permissions", "Utilities", "flatpak", "com.github.tchx84.Flatseal", "com.github.tchx84.Flatseal"),
]


def _flatpak_installed_ids() -> set[str]:
    if not shutil.which("flatpak"):
        return set()
    try:
        r = subprocess.run(
            ["flatpak", "list", "--app", "--columns=application"],
            capture_output=True, text=True, check=False, timeout=10,
        )
    except (FileNotFoundError, subprocess.TimeoutExpired):
        return set()
    return {ln.strip() for ln in r.stdout.splitlines() if ln.strip()}


def is_installed(app: App, pacman_set: set[str], flatpak_ids: set[str]) -> bool:
    if app.source == "flatpak":
        return app.pkg in flatpak_ids
    return app.pkg in pacman_set


def recommended(pacman_set: set[str], flatpak_ids: set[str] | None = None) -> list[App]:
    """Noch nicht installierte Katalog-Apps, in Kategorie-Reihenfolge."""
    if flatpak_ids is None:
        flatpak_ids = _flatpak_installed_ids()
    out = [a for a in CATALOG if not is_installed(a, pacman_set, flatpak_ids)]
    out.sort(key=lambda a: (CATEGORY_ORDER.index(a.category)
                            if a.category in CATEGORY_ORDER else 99, a.name.lower()))
    return out


def install_argv(app: App) -> tuple[list[str], bool]:
    """(argv, privileged). Katalog-Apps sind Repo (via Helper) oder Flatpak."""
    if app.source == "flatpak":
        return ["flatpak", "install", "--assumeyes", "flathub", app.pkg], False
    return ["pacman", "-S", "--needed", "--noconfirm", app.pkg], True
