from __future__ import annotations

import argparse
import sys
from pathlib import Path


def _human_size(n: int) -> str:
    f = float(n)
    for unit in ("B", "K", "M", "G", "T"):
        if f < 1024 or unit == "T":
            return f"{f:.0f}{unit}" if unit == "B" else f"{f:.1f}{unit}"
        f /= 1024
    return f"{f:.1f}T"


def _format_days(d: int) -> str:
    if d == 0:
        return "heute"
    if d < 30:
        return f"{d}d"
    if d < 365:
        return f"{d // 30}mo"
    return f"{d // 365}y"


def _console():
    from rich.console import Console
    return Console()


# ---------------------------------------------------------------- scan


def _scan(category: str) -> int:
    from rich.table import Table

    from taninux.maintain.scanners import SCANNERS, by_id
    from taninux.core import theme as T
    from taninux.maintain.types import (
        CLASS_COLOR, CLASS_GLYPH, CLASS_LABEL, Classification,
    )

    console = _console()
    specs = SCANNERS if category == "all" else [s for s in [by_id(category)] if s]
    if not specs:
        console.print(f"  [{T.RED}]unknown category: {category}[/]")
        return 2

    home = str(Path.home())
    for spec in specs:
        scanner = spec.factory()
        findings = scanner.scan()
        reclaim = sum(
            f.size_bytes for f in findings
            if f.classification in (Classification.SAFE, Classification.LIKELY_ORPHAN)
        )

        console.print()
        console.print(
            f"  [{T.UME}]▲ {spec.label}[/]   "
            f"[{T.YUKI}]{len(findings)}[/] [{T.BASE5}]einträge[/]   "
            f"[{T.BASE3}]·[/]   "
            f"[{T.TAKE}]{_human_size(reclaim)}[/] [{T.BASE5}]potentiell wegwerfbar[/]"
        )
        console.print()
        if not findings:
            console.print(f"  [{T.BASE4}]keine einträge.[/]")
            continue

        table = Table(
            show_header=True,
            header_style=T.SHINKAI,
            border_style=T.BASE2,
            pad_edge=False,
            box=None,
        )
        table.add_column("", width=2)
        table.add_column("pfad", no_wrap=True)
        table.add_column("grösse", justify="right")
        table.add_column("idle", justify="right")
        table.add_column("status")

        for f in findings:
            color = CLASS_COLOR[f.classification]
            s = str(f.path)
            path = s[6:] if s.startswith("pkg://") else s.replace(home, "~", 1)
            size = "—" if not f.size_bytes else _human_size(f.size_bytes)
            days = "—" if f.last_access_days is None else _format_days(f.last_access_days)
            table.add_row(
                f"[{color}]{CLASS_GLYPH[f.classification]}[/]",
                f"[{T.FG_ALT}]{path}[/]",
                f"[{T.CHIKYU}]{size}[/]" if f.size_bytes else f"[{T.BASE3}]—[/]",
                f"[{T.BASE5}]{days}[/]",
                f"[{color}]{CLASS_LABEL[f.classification]}[/]",
            )
        console.print(table)
    return 0


# ---------------------------------------------------------------- trash


def _trash_list() -> int:
    from rich.table import Table

    from taninux.core.trash import list_trash
    from taninux.core import theme as T

    console = _console()
    entries = list_trash()
    if not entries:
        console.print(f"  [{T.BASE4}]trash ist leer.[/]")
        return 0

    total = sum(e.size_bytes for e in entries)
    console.print()
    console.print(
        f"  [{T.UME}]▲ trash[/]   "
        f"[{T.YUKI}]{len(entries)}[/] [{T.BASE5}]einträge[/]   "
        f"[{T.BASE3}]·[/]   "
        f"[{T.CHIKYU}]{_human_size(total)}[/]"
    )
    console.print()

    table = Table(
        show_header=True,
        header_style=T.SHINKAI,
        border_style=T.BASE2,
        pad_edge=False,
        box=None,
    )
    table.add_column("id")
    table.add_column("origin", no_wrap=True)
    table.add_column("grösse", justify="right")
    table.add_column("gelöscht am")

    home = str(Path.home())
    for e in entries:
        origin = str(e.original).replace(home, "~", 1)
        table.add_row(
            f"[{T.BASE5}]{e.id}[/]",
            f"[{T.FG_ALT}]{origin}[/]",
            f"[{T.CHIKYU}]{_human_size(e.size_bytes)}[/]",
            f"[{T.BASE5}]{e.trashed_at}[/]",
        )
    console.print(table)
    return 0


