# -*- coding: utf-8 -*-
#
# Copyright(C) 2023 Red Hat, Inc.
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program.  If not, see <https://www.gnu.org/licenses/>.
#
# This file incorporates work covered by the following copyrights and
# permission notices:
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU Library General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
# Copyright 2005 Duke University
# Parts Copyright 2007 Red Hat, Inc
#
#   License above taken from the original code at:
#
#       https://github.com/rpm-software-management/yum/blob/master/yum/rpmtrans.py
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU Library General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#
#   License above taken from the original code at:
#
#       https://github.com/rpm-software-management/yum/blob/master/yum/callbacks.py

__metaclass__ = type


from convert2rhel import pkgmanager
from convert2rhel.logger import root_logger


logger = root_logger.getChild(__name__)
"""Instance of the logger used in this module."""


# We need to double inherit here, both from the callback class and the base
# object class, just to initialize properly with `super`
class PackageDownloadCallback(pkgmanager.DownloadProgress, object):
    """Package download callback for YUM transaction."""

    def __init__(self):
        """Constructor for the package download progress indicator.

        We initialize a few properties here for keeping track of progression of
        the downloaded files.
        """
        super(PackageDownloadCallback, self).__init__()
        # Same strategy as used in yum.rpmtrans.SimpleCliCallBack. We
        # hold the last package name to not print it twice, avoiding
        # spamming msgs.
        self.last_package_seen = None

    def updateProgress(self, name, frac, fread, ftime):
        """Update and output the message that we sent to the user.

        This method, in the `DownloadBaseCallback` class, is meant to be used
        with a progress-bar to show a nice progression for the user, but we are
        using it only to output a simple message to keep track of the packages
        which were downloaded, no need to do any fancy progress-bar.

        :param name: File that is being downloaded in the transaction.
        :type name: str
        :param frac: A number between `0` and `1` that represents the percentage of the file that has been downloaded.
        :type frac: float
        :param fread: String containing how many bytes were read in the download.
        :type fread: str
        :param ftime: String that contains the remaining time for the package to be finished downloading.
        :type ftime: str
        """
        # The base API will call this callback class on every package update,
        # not matter if it is the same update or not, so, the below statement
        # prevents the same message being sent more than once to the user.
        if self.last_package_seen != name:
            # Metadata download abut repositories will be sent to this class too.
            if name.endswith(".rpm"):
                logger.info("Downloading package: %s", name)
            else:
                logger.debug("Downloading repository metadata: %s", name)

        self.last_package_seen = name


class TransactionDisplayCallback(pkgmanager.TransactionDisplay, object):
    """Transaction display callback for YUM transaction."""

    def __init__(self):
        """Constructor that overrides initialization for SimpleCliCallBack()."""
        super(TransactionDisplayCallback, self).__init__()
        # Hold the last package name to not print it twice, avoiding
        # spamming msgs.
        self.last_package_seen = None

    def event(self, package, action, te_current, te_total, ts_current, ts_total):
        """Process and output the RPM operations in the transaction.

        .. note::
            Yum API sends the `package` parameter as two different types.
            If the package comes as a `str`, then it's a cleanup, if it's a
            `yum.sqlitesack.YumAvailablePackageSqlite`, then it's a normal
            package being processed.

        :param package: Package being processed in the transaction.
        :type param: str | yum.sqlitesack.YumAvailablePackageSqlite
        :param action: The type of the action being used for the current
            `package`. Only values that came from `rpmtrans.RPMBaseCallback.action` are valid.
        :type action: int
        :param te_current: How much work has already been done in the transaction.
        :type te_current: int
        :param te_total: How much work in total is present in the transaction.
        :type te_total: int
        :param ts_current: Number, in order, of the current transaction in the transaction set.
        :type ts_current: int
        :param ts_total: How many transactions are present in the transaction set.
        :type ts_total: int
        """
        message = "%s: %s [%s/%s]"

        # Here we convert the package to a str because we just need to do a
        # standard str comparision rather than what is implemented in
        # `yum.sqlitesack.YumAvailablePackageSqlite` (that class does
        # version comparison and a bunch of other stuff). We don't care about
        # any of that, we just want to check if the package name is equal or
        # different.
        package = str(package)

        message = message % (self.action[action], package, ts_current, ts_total)

        # The base API will call this callback class on every package update,
        # not matter if it is the same update or not, so, the below statement
        # prevents the same message being sent more than once to the user.
        if self.last_package_seen != package:
            logger.info(message)

        self.last_package_seen = package

    def scriptout(self, package, msgs):
        """Hook for reporting output from an rpm scriptlet.

        .. note::
            If there is no problem reported during the scriptlet execution,
            then this method will receive a None instead of the normal message.

        :param package: The package associated with the scriptlet.
        :type package: str | None
        :param msgs: The scriptlet output
        :type msgs: str | None
        """
        # The base API will call this callback class on every package update,
        # no matter if the messages are empty or not, so, the below statement
        # prevents the same message being sent to the user with empty strings.
        if msgs:
            logger.info("Scriptlet output %s: %s", package, msgs)

    def errorlog(self, msg):
        """Report an error that occurred during the transaction.

        :param msg: The error message sent by the API.
        :type message: str
        """
        logger.error("Transaction error: %s", msg)
