import os
from logging import getLogger

from PyQt5.QtCore import Qt, QSettings, QTimer, QSize, pyqtSignal, pyqtSlot
from PyQt5.QtGui import QCloseEvent, QCursor, QShowEvent
from PyQt5.QtWidgets import (
    QMainWindow,
    QApplication,
    QStatusBar,
    QScrollArea,
    QScroller,
    QComboBox,
    QMessageBox,
    QLabel,
    QWidget,
    QHBoxLayout,
)

from rare.components.tabs import MainTabWidget
from rare.components.tray_icon import TrayIcon
from rare.shared import RareCore
from rare.shared.workers.worker import QueueWorkerState
from rare.utils.paths import lock_file
from rare.widgets.elide_label import ElideLabel

logger = getLogger("MainWindow")


class MainWindow(QMainWindow):
    # int: exit code
    exit_app: pyqtSignal = pyqtSignal(int)

    def __init__(self, parent=None):
        self.__exit_code = 0
        self.__accept_close = False
        self._window_launched = False
        super(MainWindow, self).__init__(parent=parent)
        self.setAttribute(Qt.WA_DeleteOnClose, True)
        self.rcore = RareCore.instance()
        self.core = RareCore.instance().core()
        self.signals = RareCore.instance().signals()
        self.args = RareCore.instance().args()
        self.settings = QSettings()

        self.setWindowTitle("Rare - GUI for legendary")
        self.tab_widget = MainTabWidget(self)
        self.tab_widget.exit_app.connect(self.__on_exit_app)
        self.setCentralWidget(self.tab_widget)

        # Set up status bar stuff (jumping through a lot of hoops)
        self.status_bar = QStatusBar(self)
        self.setStatusBar(self.status_bar)

        self.active_label = QLabel(self.tr("Active:"), self.status_bar)
        # lk: set top and botton margins to accommodate border for scroll area labels
        self.active_label.setContentsMargins(5, 1, 0, 1)
        self.status_bar.addWidget(self.active_label)

        self.active_container = QWidget(self.status_bar)
        active_layout = QHBoxLayout(self.active_container)
        active_layout.setContentsMargins(0, 0, 0, 0)
        active_layout.setSizeConstraint(QHBoxLayout.SetFixedSize)
        self.status_bar.addWidget(self.active_container, stretch=0)

        self.queued_label = QLabel(self.tr("Queued:"), self.status_bar)
        # lk: set top and botton margins to accommodate border for scroll area labels
        self.queued_label.setContentsMargins(5, 1, 0, 1)
        self.status_bar.addPermanentWidget(self.queued_label)

        self.queued_scroll = QScrollArea(self.status_bar)
        self.queued_scroll.setFrameStyle(QScrollArea.NoFrame)
        self.queued_scroll.setWidgetResizable(True)
        self.queued_scroll.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
        self.queued_scroll.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
        self.queued_scroll.setFixedHeight(self.queued_label.sizeHint().height())
        self.status_bar.addPermanentWidget(self.queued_scroll, stretch=1)

        self.queued_container = QWidget(self.queued_scroll)
        queued_layout = QHBoxLayout(self.queued_container)
        queued_layout.setContentsMargins(0, 0, 0, 0)
        queued_layout.setSizeConstraint(QHBoxLayout.SetFixedSize)

        self.active_label.setVisible(False)
        self.active_container.setVisible(False)
        self.queued_label.setVisible(False)
        self.queued_scroll.setVisible(False)

        self.signals.application.update_statusbar.connect(self.update_statusbar)

        # self.status_timer = QTimer(self)
        # self.status_timer.timeout.connect(self.update_statusbar)
        # self.status_timer.setInterval(5000)
        # self.status_timer.start()

        width, height = 1280, 720
        if self.settings.value("save_size", False, bool):
            width, height = self.settings.value("window_size", (width, height), tuple)

        self.resize(width, height)

        if not self.args.offline:
            try:
                from rare.utils.rpc import DiscordRPC

                self.rpc = DiscordRPC()
            except ModuleNotFoundError:
                logger.warning("Discord RPC module not found")

        self.singleton_timer = QTimer(self)
        self.singleton_timer.setInterval(1000)
        self.singleton_timer.timeout.connect(self.timer_finished)
        self.singleton_timer.start()

        self.tray_icon: TrayIcon = TrayIcon(self)
        self.tray_icon.exit_app.connect(self.__on_exit_app)
        self.tray_icon.show_app.connect(self.show)
        self.tray_icon.activated.connect(lambda r: self.toggle() if r == self.tray_icon.DoubleClick else None)

        # enable kinetic scrolling
        for scroll_area in self.findChildren(QScrollArea):
            if not scroll_area.property("no_kinetic_scroll"):
                QScroller.grabGesture(scroll_area.viewport(), QScroller.LeftMouseButtonGesture)

            # fix scrolling
            for combo_box in scroll_area.findChildren(QComboBox):
                combo_box.wheelEvent = lambda e: e.ignore()

    def center_window(self):
        # get the margins of the decorated window
        margins = self.windowHandle().frameMargins()
        # get the screen the cursor is on
        current_screen = QApplication.screenAt(QCursor.pos())
        if not current_screen:
            current_screen = QApplication.primaryScreen()
        # get the available screen geometry (excludes panels/docks)
        screen_rect = current_screen.availableGeometry()
        decor_width = margins.left() + margins.right()
        decor_height = margins.top() + margins.bottom()
        window_size = QSize(self.width(), self.height()).boundedTo(
            screen_rect.size() - QSize(decor_width, decor_height)
        )

        self.resize(window_size)
        self.move(screen_rect.center() - self.rect().adjusted(0, 0, decor_width, decor_height).center())

    @pyqtSlot()
    def show(self) -> None:
        super(MainWindow, self).show()
        if not self._window_launched:
            self.center_window()
        self._window_launched = True

    def hide(self) -> None:
        if self.settings.value("save_size", False, bool):
            size = self.size().width(), self.size().height()
            self.settings.setValue("window_size", size)
        super(MainWindow, self).hide()

    def toggle(self):
        if self.isHidden():
            self.show()
        else:
            self.hide()

    @pyqtSlot()
    def update_statusbar(self):
        self.active_label.setVisible(False)
        self.active_container.setVisible(False)
        self.queued_label.setVisible(False)
        self.queued_scroll.setVisible(False)
        for label in self.active_container.findChildren(QLabel, options=Qt.FindDirectChildrenOnly):
            self.active_container.layout().removeWidget(label)
            label.deleteLater()
        for label in self.queued_container.findChildren(QLabel, options=Qt.FindDirectChildrenOnly):
            self.queued_container.layout().removeWidget(label)
            label.deleteLater()
        for info in self.rcore.queue_info():
            label = ElideLabel(info.app_title)
            label.setObjectName("QueueWorkerLabel")
            label.setToolTip(f"<b>{info.worker_type}</b>: {info.app_title}")
            label.setProperty("workerType", info.worker_type)
            label.setFixedWidth(150)
            label.setContentsMargins(3, 0, 3, 0)
            if info.state == QueueWorkerState.ACTIVE:
                self.active_container.layout().addWidget(label)
                self.active_label.setVisible(True)
                self.active_container.setVisible(True)
            elif info.state == QueueWorkerState.QUEUED:
                self.queued_container.layout().addWidget(label)
                self.queued_label.setVisible(True)
                self.queued_scroll.setVisible(True)

    def timer_finished(self):
        file_path = lock_file()
        if os.path.exists(file_path):
            with open(file_path, "r") as file:
                action = file.read()
            if action.startswith("show"):
                self.show()
            os.remove(file_path)
        self.singleton_timer.start()

    @pyqtSlot()
    @pyqtSlot(int)
    def __on_exit_app(self, exit_code=0) -> None:
        self.__exit_code = exit_code
        self.close()

    def close(self) -> bool:
        self.__accept_close = True
        return super(MainWindow, self).close()

    def closeEvent(self, e: QCloseEvent) -> None:
        # lk: `accept_close` is set to `True` by the `close()` method, overrides exiting to tray in `closeEvent()`
        # lk: ensures exiting instead of hiding when `close()` is called programmatically
        if not self.__accept_close:
            if self.settings.value("sys_tray", True, bool):
                self.hide()
                e.ignore()
                return

        # FIXME: Move this to RareCore once the download manager is implemented
        if self.rcore.queue_threadpool.activeThreadCount():
            reply = QMessageBox.question(
                self,
                self.tr("Quit {}?").format(QApplication.applicationName()),
                self.tr(
                    "There are currently running operations. "
                    "Rare cannot exit until they are completed.\n\n"
                    "Do you want to clear the queue?"
                ),
                buttons=(QMessageBox.Yes | QMessageBox.No),
                defaultButton=QMessageBox.No,
            )
            if reply == QMessageBox.Yes:
                self.rcore.queue_threadpool.clear()
                for qw in self.rcore.queued_workers():
                    self.rcore.dequeue_worker(qw)
                self.update_statusbar()
            e.ignore()
            return
        elif self.tab_widget.downloads_tab.is_download_active:
            reply = QMessageBox.question(
                self,
                self.tr("Quit {}?").format(QApplication.applicationName()),
                self.tr(
                    "There is an active download. "
                    "Quitting Rare now will stop the download.\n\n"
                    "Are you sure you want to quit?"
                ),
                buttons=(QMessageBox.Yes | QMessageBox.No),
                defaultButton=QMessageBox.No,
            )
            if reply == QMessageBox.Yes:
                self.tab_widget.downloads_tab.stop_download(omit_queue=True)
            else:
                e.ignore()
                return
        # FIXME: End of FIXME
        self.singleton_timer.stop()
        self.tray_icon.deleteLater()
        self.hide()
        self.exit_app.emit(self.__exit_code)
        super(MainWindow, self).closeEvent(e)