def _trash_restore(slot_id: str) -> int:
    from taninux.core.trash import restore
    from taninux.core import theme as T

    console = _console()
    try:
        original = restore(slot_id)
    except FileNotFoundError:
        console.print(f"  [{T.RED}]kein eintrag: {slot_id}[/]")
        return 2
    except FileExistsError as e:
        console.print(f"  [{T.RED}]{e}[/]")
        return 2
    console.print(f"  [{T.TAKE}]wiederhergestellt:[/] [{T.YUKI}]{original}[/]")
    return 0


def _trash_purge(slot_id: str | None) -> int:
    from taninux.core.trash import list_trash, purge
    from taninux.core import theme as T

    console = _console()
    if slot_id:
        try:
            purge(slot_id)
        except FileNotFoundError:
            console.print(f"  [{T.RED}]kein eintrag: {slot_id}[/]")
            return 2
        console.print(f"  [{T.BASE5}]gelöscht:[/] [{T.YUKI}]{slot_id}[/]")
        return 0

    entries = list_trash()
    if not entries:
        console.print(f"  [{T.BASE4}]trash bereits leer.[/]")
        return 0
    total = sum(e.size_bytes for e in entries)
    console.print(
        f"  [{T.RED}]purge {len(entries)} einträge[/] "
        f"[{T.BASE4}]({_human_size(total)})?[/]   "
        f"[{T.BASE5}]bestätige mit:[/] taninux trash purge --all --yes"
    )
    return 1


def _trash_purge_all(confirmed: bool) -> int:
    from taninux.core.trash import list_trash, purge
    from taninux.core import theme as T

    console = _console()
    entries = list_trash()
    if not entries:
        console.print(f"  [{T.BASE4}]trash bereits leer.[/]")
        return 0
    if not confirmed:
        return _trash_purge(None)
    for e in entries:
        purge(e.id)
    console.print(f"  [{T.BASE5}]{len(entries)} einträge endgültig gelöscht.[/]")
    return 0


# ---------------------------------------------------------------- update


def _update_list() -> int:
    from collections import Counter
    from rich.table import Table

    from taninux.core import theme as T
    from taninux.update.sources import fetch_all

    console = _console()
    console.print(f"\n  [{T.BASE4}]scanne pacman + AUR + flatpak + fwupd…[/]")
    items = fetch_all()

    if not items:
        console.print(f"\n  [{T.TAKE}]system ist aktuell.[/]\n")
        return 0

    total_size = sum(i.size_bytes for i in items)
    counts = Counter(i.source for i in items)
    console.print()
    console.print(
        f"  [{T.UME}]↻ updates[/]   "
        f"[{T.YUKI}]{len(items)}[/] [{T.BASE5}]verfügbar[/]"
        + (f"   [{T.BASE3}]·[/]   [{T.CHIKYU}]{_human_size(total_size)}[/]" if total_size else "")
    )
    parts = []
    for src in ("pacman", "aur", "flatpak", "fwupd"):
        n = counts.get(src, 0)
        if n:
            parts.append(f"[{T.BASE5}]{src} {n}[/]")
    if parts:
        console.print(f"   {'  ·  '.join(parts)}")
    console.print()

    order = {"pacman": 0, "aur": 1, "flatpak": 2, "fwupd": 3}
    items.sort(key=lambda i: (order.get(i.source, 9), i.name))

    table = Table(
        show_header=True,
        header_style=T.SHINKAI,
        border_style=T.BASE2,
        pad_edge=False,
        box=None,
    )
    table.add_column("quelle", width=9)
    table.add_column("paket")
    table.add_column("aktuell")
    table.add_column("→", width=1)
    table.add_column("neu")
    table.add_column("grösse", justify="right")

    for i in items:
        size = "—" if not i.size_bytes else _human_size(i.size_bytes)
        table.add_row(
            f"[{T.BASE5}]{i.source}[/]",
            f"[{T.YUKI}]{i.name}[/]",
            f"[{T.BASE4}]{i.current_version}[/]",
            f"[{T.BASE3}]→[/]",
            f"[{T.TAKE}]{i.new_version}[/]",
            f"[{T.CHIKYU}]{size}[/]" if i.size_bytes else f"[{T.BASE3}]—[/]",
        )
    console.print(table)
    console.print()

    cmds = []
    if counts.get("pacman") or counts.get("aur"):
        cmds.append("paru -Syu")
    if counts.get("flatpak"):
        cmds.append("flatpak update -y")
    if counts.get("fwupd"):
        cmds.append("sudo fwupdmgr update")
    if cmds:
        console.print(f"  [{T.SHINKAI}]ausführen:[/]")
        for c in cmds:
            console.print(f"    [{T.CHIKYU}]{c}[/]")
        console.print()
    return 0


