#!/usr/bin/python3 -su

# Copyright (C) 2025 - 2025 ENCRYPTED SUPPORT LLC <adrelanos@whonix.org>
# See the file COPYING for copying conditions.

# pylint: disable=invalid-name

"""
selectapplicationpage.py - Displays a list of package installation methods
to the user.
"""

import functools
from enum import Enum

from PyQt5.QtCore import pyqtSignal
from PyQt5.QtWidgets import (
    QWidget,
    QVBoxLayout,
    QRadioButton,
)

## NOTE: _ui modules are autogenerated by build-ui.sh.
from browser_choice.chooseinstallationpage_ui import Ui_ChooseInstallationPage

from browser_choice.packagecard import PackageCard
from browser_choice.cardview import CardView
from browser_choice import GlobalData


class ManageMode(Enum):
    """
    An enum indicating what kind of software management the user wants to
    perform.
    """

    Unknown = 0
    Install = 1
    UpdateAndInstall = 2
    Remove = 3
    Purge = 4
    Run = 5


class ChooseInstallationPage(QWidget):
    """
    A wizard screen widget allowing the user to choose what repository to
    install an application from.
    """

    backClicked: pyqtSignal = pyqtSignal()
    continueClicked: pyqtSignal = pyqtSignal()

    # pylint: disable=too-many-arguments
    def __init__(
        self,
        app_name: str,
        card_list: list[PackageCard],
        is_network_connected: bool,
        in_sysmaint_session: bool,
        user_sysmaint_split_installed: bool,
        parent: QWidget | None = None,
    ):
        super().__init__(parent)
        self.ui = Ui_ChooseInstallationPage()
        self.ui.setupUi(self)

        self.ui.backButton.clicked.connect(self.backClicked)
        self.ui.continueButton.clicked.connect(self.continueClicked)

        self.is_network_connected: bool = is_network_connected
        self.in_sysmaint_session: bool = in_sysmaint_session
        self.user_sysmaint_split_installed: bool = user_sysmaint_split_installed

        self.ui.appNameChoiceLabel.setText(
            f"Choose the package for '{app_name}' to install or remove."
        )
        self.ui.continueButton.setEnabled(False)

        if (
            (self.in_sysmaint_session and self.user_sysmaint_split_installed)
            or GlobalData.qube_type == "templatevm"
            or GlobalData.uid == 0
        ):
            self.ui.runRadioButton.setText("Run (User session required)")
        else:
            self.ui.runRadioButton.setText("Run")

        self.card_view_layout: QVBoxLayout = QVBoxLayout(
            self.ui.packageChooserWidget
        )
        self.card_view: CardView = CardView("PackageCard", self)
        for card in card_list:
            self.card_view.add_card(card)
            card.toggled.connect(
                functools.partial(
                    self.update_current_card,
                    card,
                )
            )
        self.card_view_layout.addWidget(self.card_view)

        self.current_card: PackageCard | None = None

        self.ui.installRadioButton.setEnabled(False)
        self.ui.noUpdateCheckbox.setEnabled(False)
        self.ui.removeRadioButton.setEnabled(False)
        self.ui.purgeRadioButton.setEnabled(False)
        self.ui.runRadioButton.setEnabled(False)

        self.ui.installRadioButton.toggled.connect(
            functools.partial(self.update_available_actions)
        )
        self.ui.removeRadioButton.toggled.connect(
            functools.partial(self.update_available_actions)
        )
        self.ui.purgeRadioButton.toggled.connect(
            functools.partial(self.update_available_actions)
        )
        self.ui.runRadioButton.toggled.connect(
            functools.partial(self.update_available_actions)
        )

    def disable_radio_button(self, radio_button: QRadioButton) -> None:
        """
        Convenience function for disabling and unchecking a radio button that
        is being disabled.
        """

        if radio_button.isChecked():
            ## setAutoExclusive(False) is needed to allow unchecking buttons.
            ## See https://forum.qt.io/topic/118139/radio-buttons-not-getting-unchecked/4
            radio_button.setAutoExclusive(False)
            radio_button.setChecked(False)
            radio_button.setAutoExclusive(True)
        radio_button.setEnabled(False)

    # pylint: disable=too-many-branches
    def update_available_actions(self) -> None:
        """
        Enables or disables software modification radio buttons and
        checkboxes as appropriate for the user's currently chosen settings.
        """

        if self.current_card is None:
            return

        if (
            self.ui.installRadioButton.isChecked()
            or self.ui.removeRadioButton.isChecked()
            or self.ui.purgeRadioButton.isChecked()
            or self.ui.runRadioButton.isChecked()
        ):
            self.ui.continueButton.setEnabled(True)
        else:
            self.ui.continueButton.setEnabled(False)

        if self.ui.installRadioButton.isChecked():
            if self.current_card.supports_update and self.is_network_connected:
                self.ui.noUpdateCheckbox.setEnabled(True)
            else:
                self.ui.noUpdateCheckbox.setChecked(False)
                self.ui.noUpdateCheckbox.setEnabled(False)
        else:
            self.ui.noUpdateCheckbox.setChecked(False)
            self.ui.noUpdateCheckbox.setEnabled(False)

    def update_current_card(
        self,
        source_card: PackageCard,
    ) -> None:
        """
        Qt signal handler. Triggered when the user changes the currently
        selected card.
        """

        if source_card.isChecked():
            self.current_card = source_card

            if (
                self.current_card.mod_requires_privileges
                and not self.in_sysmaint_session
                and self.user_sysmaint_split_installed
                and GlobalData.uid != 0
            ):
                self.ui.installRadioButton.setText(
                    "Install (Sysmaint mode or root privileges required)"
                )
                self.ui.removeRadioButton.setText(
                    "Remove (Sysmaint mode or root privileges required)"
                )
                self.ui.purgeRadioButton.setText(
                    "Purge (Sysmaint mode or root privileges required)"
                )
            else:
                if self.current_card.is_installed:
                    self.ui.installRadioButton.setText(
                        "Install (Already installed)"
                    )
                else:
                    self.ui.installRadioButton.setText("Install")
                self.ui.removeRadioButton.setText("Remove")
                self.ui.purgeRadioButton.setText("Purge")

            # pylint: disable=too-many-boolean-expressions
            if (
                self.current_card.supports_install
                and self.is_network_connected
                and (
                    not self.current_card.mod_requires_privileges
                    or self.in_sysmaint_session
                    or not self.user_sysmaint_split_installed
                    or GlobalData.uid == 0
                )
                and not self.current_card.is_installed
            ):
                self.ui.installRadioButton.setEnabled(True)
            else:
                self.disable_radio_button(self.ui.installRadioButton)

            if (
                self.current_card.supports_remove
                and self.current_card.is_installed
                and (
                    not self.current_card.mod_requires_privileges
                    or self.in_sysmaint_session
                    or not self.user_sysmaint_split_installed
                    or GlobalData.uid == 0
                )
            ):
                self.ui.removeRadioButton.setEnabled(True)
            else:
                self.disable_radio_button(self.ui.removeRadioButton)

            if (
                self.current_card.supports_purge
                and self.current_card.is_installed
                and (
                    not self.current_card.mod_requires_privileges
                    or self.in_sysmaint_session
                    or not self.user_sysmaint_split_installed
                    or GlobalData.uid == 0
                )
            ):
                self.ui.purgeRadioButton.setEnabled(True)
            else:
                self.disable_radio_button(self.ui.purgeRadioButton)

            if self.current_card.is_installed and (
                (
                    not self.in_sysmaint_session
                    or not self.user_sysmaint_split_installed
                )
                and not GlobalData.qube_type == "templatevm"
                and GlobalData.uid != 0
            ):
                self.ui.runRadioButton.setEnabled(True)
            else:
                self.disable_radio_button(self.ui.runRadioButton)

            self.update_available_actions()

    # pylint: disable=too-many-return-statements
    def manageMode(self) -> ManageMode:
        """
        Gets the software modification mode currently selected by the user.
        """

        if self.current_card is None:
            return ManageMode.Unknown

        if self.ui.installRadioButton.isChecked():
            if self.ui.noUpdateCheckbox.isChecked():
                return ManageMode.Install
            if self.current_card.supports_update:
                return ManageMode.UpdateAndInstall
            return ManageMode.Install
        if self.ui.removeRadioButton.isChecked():
            return ManageMode.Remove
        if self.ui.purgeRadioButton.isChecked():
            return ManageMode.Purge
        if self.ui.runRadioButton.isChecked():
            return ManageMode.Run
        return ManageMode.Unknown
