#  pylint: disable=missing-module-docstring
#
# Module that provides the client-side functionality for an XML importer
#
# Copyright (c) 2008--2016 Red Hat, Inc.
#
# This software is licensed to you under the GNU General Public License,
# version 2 (GPLv2). There is NO WARRANTY for this software, express or
# implied, including the implied warranties of MERCHANTABILITY or FITNESS
# FOR A PARTICULAR PURPOSE. You should have received a copy of GPLv2
# along with this software; if not, see
# http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt.
#
# Red Hat trademarks are not licensed under GPLv2. No permission is
# granted to use or replicate Red Hat trademarks that are incorporated
# in this software or its documentation.
#
#

import gzip

from rhn import rpclib
from spacewalk.satellite_tools import constants

__version__ = "0.1"


# pylint: disable-next=missing-class-docstring
class Transport(rpclib.transports.Transport):
    # pylint: disable-next=consider-using-f-string
    user_agent = "satellite-sync/%s" % __version__

    def __init__(self, timeout=None):
        if timeout:
            rpclib.transports.Transport.__init__(self, timeout=timeout)
        else:
            rpclib.transports.Transport.__init__(self)
        self.add_header("Accept-Encoding", "gzip")

    def _process_response(self, fd, connection):
        # Content-Type defaults to txt/xml
        content_type = self.headers_in.get("Content-Type", "text/xml")
        content_encoding = self.headers_in.get("Content-Encoding")

        if content_encoding == "gzip":
            # Un-gzipit
            fd = CompressedStream(fd)

        if content_type == "text/xml":
            # XML-RPC error
            # Catch exceptions so we can properly close file descriptors
            try:
                ret = self.parse_response(fd)
            except:
                fd.close()
                connection.close()
                raise
            fd.close()
            connection.close()
            return ret

        # XXX application/octet-stream should go away
        if content_type in (
            "application/xml",
            "application/octet-stream",
            "application/x-rpm",
        ):
            f = rpclib.transports.File(fd)
            # Explanation copied from the base class' method (rhn.transports):
            # Set the File's close method to the connection's
            # Note that calling the HTTPResponse's close() is not enough,
            # since the main socket would remain open, and this is
            # particularily bad with SSL
            f.close = connection.close
            return f

        connection.close()
        # pylint: disable-next=broad-exception-raised
        raise Exception("Unknown response type: " + content_type)


class SafeTransport(rpclib.transports.SafeTransport, Transport):
    _process_response = Transport._process_response


class ProxyTransport(rpclib.transports.ProxyTransport, Transport):
    _process_response = Transport._process_response


class SafeProxyTransport(rpclib.transports.SafeProxyTransport, Transport):
    _process_response = Transport._process_response


class _Server(rpclib.Server):
    _transport_class = Transport
    _transport_class_https = SafeTransport
    _transport_class_proxy = ProxyTransport
    _transport_class_https_proxy = SafeProxyTransport

    def use_CA_chain(self, ca_chain=None):
        pass


# pylint: disable-next=missing-class-docstring
class StreamConnection(_Server):
    def __init__(
        self,
        uri,
        proxy=None,
        username=None,
        password=None,
        refreshCallback=None,
        xml_dump_version=constants.PROTOCOL_VERSION,
        timeout=None,
    ):
        _Server.__init__(
            self,
            uri,
            proxy=proxy,
            username=username,
            password=password,
            refreshCallback=refreshCallback,
            timeout=timeout,
        )
        self.add_header("X-RHN-Satellite-XML-Dump-Version", xml_dump_version)


class GETServer(rpclib.GETServer):
    """class rpclib.GETServer with overriden default transports classes"""

    _transport_class = Transport
    _transport_class_https = SafeTransport
    _transport_class_proxy = ProxyTransport
    _transport_class_https_proxy = SafeProxyTransport

    def __init__(
        self,
        uri,
        transport=None,
        proxy=None,
        username=None,
        password=None,
        client_version=2,
        headers=None,
        refreshCallback=None,
        progressCallback=None,
        xml_dump_version=constants.PROTOCOL_VERSION,
        timeout=None,
    ):
        if headers is None:
            headers = {}
        rpclib.GETServer.__init__(
            self,
            uri,
            transport=transport,
            proxy=proxy,
            username=username,
            password=password,
            client_version=client_version,
            headers=headers,
            refreshCallback=refreshCallback,
            timeout=timeout,
        )
        self.add_header("X-RHN-Satellite-XML-Dump-Version", xml_dump_version)

    def use_CA_chain(self, ca_chain=None):
        pass


class CompressedStream:
    """
    GzipStream will not close the connection by itself, so we have to keep the
    underlying stream around
    """

    def __init__(self, stream):
        def noop():
            pass

        self._real_stream = stream
        # TODO: Is this sill true, since gzipstream is no longer in use?
        # gzipstream tries to flush stuff; add a noop function
        self._real_stream.flush = noop
        self.stream = self._real_stream
        if not isinstance(self._real_stream, gzip.GzipFile):
            self.stream = gzip.GzipFile(fileobj=self._real_stream, mode="r")

    def close(self):
        if self.stream:
            self.stream.close()
            self.stream = None
        if self._real_stream:
            self._real_stream.close()
            self._real_stream = None

    def __getattr__(self, name):
        return getattr(self.stream, name)

    def __repr__(self):
        # pylint: disable-next=consider-using-f-string
        return "<_CompressedStream at %s>" % id(self)


if __name__ == "__main__":
    pass