def _update_execute() -> int:
    """Headless: führt alle pending updates aus mit --noconfirm."""
    import subprocess

    from taninux.core import sysinfo
    from taninux.core import theme as T
    from taninux.update.sources import fetch_all
    from collections import Counter

    console = _console()
    console.print(f"\n  [{T.UME}]↻ scanne updates…[/]")
    items = fetch_all()
    if not items:
        console.print(f"  [{T.TAKE}]system ist aktuell.[/]\n")
        return 0
    counts = Counter(i.source for i in items)
    console.print(f"  [{T.YUKI}]{len(items)}[/] [{T.BASE5}]updates verfügbar[/]")
    console.print()

    cmds: list[list[str]] = []
    if counts.get("pacman") or counts.get("aur"):
        helper = "paru" if sysinfo.has_binary("paru") else (
            "yay" if sysinfo.has_binary("yay") else None
        )
        if helper:
            cmds.append([
                helper, "-Syu",
                "--noconfirm", "--sudoloop",
                "--skipreview", "--removemake", "--cleanafter",
                "--batchinstall",
            ])
        elif counts.get("pacman"):
            cmds.append(["sudo", "pacman", "-Syu", "--noconfirm"])
    if counts.get("flatpak"):
        cmds.append(["flatpak", "update", "--noninteractive"])
    if counts.get("fwupd"):
        cmds.append(["sudo", "fwupdmgr", "update", "--assume-yes", "--no-reboot-check"])

    rc_total = 0
    for argv in cmds:
        console.print(f"  [{T.SHINKAI}]$ {' '.join(argv)}[/]")
        rc = subprocess.run(argv, check=False).returncode
        if rc != 0:
            console.print(f"  [{T.RED}]exit {rc}[/]\n")
            rc_total = rc
        else:
            console.print(f"  [{T.TAKE}]ok[/]\n")
    return rc_total


# ---------------------------------------------------------------- health


def _health_summary() -> int:
    from taninux.core import sysinfo
    from taninux.core import theme as T

    console = _console()
    mem = sysinfo.memory()
    total = mem.get("MemTotal", 0)
    avail = mem.get("MemAvailable", 0)
    used = total - avail
    la = sysinfo.load_avg()
    ncpu = sysinfo.cpu_count()
    failed = sysinfo.systemctl_failed()
    errs_today = sysinfo.journal_errors_today()
    boot = sysinfo.boot_time_seconds()

    console.print()
    console.print(f"  [{T.UME}]◈ {sysinfo.hostname()}[/]  [{T.BASE5}]{sysinfo.distro_pretty()} · {sysinfo.kernel_release()}[/]")
    console.print()
    console.print(f"  [{T.SHINKAI}]uptime[/]     [{T.YUKI}]{int(sysinfo.uptime_seconds() // 3600)}h[/]")
    if boot:
        console.print(f"  [{T.SHINKAI}]boot[/]       [{T.YUKI}]{boot:.1f}s[/]")
    console.print(f"  [{T.SHINKAI}]load[/]       [{T.YUKI}]{la[0]:.2f}[/] [{T.BASE5}]{la[1]:.2f} {la[2]:.2f} ({ncpu} cpus)[/]")
    if total:
        console.print(f"  [{T.SHINKAI}]memory[/]     [{T.YUKI}]{used/1024**3:.1f}G[/] [{T.BASE5}]/[/] [{T.YUKI}]{total/1024**3:.1f}G[/] [{T.BASE4}]({used*100/total:.0f}%)[/]")
    console.print()

    for g in sysinfo.gpus():
        console.print(f"  [{T.SHINKAI}]gpu[/]        [{T.YUKI}]{g.vendor}[/] [{T.BASE5}]·[/] [{T.BASE5}]{g.model[:55]}[/]  [{T.CHIKYU}]{g.driver or '—'}[/]")
    console.print(f"  [{T.SHINKAI}]audio[/]      [{T.YUKI}]{sysinfo.audio_server()}[/]")

    console.print()
    failed_color = T.TAKE if not failed else T.RED
    console.print(f"  [{failed_color}]●[/] [{T.YUKI}]{len(failed)}[/] [{T.BASE5}]failed services[/]")
    for u in failed[:5]:
        console.print(f"    [{T.RED}]{u}[/]")
    err_color = T.TAKE if errs_today == 0 else (T.CHIKYU if errs_today < 20 else T.RED)
    err_label = f"{errs_today}" if errs_today >= 0 else "?"
    console.print(f"  [{err_color}]●[/] [{T.YUKI}]{err_label}[/] [{T.BASE5}]errors heute (journalctl -p 3)[/]")
    console.print()
    return 0


