# -*- coding: utf-8 -*-

# Copyright (c) 2015 - 2022 Detlev Offenbach <detlev@die-offenbachs.de>
#

"""
Module implementing a dialog to show details about a package.
"""

from PyQt6.QtCore import pyqtSlot, Qt, QLocale
from PyQt6.QtWidgets import (
    QDialog, QDialogButtonBox, QTreeWidgetItem, QLabel, QHeaderView,
    QAbstractButton
)

from .Ui_PipPackageDetailsDialog import Ui_PipPackageDetailsDialog


class PipPackageDetailsDialog(QDialog, Ui_PipPackageDetailsDialog):
    """
    Class implementing a dialog to show details about a package.
    """
    ButtonInstall = 1
    ButtonRemove = 2
    ButtonUpgrade = 4
    
    def __init__(self, detailsData, buttonsMode=0, parent=None):
        """
        Constructor
        
        @param detailsData package details
        @type dict
        @param buttonsMode flags telling which convenience buttons to enable
            (defaults to 0)
        @type int (optional)
        @param parent reference to the parent widget (defaults to None)
        @type QWidget (optional)
        """
        super().__init__(parent)
        self.setupUi(self)
        self.setWindowFlags(Qt.WindowType.Window)
        
        self.__pipWidget = parent
        
        self.__installButton = self.buttonBox.addButton(
            self.tr("Install"), QDialogButtonBox.ButtonRole.ActionRole)
        self.__removeButton = self.buttonBox.addButton(
            self.tr("Uninstall"), QDialogButtonBox.ButtonRole.ActionRole)
        self.__upgradeButton = self.buttonBox.addButton(
            self.tr("Upgrade"), QDialogButtonBox.ButtonRole.ActionRole)
        
        self.__locale = QLocale()
        self.__packageTypeMap = {
            "sdist": self.tr("Source"),
            "bdist_wheel": self.tr("Python Wheel"),
            "bdist_egg": self.tr("Python Egg"),
            "bdist_wininst": self.tr("MS Windows Installer"),
            "bdist_msi": self.tr("MS Windows Installer"),
            "bdist_rpm": self.tr("Unix Installer"),
            "bdist_deb": self.tr("Unix Installer"),
            "bdist_dumb": self.tr("Archive"),
        }
        self.__packageName = detailsData["info"]["name"]
        
        self.__populateDetails(detailsData["info"])
        self.__populateDownloadUrls(detailsData["urls"])
        self.__populateRequiresProvides(detailsData["info"])
        
        self.__installButton.setEnabled(buttonsMode & self.ButtonInstall)
        self.__removeButton.setEnabled(buttonsMode & self.ButtonRemove)
        self.__upgradeButton.setEnabled(buttonsMode & self.ButtonUpgrade)
    
    def __populateDetails(self, detailsData):
        """
        Private method to populate the details tab.
        
        @param detailsData package details
        @type dict
        """
        self.packageNameLabel.setText(
            "<h1>{0} {1}</h1".format(self.__sanitize(detailsData["name"]),
                                     self.__sanitize(detailsData["version"])))
        self.summaryLabel.setText(
            self.__sanitize(detailsData["summary"][:240]))
        self.descriptionEdit.setPlainText(
            self.__sanitize(detailsData["description"]))
        self.authorLabel.setText(self.__sanitize(detailsData["author"]))
        self.authorEmailLabel.setText(
            '<a href="mailto:{0}">{0}</a>'.format(
                self.__sanitize(detailsData["author_email"])))
        self.licenseLabel.setText(self.__sanitize(detailsData["license"]))
        self.platformLabel.setText(self.__sanitize(detailsData["platform"]))
        self.homePageLabel.setText(
            '<a href="{0}">{0}</a>'.format(
                self.__sanitize(detailsData["home_page"], forUrl=True)))
        self.packageUrlLabel.setText(
            '<a href="{0}">{0}</a>'.format(
                self.__sanitize(detailsData["package_url"], forUrl=True)))
        self.releaseUrlLabel.setText(
            '<a href="{0}">{0}</a>'.format(
                self.__sanitize(detailsData["release_url"], forUrl=True)))
        self.docsUrlLabel.setText(
            '<a href="{0}">{0}</a>'.format(
                self.__sanitize(detailsData["docs_url"], forUrl=True)))
        self.classifiersList.addItems(detailsData["classifiers"])
        
        self.buttonBox.button(
            QDialogButtonBox.StandardButton.Close).setDefault(True)
        self.buttonBox.button(
            QDialogButtonBox.StandardButton.Close).setFocus(
                Qt.FocusReason.OtherFocusReason)
    
    def __populateDownloadUrls(self, downloadsData):
        """
        Private method to populate the download URLs tab.
        
        @param downloadsData downloads information
        @type dict
        """
        index = self.infoWidget.indexOf(self.urls)
        if downloadsData:
            self.infoWidget.setTabEnabled(index, True)
            for download in downloadsData:
                itm = QTreeWidgetItem(self.downloadUrlsList, [
                    "",
                    self.__packageTypeMap[download["packagetype"]]
                    if download["packagetype"] in self.__packageTypeMap
                    else "",
                    download["python_version"]
                    if download["python_version"] != "source"
                    else "",
                    self.__formatUploadDate(download["upload_time"]),
                    self.__formatSize(download["size"]),
                ])
                pgpLink = (
                    ' (<a href="{0}">pgp</a>)'.format(download["url"] + ".asc")
                    if download["has_sig"] else
                    ""
                )
                urlLabel = QLabel('<a href="{0}#md5={2}">{1}</a>{3}'.format(
                    download["url"], download["filename"],
                    download["md5_digest"], pgpLink))
                urlLabel.setTextInteractionFlags(
                    Qt.TextInteractionFlag.LinksAccessibleByMouse)
                urlLabel.setOpenExternalLinks(True)
                self.downloadUrlsList.setItemWidget(itm, 0, urlLabel)
            header = self.downloadUrlsList.header()
            header.resizeSections(QHeaderView.ResizeMode.ResizeToContents)
        else:
            self.infoWidget.setTabEnabled(index, False)
    
    def __populateRequiresProvides(self, detailsData):
        """
        Private method to populate the requires/provides tab.
        
        @param detailsData package details
        @type dict
        """
        populatedItems = 0
        
        if "requires" in detailsData and detailsData["requires"]:
            self.requiredPackagesList.addItems(detailsData["requires"])
            populatedItems += len(detailsData["requires"])
        if "requires_dist" in detailsData and detailsData["requires_dist"]:
            self.requiredDistributionsList.addItems(
                detailsData["requires_dist"])
            populatedItems += len(detailsData["requires_dist"])
        if "provides" in detailsData and detailsData["provides"]:
            self.providedPackagesList.addItems(detailsData["provides"])
            populatedItems += len(detailsData["provides"])
        if "provides_dist" in detailsData and detailsData["provides_dist"]:
            self.providedDistributionsList.addItems(
                detailsData["provides_dist"])
            populatedItems += len(detailsData["provides_dist"])
        
        index = self.infoWidget.indexOf(self.requires)
        self.infoWidget.setTabEnabled(index, populatedItems > 0)
    
    def __sanitize(self, text, forUrl=False):
        """
        Private method to clean-up the given text.
        
        @param text raw text
        @type str
        @param forUrl flag indicating to sanitize an URL text
        @type bool
        @return processed text
        @rtype str
        """
        if text == "UNKNOWN" or text is None:
            text = ""
        elif text == "any":
            text = self.tr("any")
        if forUrl and (
            not isinstance(text, str) or
            not text.startswith(("http://", "https://", "ftp://"))
        ):
            # ignore if the schema is not one of the listed ones
            text = ""
        
        return text
    
    def __formatUploadDate(self, datetime):
        """
        Private method to format the upload date.
        
        @param datetime upload date and time
        @type xmlrpc.DateTime or str
        @return formatted date string
        @rtype str
        """
        if isinstance(datetime, str):
            return datetime.split("T")[0]
        else:
            date = datetime.value.split("T")[0]
            return "{0}-{1}-{2}".format(date[:4], date[4:6], date[6:])
    
    def __formatSize(self, size):
        """
        Private slot to format the size.
        
        @param size size to be formatted
        @type int
        @return formatted size
        @rtype str
        """
        unit = ""
        if size < 1024:
            unit = self.tr("B")
        elif size < 1024 * 1024:
            size /= 1024
            unit = self.tr("KB")
        elif size < 1024 * 1024 * 1024:
            size /= 1024 * 1024
            unit = self.tr("MB")
        else:
            size /= 1024 * 1024 * 1024
            unit = self.tr("GB")
        return self.tr("{0:.1f} {1}", "value, unit").format(size, unit)
    
    @pyqtSlot(QAbstractButton)
    def on_buttonBox_clicked(self, button):
        """
        Private slot handling the user pressing an action button.
        
        @param button button activated by the user
        @type QAbstractButton
        """
        if button is self.__installButton:
            self.__pipWidget.executeInstallPackages([self.__packageName])
            self.__installButton.setEnabled(False)
            self.__removeButton.setEnabled(True)
            self.__upgradeButton.setEnabled(False)
            self.raise_()
        elif button is self.__removeButton:
            self.__pipWidget.executeUninstallPackages([self.__packageName])
            self.__installButton.setEnabled(True)
            self.__removeButton.setEnabled(False)
            self.__upgradeButton.setEnabled(False)
            self.raise_()
        elif button is self.__upgradeButton:
            self.__pipWidget.executeUpgradePackages([self.__packageName])
            self.__installButton.setEnabled(False)
            self.__removeButton.setEnabled(True)
            self.__upgradeButton.setEnabled(False)
            self.raise_()
