# Polychromatic is licensed under the GPLv3.
# Copyright (C) 2020-2024 Luke Horwell <code@horwell.me>
"""
This module contains the frontend for the troubleshooter.
"""

import time

from PyQt6.QtCore import QObject, Qt, QThread, pyqtSignal
from PyQt6.QtGui import QBrush, QColor, QFont, QIcon
from PyQt6.QtWidgets import (QApplication, QDialog, QLabel, QProgressBar,
                             QPushButton, QTreeWidget, QTreeWidgetItem)

from .. import common
from . import shared


class TroubleshooterGUI(QObject):
    """
    Runs the troubleshooter and shows the results in a dialog box.
    """
    signal_progress_bar_advance = pyqtSignal()
    signal_progress_bar_set_max = pyqtSignal(int)

    def __init__(self, appdata, backend, backend_human_name):
        # Required for slot/signals to work
        super().__init__()

        self.appdata = appdata
        self.backend = backend
        self.human_name = backend_human_name
        self._ = appdata._
        self.dbg = self.appdata.dbg
        self.widgets = shared.PolychromaticWidgets(appdata)

        self.result_window = shared.get_ui_widget(self.appdata, "troubleshooter", QDialog)
        self.result_label = self.result_window.findChild(QLabel, "Title")
        self.result_tree = self.result_window.findChild(QTreeWidget, "Results")
        self.result_tree_root = self.result_tree.invisibleRootItem()
        self.result_copy_btn = self.result_window.findChild(QPushButton, "CopyToClipboard")

        if not self.appdata.system_qt_theme:
            self.result_window.findChild(QPushButton, "Close").setIcon(self.widgets.get_icon_qt("general", "close"))

        self.loading = shared.get_ui_widget(self.appdata, "loading", QDialog)
        self.loading_label = self.loading.findChild(QLabel, "Label")
        self.loading_progress_bar = self.loading.findChild(QProgressBar, "ProgressBar")

        self.signal_progress_bar_advance.connect(self.progress_bar_advance)
        self.signal_progress_bar_set_max.connect(self.progress_bar_set_max)

        self.thread = self.TroubleshootThread()
        self.thread.parent = self
        self.thread.result = []
        self.thread.finished.connect(self.finished)

        self.start()

    def progress_bar_advance(self):
        self.loading_progress_bar.setValue(self.loading_progress_bar.value() + 1)

    def progress_bar_set_max(self, max_value):
        self.loading_progress_bar.setRange(0, 0)
        self.loading_progress_bar.setMaximum(max_value)

    class TroubleshootThread(QThread):
        """
        Troubleshooting runs in a separate thread to prevent UI lock up, in case
        the code takes longer to run on some systems.
        """
        def run(self):
            _ = self.parent._

            self.parent.loading_label.setText(_("Running troubleshooter..."))
            self.result = self.parent.appdata.middleman.troubleshoot(self.parent.backend, _, self.parent.signal_progress_bar_set_max.emit, self.parent.signal_progress_bar_advance.emit)

    def start(self):
        self.dbg.stdout("Running troubleshooter backend: {0}".format(self.backend), self.dbg.action, 1)
        self.loading_label.setText(self._("Running troubleshooter..."))
        self.loading.setWindowTitle(self._("Troubleshooting..."))
        self.loading.setWindowFlag(Qt.WindowType.WindowMinimizeButtonHint, False)
        self.loading.setWindowFlag(Qt.WindowType.WindowMaximizeButtonHint, False)
        self.loading.open()

        self.thread.start()

    def finished(self):
        results = self.thread.result
        all_passed = True
        self.loading.close()

        if type(results) == None:
            return self.widgets.open_dialog(self.widgets.dialog_warning,
                                            self._("Troubleshooting Failed"),
                                            self._("The troubleshooter for this backend is not available for this operating system."))

        elif type(results) == str:
            return self.widgets.open_dialog(self.widgets.dialog_error,
                                            self._("Troubleshooting Failed"),
                                            self._("An exception was thrown while running the troubleshooter. This is probably a bug."),
                                            details=results)

        for result in results:
            item = QTreeWidgetItem()

            # "passed" must be either: True (Passed); False (Failed); None (Unknown)
            if result["passed"] is True:
                item.setText(0, self._("Passed"))
                item.setIcon(0, QIcon(common.get_icon("general", "success")))
            elif result["passed"] is False:
                item.setText(0, self._("Failed"))
                item.setIcon(0, QIcon(common.get_icon("general", "serious")))
            else:
                item.setText(0, self._("Unknown"))
                item.setIcon(0, QIcon(common.get_icon("general", "unknown")))

            item.setText(1, result["test_name"])
            item.copyable = False

            # Provide suggestions on failures
            if not result["passed"]:
                all_passed = False

                suggestions = result["suggestions"]
                for line in suggestions:
                    subitem = QTreeWidgetItem()
                    if line.startswith("$"):
                        text = line
                        subitem.setBackground(1, QBrush(QColor("black")))
                        subitem.setForeground(1, QBrush(QColor("green")))
                        subitem.setFont(1, QFont("monospace"))
                        subitem.copyable = True
                    else:
                        text = "• " + line
                        subitem.setDisabled(True)
                    subitem.setText(1, text)
                    subitem.setToolTip(1, line)
                    item.addChild(subitem)
            self.result_tree_root.addChild(item)

        self.result_tree.expandAll()
        self.result_window.findChild(QPushButton, "Close").clicked.connect(self._close_troubleshooter)
        self.result_tree.itemSelectionChanged.connect(self._item_changed)
        self.result_copy_btn.setHidden(True)
        self.result_copy_btn.clicked.connect(self._copy_to_clipboard)
        self.result_window.setWindowTitle(self._("Troubleshooter for []").replace("[]", self.human_name))
        if all_passed:
            self.result_label.setText(self._("Everything appears to be in working order!"))
        self.dbg.stdout("Troubleshooting completed!", self.dbg.success, 1)
        self.result_window.open()

    def _item_changed(self):
        """
        User clicks on one of the results. Shows/hides the "Copy" button.
        """
        selected = self.result_tree.selectedItems()[0]
        self.result_copy_btn.setHidden(selected.copyable == False)

    def _copy_to_clipboard(self):
        """
        Commands can be copied to clipboard for convenience
        """
        selected_text = self.result_tree.selectedItems()[0].text(1).replace("$ ", "")
        QApplication.clipboard().setText(selected_text)

    def _close_troubleshooter(self):
        """
        """
        self.result_window.close()
        self.result_window.deleteLater()
        self.loading.deleteLater()