# ---------------------------------------------------------------- suggest


def _suggest_list() -> int:
    import subprocess
    from rich.table import Table

    from taninux.core import theme as T
    from taninux.suggest.rules import all_suggestions

    console = _console()
    installed = {
        l for l in subprocess.run(
            ["pacman", "-Qq"], capture_output=True, text=True, check=False,
        ).stdout.splitlines() if l
    }
    suggestions = sorted(
        all_suggestions(installed),
        key=lambda s: (s.priority, s.category),
    )

    console.print()
    console.print(
        f"  [{T.UME}]✦ suggest[/]   "
        f"[{T.YUKI}]{len(suggestions)}[/] [{T.BASE5}]empfehlungen[/]"
    )
    console.print()
    if not suggestions:
        console.print(f"  [{T.TAKE}]nichts zu empfehlen — solides setup.[/]\n")
        return 0

    table = Table(
        show_header=True,
        header_style=T.SHINKAI,
        border_style=T.BASE2,
        pad_edge=False,
        box=None,
    )
    table.add_column("kat", width=8)
    table.add_column("anlass")
    table.add_column("pakete")
    table.add_column("pri", width=6)
    for s in suggestions:
        pri_color = {1: T.RED, 2: T.CHIKYU, 3: T.BASE5}.get(s.priority, T.BASE5)
        pri_label = {1: "hoch", 2: "mittel", 3: "nice"}.get(s.priority, str(s.priority))
        table.add_row(
            f"[{T.SHINKAI}]{s.category}[/]",
            f"[{T.YUKI}]{s.reason}[/]",
            f"[{T.BASE5}]{', '.join(s.packages)}[/]",
            f"[{pri_color}]{pri_label}[/]",
        )
    console.print(table)
    console.print()
    return 0


# ---------------------------------------------------------------- snapshot


def _snapshot_save(label: str) -> int:
    from taninux.core import snapshot as snap
    from taninux.core import theme as T

    console = _console()
    s = snap.take_snapshot(label=label)
    console.print()
    console.print(
        f"  [{T.TAKE}]snapshot[/] [{T.YUKI}]{s.id}[/]   "
        f"[{T.BASE5}]({s.label})[/]"
    )
    console.print(
        f"  [{T.BASE5}]explicit[/] [{T.YUKI}]{len(s.explicit)}[/]   "
        f"[{T.BASE5}]aur[/] [{T.YUKI}]{len(s.aur)}[/]   "
        f"[{T.BASE5}]deps[/] [{T.YUKI}]{len(s.deps)}[/]"
    )
    console.print(f"  [{T.BASE4}]→ {s.path}[/]")
    console.print()
    return 0


def _snapshot_list() -> int:
    from rich.table import Table

    from taninux.core import snapshot as snap
    from taninux.core import theme as T

    console = _console()
    snaps = snap.list_snapshots()
    if not snaps:
        console.print(f"  [{T.BASE4}]keine snapshots — speichere mit:[/] taninux snapshot save")
        return 0

    table = Table(
        show_header=True,
        header_style=T.SHINKAI,
        border_style=T.BASE2,
        pad_edge=False,
        box=None,
    )
    table.add_column("id")
    table.add_column("zeitpunkt")
    table.add_column("label")
    table.add_column("explicit", justify="right")
    table.add_column("aur", justify="right")
    table.add_column("deps", justify="right")
    for s in snaps:
        table.add_row(
            f"[{T.YUKI}]{s.id}[/]",
            f"[{T.BASE5}]{s.timestamp}[/]",
            f"[{T.BASE5}]{s.label}[/]",
            f"[{T.CHIKYU}]{len(s.explicit)}[/]",
            f"[{T.CHIKYU}]{len(s.aur)}[/]",
            f"[{T.BASE4}]{len(s.deps)}[/]",
        )
    console.print()
    console.print(table)
    console.print()
    return 0


