#!/usr/bin/python3
"""motel-compat-manager — list, repair and remove Windows applications.

Deliberately plain. The user should almost never need this; it exists for when
something went wrong, so it prioritises telling the truth about state over
looking impressive.
"""
from __future__ import annotations

import shutil
import subprocess
import sys
from pathlib import Path

sys.path.insert(0, "/usr/share/motel/compat")

import integrate  # noqa: E402
import prefix  # noqa: E402
from ui import ask_yes_no, show_error, show_info  # noqa: E402

KDIALOG = shutil.which("kdialog")


def human_size(path: Path) -> str:
    try:
        total = sum(f.stat().st_size for f in path.rglob("*") if f.is_file())
    except OSError:
        return "unknown size"
    for unit in ("B", "KB", "MB", "GB"):
        if total < 1024:
            return f"{total:.0f} {unit}"
        total /= 1024
    return f"{total:.1f} TB"


def list_text() -> str:
    prefixes = prefix.list_prefixes()
    if not prefixes:
        return "No Windows applications are installed."

    lines = []
    for info in prefixes:
        state = "OK" if info.installed_exe and Path(info.installed_exe).is_file() \
            else "incomplete"
        lines.append(
            f"{info.name}  [{info.app_id}]\n"
            f"    status: {state}, {info.runtime}, {human_size(info.path)}"
        )
    return "\n\n".join(lines)


def choose_app(title: str) -> str | None:
    prefixes = prefix.list_prefixes()
    if not prefixes:
        show_info("Windows Applications", "No Windows applications are installed.")
        return None

    if not KDIALOG:
        print(list_text())
        try:
            return input("\nApplication id: ").strip() or None
        except EOFError:
            return None

    args = [KDIALOG, "--title", title, "--menu", "Choose an application:"]
    for info in prefixes:
        args += [info.app_id, info.name]

    result = subprocess.run(args, capture_output=True, text=True, check=False)
    return result.stdout.strip() or None


def cmd_list() -> int:
    if KDIALOG:
        subprocess.run(
            [KDIALOG, "--title", "Windows Applications",
             "--msgbox", list_text()],
            check=False,
        )
    else:
        print(list_text())
    return 0


def cmd_remove() -> int:
    app_id = choose_app("Remove a Windows application")
    if not app_id:
        return 0

    info = next((p for p in prefix.list_prefixes() if p.app_id == app_id), None)
    if info is None:
        show_error("Not found", f"“{app_id}” is not installed.")
        return 1

    if not ask_yes_no(
        "Remove application?",
        f"Remove <b>{info.name}</b> and everything it stored?\n\n"
        f"This frees {human_size(info.path)} and cannot be undone.",
    ):
        return 0

    try:
        integrate.remove_integration(app_id)
        prefix.remove(app_id)
    except prefix.PrefixError as exc:
        show_error("Could not remove it", str(exc))
        return 1

    show_info("Removed", f"{info.name} has been removed.")
    return 0


def cmd_repair() -> int:
    """Rebuild the menu entry for an application whose launcher went missing."""
    app_id = choose_app("Repair a Windows application")
    if not app_id:
        return 0

    info = next((p for p in prefix.list_prefixes() if p.app_id == app_id), None)
    if info is None:
        show_error("Not found", f"“{app_id}” is not installed.")
        return 1

    exe = integrate.find_main_executable(info)
    if exe is None:
        show_error(
            "Nothing to repair",
            f"No program file could be found for {info.name}.\n\n"
            "The installation is probably incomplete; removing and "
            "reinstalling is the reliable fix.",
        )
        return 1

    icon = integrate.extract_icon(exe, app_id)
    integrate.create_desktop_entry(info, exe, icon)
    info.installed_exe = str(exe)
    prefix._write_metadata(info)

    show_info("Repaired", f"{info.name} is back in your application menu.")
    return 0


COMMANDS = {
    "list": cmd_list,
    "remove": cmd_remove,
    "repair": cmd_repair,
}


def main(argv: list[str]) -> int:
    if len(argv) > 1:
        command = argv[1]
        if command not in COMMANDS:
            print(f"usage: motel-compat-manager [{'|'.join(COMMANDS)}]",
                  file=sys.stderr)
            return 2
        return COMMANDS[command]()

    if not KDIALOG:
        return cmd_list()

    result = subprocess.run(
        [KDIALOG, "--title", "Windows Applications", "--menu", "What would you like to do?",
         "list", "Show installed applications",
         "repair", "Put an application back in the menu",
         "remove", "Remove an application"],
        capture_output=True, text=True, check=False,
    )
    choice = result.stdout.strip()
    if not choice:
        return 0
    return COMMANDS[choice]()


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