#!/usr/bin/env python3
"""Ubuntu Studio Installer."""

from __future__ import annotations

import os
import sys
from pathlib import Path

_pkg_dir = Path(__file__).resolve().parent
if str(_pkg_dir) not in sys.path:
    sys.path.insert(0, str(_pkg_dir))

from backend import (  # noqa: E402
    APP_TITLE,
    apt_cache_depends_recommends,
    autoremove_packages,
    build_package_table,
    dpkg_installed,
    install_packages,
    run_installer_fix,
    update_apt_cache,
)
from ui.base import AudioConfigUI, ChecklistItem  # noqa: E402
from i18n import _  # noqa: E402


# ======================================================================


def _detect_toolkit() -> str:
    """Return 'qt' or 'gtk' based on the running desktop.

    Qt desktops:  KDE Plasma (Kubuntu, Ubuntu Studio), LXQt (Lubuntu)
    GTK desktops: GNOME (Ubuntu), Xfce (Xubuntu), MATE (Ubuntu MATE),
                  Budgie (Ubuntu Budgie), Cinnamon (Ubuntu Cinnamon),
                  Unity (Ubuntu Unity)
    """
    desktop = os.environ.get("XDG_CURRENT_DESKTOP", "").lower()
    session = os.environ.get("DESKTOP_SESSION", "").lower()
    if any(tok in desktop for tok in ("kde", "plasma", "lxqt")):
        return "qt"
    if "plasma" in session or "lxqt" in session:
        return "qt"
    return "gtk"


def _create_ui() -> AudioConfigUI:
    """Pick the right UI backend, with fallback."""
    preferred = _detect_toolkit()
    if preferred == "qt":
        try:
            from ui.qt_ui import QtUI
            ui = QtUI()
            ui.init()
            return ui
        except ImportError:
            pass  # fall through to GTK
    try:
        from ui.gtk_ui import GtkUI
        ui = GtkUI()
        ui.init()
        return ui
    except ImportError:
        pass
    # Last resort: try Qt even on non-KDE desktops
    try:
        from ui.qt_ui import QtUI
        ui = QtUI()
        ui.init()
        return ui
    except ImportError:
        print(
            _("ERROR: Neither GTK 4 (python3-gi + gir1.2-adw-1) nor "
              "PyQt6 (python3-pyqt6) is available."),
            file=sys.stderr,
        )
        sys.exit(1)


# ======================================================================