def _snapshot_diff(from_id: str | None, to_id: str | None) -> int:
    from taninux.core import snapshot as snap
    from taninux.core import theme as T

    console = _console()
    if from_id is None:
        old = snap.latest()
        if old is None:
            console.print(f"  [{T.BASE4}]keine snapshots vorhanden — speichere mit:[/] taninux snapshot save")
            return 1
    else:
        old = snap.load(from_id)
        if old is None:
            console.print(f"  [{T.RED}]kein snapshot mit id: {from_id}[/]")
            return 2

    if to_id is None:
        new = snap.current_snapshot()
    else:
        new_loaded = snap.load(to_id)
        if new_loaded is None:
            console.print(f"  [{T.RED}]kein snapshot mit id: {to_id}[/]")
            return 2
        new = new_loaded

    d = snap.diff(old, new)
    console.print()
    console.print(
        f"  [{T.UME}]drift[/]   "
        f"[{T.BASE5}]{old.id} ({old.label})[/] [{T.BASE3}]→[/] "
        f"[{T.BASE5}]{new.id} ({new.label})[/]"
    )
    console.print()

    total_added = len(d.added_explicit) + len(d.added_aur) + len(d.added_deps)
    total_removed = len(d.removed_explicit) + len(d.removed_aur) + len(d.removed_deps)
    if total_added == 0 and total_removed == 0:
        console.print(f"  [{T.TAKE}]keine änderungen seit {old.timestamp}.[/]")
        console.print()
        return 0

    def render_block(title: str, added: list[str], removed: list[str]) -> None:
        if not added and not removed:
            return
        console.print(f"  [{T.SHINKAI}]{title}[/]")
        for p in added:
            console.print(f"    [{T.TAKE}]+[/] [{T.YUKI}]{p}[/]")
        for p in removed:
            console.print(f"    [{T.RED}]-[/] [{T.BASE5}]{p}[/]")
        console.print()

    render_block("explicit", d.added_explicit, d.removed_explicit)
    render_block("aur", d.added_aur, d.removed_aur)
    render_block("deps", d.added_deps, d.removed_deps)

    console.print(
        f"  [{T.BASE4}]gesamt:[/] "
        f"[{T.TAKE}]+{total_added}[/]  "
        f"[{T.RED}]-{total_removed}[/]"
    )
    console.print()
    return 0


# ---------------------------------------------------------------- bundles


def _bundles_list() -> int:
    from rich.table import Table

    from taninux.bundles.loader import all_bundles
    from taninux.core import pacman as pac
    from taninux.core import theme as T

    console = _console()
    installed = pac.installed_packages()
    bundles = all_bundles()

    console.print()
    console.print(f"  [{T.UME}]◆ bundles[/]   [{T.YUKI}]{len(bundles)}[/] [{T.BASE5}]bundles[/]")
    console.print()

    table = Table(
        show_header=True,
        header_style=T.SHINKAI,
        border_style=T.BASE2,
        pad_edge=False,
        box=None,
    )
    table.add_column("", width=2)
    table.add_column("id", width=22)
    table.add_column("status")
    table.add_column("fortschritt")
    table.add_column("tagline")

    for b in bundles:
        inst = sum(1 for p in b.packages if p in installed)
        total = len(b.packages)
        state = "satisfied" if inst == total else ("partial" if inst else "none")
        glyph, color = {
            "satisfied": ("●", T.TAKE),
            "partial":   ("◐", T.CHIKYU),
            "none":      ("○", T.BASE4),
        }[state]
        ratio = inst / total if total else 0
        bar = "█" * int(ratio * 10) + "░" * (10 - int(ratio * 10))
        table.add_row(
            f"[{color}]{glyph}[/]",
            f"[{T.YUKI}]{b.id}[/]",
            f"[{color}]{state}[/]",
            f"[{color}]{bar}[/] [{T.BASE5}]{inst}/{total}[/]",
            f"[{T.BASE5}]{b.tagline}[/]",
        )
    console.print(table)
    console.print()
    return 0


