import json
from os import sep
from pathlib import Path
from pprint import pformat
from shutil import rmtree

from PyQt5 import QtWidgets, QtGui, QtCore
from PyQt5.QtCore import pyqtSignal

from components.globals import *
from .job_queue import Job
from .widgets.nodes import PathWidget


class PopupWindow(QtWidgets.QMessageBox):

    def __init__(self, title, text, details=None, width=200, height=400):
        super().__init__()
        self.setWindowTitle(title)
        self.setText(text)
        self.setDetailedText(details)
        self.setWindowIcon(QtGui.QIcon("ui/icon.png"))
        self.setMinimumWidth(width)
        self.setMinimumWidth(height)

    def pop(self, _):
        print("pop")
        self.exec_()


class ScrollWindow(QtWidgets.QMainWindow):

    def __init__(self, title, subtitle, layout=QtWidgets.QVBoxLayout, parent=None):
        super().__init__(parent)
        self.setWindowTitle(title)

        self.mainWidget = QtWidgets.QWidget(self)
        self.mainWidget.layout = QtWidgets.QVBoxLayout(self.mainWidget)
        self.mainWidget.setLayout(self.mainWidget.layout)
        self.setCentralWidget(self.mainWidget)

        if subtitle:
            self.mainWidget.layout.addWidget(QtWidgets.QLabel(subtitle))

        self.scrollArea = QtWidgets.QScrollArea(self.mainWidget)
        self.scrollArea.setWidgetResizable(True)

        self.contents = QtWidgets.QWidget(self.scrollArea)
        self.contents.layout = layout()
        self.contents.setLayout(self.contents.layout)
        self.mainWidget.layout.addWidget(self.contents)
        self.mainWidget.layout.addWidget(self.scrollArea)

        self.scrollArea.setWidget(self.contents)


class LicenseWidget(QtWidgets.QWidget):

    def __init__(self, text, details, parent=None):
        super().__init__(parent=parent)
        self.setMinimumHeight(300)

        self.layout = QtWidgets.QVBoxLayout(self)
        self.setLayout(self.layout)

        label = QtWidgets.QLabel(text, self)
        self.layout.addWidget(label)

        browser = QtWidgets.QTextBrowser(self)
        browser.setText(details)
        self.layout.addWidget(browser)


class LicenseWindow(ScrollWindow):

    def __init__(self, parent=None):
        super().__init__("Software licenses", "Licenses", parent=parent)
        self.setFixedWidth(400)

        with open(f"{BUNDLE_DIR}{sep}LICENSE.txt", "r") as f:
            reaper = LicenseWidget("Reaper GPL license", f.read(), self)
            self.contents.layout.addWidget(reaper)

        with open(f"{BUNDLE_DIR}{sep}licenses/socialreaper.txt", "r") as f:
            reaper = LicenseWidget("Social Reaper MIT license", f.read(), self)
            self.contents.layout.addWidget(reaper)

        with open(f"{BUNDLE_DIR}{sep}LICENSE.txt", "r") as f:
            reaper = LicenseWidget("PyQt GPL license", f.read(), self)
            self.contents.layout.addWidget(reaper)

        with open(f"{BUNDLE_DIR}{sep}licenses/requests.txt", "r") as f:
            reaper = LicenseWidget("Requests Apache license", f.read(), self)
            self.contents.layout.addWidget(reaper)

        with open(f"{BUNDLE_DIR}{sep}licenses/requests-oauthlib.txt", "r") as f:
            reaper = LicenseWidget("Requests-OAuthLib ISC license", f.read(), self)
            self.contents.layout.addWidget(reaper)

        with open(f"{BUNDLE_DIR}{sep}licenses/oauthlib.txt", "r") as f:
            reaper = LicenseWidget("OAuthLib BSD license", f.read(), self)
            self.contents.layout.addWidget(reaper)

    def pop(self):
        self.show()


