"""App-Detail-Metadaten + Quellen-Probing. UI-frei.

Holt Beschreibung/Version/Größe/Lizenz für ein Paket aus pacman/paru/flatpak
und ermittelt, aus welchen Quellen (Repo · AUR · Flatpak) es installierbar ist.
Cross-Source-Mapping ist namensbasiert (Repo/AUR teilen oft den Paketnamen);
einen echten AppStream-Mapping gibt es bewusst nicht.
"""
from __future__ import annotations

import shutil
import subprocess
from dataclasses import dataclass

from taninux.core import pacman

SOURCE_LABEL = {
    "repo": "Official repository",
    "aur": "AUR (built from source)",
    "flatpak": "Flatpak (Flathub)",
}


@dataclass
class Detail:
    source: str           # 'repo' | 'aur' | 'flatpak'
    pkg: str              # pacman-Name bzw. Flatpak-App-ID
    version: str = ""
    summary: str = ""
    description: str = ""
    size: str = ""        # Download-Größe
    installed_size: str = ""
    license: str = ""
    url: str = ""
    remote: str = "flathub"
    available: bool = False
    installed: bool = False


def _run(argv: list[str], timeout: int = 20):
    try:
        return subprocess.run(argv, capture_output=True, text=True,
                              check=False, timeout=timeout)
    except (FileNotFoundError, subprocess.TimeoutExpired):
        return None


def _parse_kv(text: str) -> dict[str, str]:
    out: dict[str, str] = {}
    for line in text.splitlines():
        if ":" not in line:
            continue
        key, _, val = line.partition(":")
        key = key.strip().lower()
        if key and key not in out:
            out[key] = val.strip()
    return out


def _pacman_detail(name: str, *, aur: bool) -> Detail | None:
    helper = shutil.which("paru") or shutil.which("yay")
    argv = [helper, "-Si", name] if (aur and helper) else ["pacman", "-Si", name]
    r = _run(argv)
    if r is None or r.returncode != 0 or not r.stdout.strip():
        return None
    kv = _parse_kv(r.stdout)
    # paru -Si markiert AUR-Pakete mit "Repository: aur"; sonst ist's Repo.
    source = "aur" if kv.get("repository") == "aur" else "repo"
    return Detail(
        source=source,
        pkg=name,
        version=kv.get("version", ""),
        summary=kv.get("description", ""),
        description=kv.get("description", ""),
        size=kv.get("download size", ""),
        installed_size=kv.get("installed size", ""),
        license=kv.get("licenses", ""),
        url=kv.get("url", ""),
        available=True,
        installed=pacman.is_installed(name),
    )


def _flatpak_detail(app_id: str, remote: str = "flathub") -> Detail | None:
    if not shutil.which("flatpak"):
        return None
    r = _run(["flatpak", "remote-info", remote, app_id])
    if r is None or r.returncode != 0 or not r.stdout.strip():
        return None
    kv = _parse_kv(r.stdout)
    summary = ""
    first = r.stdout.strip().splitlines()[0].strip()
    if " - " in first:
        summary = first.split(" - ", 1)[1].strip()
    installed = False
    chk = _run(["flatpak", "info", app_id], timeout=8)
    if chk is not None and chk.returncode == 0:
        installed = True
    return Detail(
        source="flatpak",
        pkg=app_id,
        version=kv.get("version", ""),
        summary=summary,
        description=summary,
        size=kv.get("download size", ""),
        installed_size=kv.get("installed size", ""),
        license=kv.get("license", ""),
        url="",
        remote=remote,
        available=True,
        installed=installed,
    )


def candidates(*, repo_name: str | None = None, flatpak_id: str | None = None,
               remote: str = "flathub", include_aur: bool = False) -> list[Detail]:
    """Verfügbare Installationsquellen für eine App (blockierend → Worker)."""
    out: list[Detail] = []
    if repo_name:
        d = _pacman_detail(repo_name, aur=False)
        if d and d.source == "repo":
            out.append(d)
        if include_aur:
            a = _pacman_detail(repo_name, aur=True)
            if a and a.source == "aur":   # nur echte AUR-Treffer
                out.append(a)
    if flatpak_id:
        f = _flatpak_detail(flatpak_id, remote)
        if f:
            out.append(f)
    return out


def install_argv(d: Detail) -> tuple[list[str], bool]:
    if d.source == "flatpak":
        return ["flatpak", "install", "--assumeyes", d.remote, d.pkg], False
    if d.source == "aur":
        helper = shutil.which("paru") or shutil.which("yay") or "paru"
        return [helper, "--sudo", "pkexec", "-S", "--noconfirm", d.pkg], False
    return ["pacman", "-S", "--needed", "--noconfirm", d.pkg], True


def remove_argv(d: Detail) -> tuple[list[str], bool]:
    if d.source == "flatpak":
        return ["flatpak", "uninstall", "--assumeyes", d.pkg], False
    return ["pacman", "-Rns", "--noconfirm", d.pkg], True