def _bundles_export(bundle_id: str, label: str | None, tagline: str) -> int:
    import subprocess
    from taninux.bundles.loader import save_user_bundle
    from taninux.core import theme as T

    console = _console()
    try:
        r = subprocess.run(
            ["pacman", "-Qenq"],
            capture_output=True, text=True, check=False, timeout=15,
        )
    except (FileNotFoundError, subprocess.TimeoutExpired):
        console.print(f"  [{T.RED}]pacman nicht verfügbar[/]")
        return 2
    pkgs = sorted(l.strip() for l in r.stdout.splitlines() if l.strip())
    if not pkgs:
        console.print(f"  [{T.RED}]keine pakete gefunden[/]")
        return 2

    target = save_user_bundle(
        bundle_id=bundle_id,
        label=label or bundle_id,
        tagline=tagline,
        packages=pkgs,
        tags=["exported"],
    )
    console.print()
    console.print(
        f"  [{T.TAKE}]exportiert:[/] [{T.YUKI}]{target}[/]   "
        f"[{T.BASE5}]{len(pkgs)} pakete[/]"
    )
    console.print(
        f"  [{T.BASE4}]restore via:[/]  taninux bundles install {bundle_id}"
        f"   [{T.BASE4}](in TUI: bundles tab → cursor auf '{bundle_id}' → i)[/]"
    )
    console.print()
    return 0


# ---------------------------------------------------------------- whitelist


def _whitelist_list() -> int:
    from taninux.core.state import State
    from taninux.core import theme as T

    console = _console()
    state = State.load()
    if not state.whitelist and not state.ignored:
        console.print(f"  [{T.BASE4}]whitelist und ignore-list sind leer.[/]")
        return 0

    home = str(Path.home())
    if state.whitelist:
        console.print(f"\n  [{T.SHINKAI}]whitelist[/]")
        for p in sorted(state.whitelist):
            console.print(f"    [{T.YUKI}]{str(p).replace(home, '~', 1)}[/]")
    if state.ignored:
        console.print(f"\n  [{T.BASE4}]ignored[/]")
        for p in sorted(state.ignored):
            console.print(f"    [{T.BASE5}]{str(p).replace(home, '~', 1)}[/]")
    return 0


def _whitelist_add(path: str) -> int:
    from taninux.core.state import State
    from taninux.core import theme as T

    console = _console()
    p = Path(path).expanduser().resolve()
    state = State.load()
    state.whitelist_add(p)
    console.print(f"  [{T.SHINKAI}]+ whitelist:[/] [{T.YUKI}]{p}[/]")
    return 0


def _whitelist_remove(path: str) -> int:
    from taninux.core.state import State
    from taninux.core import theme as T

    console = _console()
    p = Path(path).expanduser().resolve()
    state = State.load()
    state.whitelist_remove(p)
    console.print(f"  [{T.BASE5}]- whitelist:[/] [{T.YUKI}]{p}[/]")
    return 0


# ---------------------------------------------------------------- entry