def _main_loop(ui: AudioConfigUI) -> None:
    """Run the main installer loop."""

    while True:
        # Refresh apt cache
        ui.show_progress(
            APP_TITLE,
            _("Updating package cache…"),
            lambda out, _prog: update_apt_cache(on_output=out),
        )

        # Build the package table (with a progress indication)
        table = [None]

        def _build():
            table[0] = build_package_table()

        ui.show_progress(
            APP_TITLE,
            _("Building Package Table…"),
            lambda _out, _prog: _build(),
        )
        pkg_table = table[0]
        if not pkg_table:
            ui.show_error(
                APP_TITLE,
                _("Failed to load the metapackage configuration."),
            )
            return

        # Build checklist items
        child_pkg_to_keys: dict[str, list[str]] = {}
        child_pkg_installed: dict[str, bool] = {}
        items = [
            ChecklistItem(
                key=pkg.name,
                label=pkg.name,
                description=pkg.description,
                checked=pkg.installed,
                children=[
                    ChecklistItem(
                        key=f"{pkg.name}::{rel.relation}::{rel.package}",
                        label=rel.package,
                        description=_("{relation} of {package}").format(
                            relation=rel.relation,
                            package=pkg.name,
                        ),
                        checked=dpkg_installed(rel.package),
                    )
                    for rel in apt_cache_depends_recommends(
                        pkg.name,
                        include_depends=pkg.show_depends,
                        include_recommends=pkg.show_recommends,
                    )
                ],
            )
            for pkg in pkg_table
        ]

        for item in items:
            if not item.children:
                continue
            for child in item.children:
                child_pkg = child.label
                child_pkg_to_keys.setdefault(child_pkg, []).append(child.key)
                child_pkg_installed.setdefault(child_pkg, child.checked)

        selected = ui.show_checklist(
            APP_TITLE,
            _("Select or deselect package(s) to modify installation status."),
            items,
            ok_label=_("Modify Installed Package Selection"),
            cancel_label=_("Close"),
            width=950,
            height=600,
        )

        if selected is None:
            return  # user closed

        selected_set = set(selected)
        installed_names = {pkg.name for pkg in pkg_table if pkg.installed}
        metapackage_names = {pkg.name for pkg in pkg_table}
        selected_metapackages = selected_set.intersection(metapackage_names)
        selected_children = {key for key in selected_set if "::" in key}

        desired_child_installed: dict[str, bool] = {}
        for child_pkg, keys in child_pkg_to_keys.items():
            desired_child_installed[child_pkg] = any(key in selected_children for key in keys)

        # --- Packages to install (selected but not installed) ---
        to_install_metas = selected_metapackages - installed_names
        to_install_children = {
            child_pkg
            for child_pkg, desired in desired_child_installed.items()
            if desired and not child_pkg_installed.get(child_pkg, False)
        }
        to_install = sorted(to_install_metas.union(to_install_children))

        if to_install:
            install_msg = _("The following packages will be installed by this "
                            "action:\n\n{packages}\n\n"
                            "You may be asked for your password more than once.\n"
                            "Do you wish to continue?").format(
                                packages="\n".join(to_install))

            if ui.show_question(
                APP_TITLE,
                install_msg,
                ok_label=_("Continue"),
                cancel_label=_("Skip/Go Back"),
            ):
                install_result = [False]

                def _do_install(on_output, on_progress):
                    install_result[0] = install_packages(
                        to_install, on_output=on_output, on_progress=on_progress,
                    )

                ui.show_progress(
                    APP_TITLE,
                    _("Installing packages…"),
                    _do_install,
                )

                if not install_result[0]:
                    ui.show_error(
                        APP_TITLE,
                        _("Package installation failed."),
                    )

                # If ubuntustudio-audio was just installed, run the fix
                if "ubuntustudio-audio" in to_install:
                    run_installer_fix()
                    ui.show_warning(
                        APP_TITLE,
                        _("Please log out and log in for proper audio "
                          "configuration to take effect."),
                    )

        # --- Packages to remove (installed but not selected) ---
        to_remove_metas = installed_names - selected_metapackages
        to_remove_children = {
            child_pkg
            for child_pkg, desired in desired_child_installed.items()
            if not desired and child_pkg_installed.get(child_pkg, False)
        }
        to_remove = sorted(to_remove_metas.union(to_remove_children))

        if to_remove:
            remove_msg = _(
                "The following packages are requested for removal:\n\n"
                "{packages}\n\n"
                "APT will resolve dependencies and remove only what is "
                "safe to remove based on package relationships.\n\n"
                "If you do not wish to have any one of these individual "
                "packages removed, simply reinstall them. No settings are "
                "being removed by uninstallation. Additionally, some of "
                "these packages may be required by other packages and may "
                "not be uninstalled, so parts of this list may not apply "
                "to you."
            ).format(packages="\n".join(to_remove))

            if ui.show_question(
                APP_TITLE,
                remove_msg,
                ok_label=_("Continue"),
                cancel_label=_("Skip/Go Back"),
            ):
                ui.show_progress(
                    APP_TITLE,
                    _("Removing packages…"),
                    lambda out, prog: autoremove_packages(
                        to_remove, on_output=out, on_progress=prog,
                    ),
                )


# ======================================================================


def main() -> None:
    ui = _create_ui()
    _main_loop(ui)
    ui.quit()


if __name__ == "__main__":
    main()