class ErrorWindow(QtWidgets.QMainWindow):
    job_error = pyqtSignal(Job)

    def __init__(self, parent=None):
        super().__init__(parent)

        self.job_error.connect(self.throw_job)
        self.job = None
        self.log = ""

        self.setWindowTitle("Error manager")
        self.setMinimumSize(500, 500)

        self.mainWidget = QtWidgets.QWidget(self)
        self.mainWidget.layout = QtWidgets.QVBoxLayout(self.mainWidget)
        self.mainWidget.setLayout(self.mainWidget.layout)
        self.setCentralWidget(self.mainWidget)

        self.tabs = QtWidgets.QTabWidget(self.mainWidget)

        self.console = QtWidgets.QTextBrowser()
        self.tabs.addTab(self.console, "Error log")

        self.job_browser = QtWidgets.QTextBrowser()
        self.tabs.addTab(self.job_browser, "Job state")

        self.itr_browser = QtWidgets.QTextBrowser()
        self.tabs.addTab(self.itr_browser, "Iterator state")

        self.api_browser = QtWidgets.QTextBrowser()
        self.tabs.addTab(self.api_browser, "API state")

        self.error_browser = QtWidgets.QTextBrowser()
        self.tabs.addTab(self.error_browser, "Error state")

        self.toggle_job_tabs(False)

        self.mainWidget.layout.addWidget(self.tabs)

        self.cancelButton = QtWidgets.QPushButton("Stop retrying", self.mainWidget)
        self.mainWidget.layout.addWidget(self.cancelButton)

        self.options = self.menuBar().addMenu("Options")
        self.clearAction = QtWidgets.QAction("Clear")
        self.clearAction.triggered.connect(self.clear)
        self.options.addAction(self.clearAction)

    def toggle_job_tabs(self, boolean):
        for i in range(1, 5):
            self.tabs.setTabEnabled(i, boolean)

    def clear(self, _):
        self.log = ""
        self.toggle_job_tabs(False)
        self.console.clear()
        self.job_browser.clear()
        self.itr_browser.clear()
        self.api_browser.clear()
        self.error_browser.clear()

    @QtCore.pyqtSlot(Job)
    def throw_job(self, job):
        self.toggle_job_tabs(True)
        self.job_browser.setText(pformat(vars(job)))
        self.itr_browser.setText(str(job.iterator))
        self.api_browser.setText(str(job.source.api))
        self.error_browser.setText(str(job.error))
        self.show()

    def log_error(self, log):
        self.show()
        self.log += log + "\n"
        self.console.setText(self.log)
        scrollbar = self.console.verticalScrollBar()
        scrollbar.setValue(scrollbar.maximum())


class BinaryBox(QtWidgets.QGroupBox):

    def __init__(
        self, title, choices, description, default_choice, toggle_function, parent=None
    ):
        super().__init__(parent)
        self.toggle_function = toggle_function

        self.setTitle(title)

        self.layout = QtWidgets.QVBoxLayout(self)
        self.setLayout(self.layout)

        self.layout.addWidget(QtWidgets.QLabel(description, self))

        self.option_1 = QtWidgets.QRadioButton(choices[0], self)
        self.layout.addWidget(self.option_1)

        self.option_2 = QtWidgets.QRadioButton(choices[1], self)
        self.layout.addWidget(self.option_2)

        self.option_1.toggled.connect(toggle_function)
        self.option_2.toggled.connect(self.invert)

        if default_choice:
            self.option_1.toggle()
        else:
            self.option_2.toggle()

    def invert(self, boolean):
        self.toggle_function(not boolean)