def main() -> int:
    parser = argparse.ArgumentParser(
        prog="taninux",
        description="Mountain-themed Arch Linux maintenance TUI.",
    )
    sub = parser.add_subparsers(dest="cmd")

    sub.add_parser("tui", help="Open the interactive TUI (default)")
    p_scan = sub.add_parser("scan", help="Print findings without the TUI")
    p_scan.add_argument(
        "--category",
        choices=[
            "all", "dotfolders", "orphans", "pacnew", "cache",
            "flatpak-runtimes", "flatpak-data", "aur-cache",
            "journal", "coredumps", "kernels",
        ],
        default="all",
        help="Scanner-Kategorie (default: all)",
    )

    p_trash = sub.add_parser("trash", help="Trash management")
    p_trash_sub = p_trash.add_subparsers(dest="trash_cmd")
    p_trash_sub.add_parser("list", help="List trashed entries")
    p_restore = p_trash_sub.add_parser("restore", help="Restore an entry")
    p_restore.add_argument("id", help="Trash entry id")
    p_purge = p_trash_sub.add_parser("purge", help="Permanently delete trash")
    p_purge.add_argument("id", nargs="?", help="Trash entry id (or omit for --all)")
    p_purge.add_argument("--all", action="store_true", help="Purge everything")
    p_purge.add_argument("--yes", action="store_true", help="Skip confirmation")

    p_update = sub.add_parser("update", help="List pending updates from all sources")
    p_update.add_argument("--execute", "-y", action="store_true",
                          help="Updates direkt ausführen (paru/flatpak/fwupdmgr, --noconfirm)")
    sub.add_parser("health", help="System health summary (headless)")
    sub.add_parser("suggest", help="System-aware package suggestions")

    p_snap = sub.add_parser("snapshot", help="Pacman state snapshots / drift")
    p_snap_sub = p_snap.add_subparsers(dest="snap_cmd")
    p_s_save = p_snap_sub.add_parser("save", help="Aktuellen State speichern")
    p_s_save.add_argument("--label", default="manual", help="Optionales Label")
    p_snap_sub.add_parser("list", help="Alle Snapshots zeigen")
    p_s_diff = p_snap_sub.add_parser("diff", help="Diff zwischen Snapshot und jetzt (oder zweier Snapshots)")
    p_s_diff.add_argument("from_id", nargs="?", help="Snapshot-ID (default: latest)")
    p_s_diff.add_argument("to_id", nargs="?", help="Zweite Snapshot-ID (default: current)")

    p_bundles = sub.add_parser("bundles", help="Bundle management")
    p_bundles_sub = p_bundles.add_subparsers(dest="bundles_cmd")
    p_bundles_sub.add_parser("list", help="Liste alle bundles (builtin + user)")
    p_b_export = p_bundles_sub.add_parser("export", help="Aktuelles System als Bundle speichern")
    p_b_export.add_argument("id", help="Bundle-ID (= dateiname ohne .toml)")
    p_b_export.add_argument("--label", default=None, help="Display-Name")
    p_b_export.add_argument("--tagline", default="exported by taninux", help="Kurze Beschreibung")
    p_b_export.add_argument("--explicit-only", action="store_true", default=True,
                            help="Nur pacman -Qe (explizit installierte, ohne deps)")

    p_wl = sub.add_parser("whitelist", help="Whitelist management")
    p_wl_sub = p_wl.add_subparsers(dest="wl_cmd")
    p_wl_sub.add_parser("list", help="Show whitelist + ignore-list")
    p_add = p_wl_sub.add_parser("add", help="Add a path to the whitelist")
    p_add.add_argument("path")
    p_rm = p_wl_sub.add_parser("remove", help="Remove a path from the whitelist")
    p_rm.add_argument("path")

    args = parser.parse_args()

    if args.cmd in (None, "tui"):
        from taninux.app import run
        run()
        return 0
    if args.cmd == "scan":
        return _scan(args.category)
    if args.cmd == "update":
        if args.execute:
            return _update_execute()
        return _update_list()
    if args.cmd == "health":
        return _health_summary()
    if args.cmd == "suggest":
        return _suggest_list()
    if args.cmd == "bundles":
        if args.bundles_cmd == "list":
            return _bundles_list()
        if args.bundles_cmd == "export":
            return _bundles_export(args.id, args.label, args.tagline)
        p_bundles.print_help()
        return 1
    if args.cmd == "snapshot":
        if args.snap_cmd == "save":
            return _snapshot_save(args.label)
        if args.snap_cmd == "list":
            return _snapshot_list()
        if args.snap_cmd == "diff":
            return _snapshot_diff(args.from_id, args.to_id)
        p_snap.print_help()
        return 1
    if args.cmd == "trash":
        if args.trash_cmd == "list":
            return _trash_list()
        if args.trash_cmd == "restore":
            return _trash_restore(args.id)
        if args.trash_cmd == "purge":
            if args.all:
                return _trash_purge_all(args.yes)
            return _trash_purge(args.id)
        p_trash.print_help()
        return 1
    if args.cmd == "whitelist":
        if args.wl_cmd == "list":
            return _whitelist_list()
        if args.wl_cmd == "add":
            return _whitelist_add(args.path)
        if args.wl_cmd == "remove":
            return _whitelist_remove(args.path)
        p_wl.print_help()
        return 1

    parser.print_help()
    return 1


if __name__ == "__main__":
    sys.exit(main())