class SettingsWindow(ScrollWindow):

    def __init__(self, parent):
        super().__init__(
            "Settings", None, layout=QtWidgets.QFormLayout, parent=parent.window
        )
        self.setMinimumSize(400, 475)
        self.location = f"{DATA_DIR}{sep}settings.json"
        self.parent = parent

        self.data = {
            "save_path": f"{Path.home()}{sep}Downloads",
            "light": True,
            "utf-8": True,
            "cache": True,
        }
        self.load_settings()

        self.savePathBox = QtWidgets.QGroupBox(self)
        self.savePathBox.setTitle("Output directory")
        self.savePathBox.layout = QtWidgets.QVBoxLayout()
        self.savePathBox.setLayout(self.savePathBox.layout)

        self.savePath = PathWidget(self.data.get("save_path"))
        self.savePath.path_changed.connect(self.set_save_path)
        self.savePathBox.layout.addWidget(self.savePath)

        self.contents.layout.addWidget(self.savePathBox)

        self.themeBox = BinaryBox(
            "Theme",
            ("Light", "Dark"),
            "Change Reaper's Appearance",
            self.get_light_mode(),
            self.set_light_mode,
        )
        self.contents.layout.addWidget(self.themeBox)

        self.encodingBox = BinaryBox(
            "Output encoding",
            ("UTF-8", "ASCII"),
            "Changing to ASCII will mean non-ASCII data will be lost",
            self.get_encoding(),
            self.set_encoding,
        )
        self.contents.layout.addWidget(self.encodingBox)

        self.cacheBox = BinaryBox(
            "Cache",
            ("Use cache and memory", "Use memory"),
            "Store large data on disk",
            self.get_cache_mode(),
            self.set_cache,
        )

        clearCacheButton = QtWidgets.QPushButton("Clear cache")
        clearCacheButton.clicked.connect(self.clear_cache)
        clearCacheButtonLayout = QtWidgets.QWidget()
        clearCacheButtonLayout.layout = QtWidgets.QHBoxLayout(clearCacheButtonLayout)
        clearCacheButtonLayout.layout.setContentsMargins(0, 0, 0, 0)
        clearCacheButtonLayout.setLayout(clearCacheButtonLayout.layout)
        clearCacheButtonLayout.layout.addWidget(clearCacheButton)
        clearCacheButtonLayout.layout.addStretch(1)

        self.cacheBox.layout.addWidget(clearCacheButtonLayout)
        self.contents.layout.addWidget(self.cacheBox)

        self.saveButtonWidget = QtWidgets.QWidget(self.contents)
        self.saveButtonWidget.layout = QtWidgets.QHBoxLayout(self.saveButtonWidget)
        self.saveButtonWidget.setLayout(self.saveButtonWidget.layout)
        self.contents.layout.addWidget(self.saveButtonWidget)

        self.saveButton = QtWidgets.QPushButton("Save", self.saveButtonWidget)
        self.saveButton.clicked.connect(self.save)
        self.saveButtonWidget.layout.addWidget(self.saveButton)
        self.saveButtonWidget.layout.addStretch(1)

    def set_light_mode(self, boolean):
        self.parent.enable_dark_mode(boolean)
        self.data["light"] = boolean

    def set_encoding(self, boolean):
        if boolean:
            self.parent.encoding = "utf-8"
        else:
            self.parent.encoding = "ascii"
        self.data["utf-8"] = boolean

    def set_cache(self, boolean):
        self.parent.cache_enabled = boolean
        self.data["cache"] = boolean

    def set_save_path(self, text):
        self.data["save_path"] = text

    def clear_cache(self, boolean):
        rmtree(CACHE_DIR, ignore_errors=True)

    def save(self, _):
        self.hide()
        self.save_settings()

    def save_settings(self):
        with open(self.location, "w") as f:
            json.dump(self.data, f)

    def load_settings(self):
        try:
            with open(self.location, "r") as f:
                self.data = json.load(f)
        except (FileNotFoundError, json.decoder.JSONDecodeError):
            pass

    def get_save_path(self):
        return self.data.get("save_path")

    def get_encoding(self):
        return self.data.get("utf-8")

    def get_light_mode(self):
        return self.data.get("light")

    def get_cache_mode(self):
        return self.data.get("cache")