#  pylint: disable=missing-module-docstring,invalid-name
#
# 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 sys
import os
import time

from spacewalk.common import apache, rhnApache, rhnTB, rhnFlags
from spacewalk.common.rhnException import rhnException, rhnFault
from spacewalk.common.rhnConfig import CFG, initCFG
from spacewalk.common.rhnLog import log_debug, log_error, initLOG, log_setreq

# local module imports
from . import rhnSQL

from .apacheRequest import apacheGET, apachePOST, HandlerNotFoundError
from . import rhnCapability

# a lame timer function for pretty logs


def timer(last):
    if not last:
        return 0
    # pylint: disable-next=consider-using-f-string
    log_debug(2, "%.2f sec" % (time.time() - last,))
    return 0


class apacheSession(rhnApache):
    """a class that extends rhnApache with several support functions used
    by the main handler class. This class deals with the processing of
    the request and setup for the real action handled in the
    apacheHandler class below."""

    _lang_catalog = "server"


class apacheHandler(apacheSession):
    """main Apache XMLRPC point of entry for the server"""

    def __init__(self):
        # First call the inherited constructor:
        apacheSession.__init__(self)
        self._req_processor = None

    ###
    # HANDLERS, in the order which they are called:
    # headerParserHandler, handler, and cleanupHandler.
    ###

    def headerParserHandler(self, req):
        log_setreq(req)
        # We need to init CFG and Logging
        options = req.get_options()
        # if we are initializing out of a <Location> handler don't
        # freak out
        if "RHNComponentType" not in options:
            # clearly nothing to do
            return apache.OK
        initCFG(options["RHNComponentType"])
        initLOG(CFG.LOG_FILE, CFG.DEBUG)

        # pylint: disable-next=pointless-string-statement
        """ parse the request, init database and figure out what can we call """
        log_debug(2, req.the_request)
        # call method from inherited class
        ret = apacheSession.headerParserHandler(self, req)
        if ret != apache.OK:
            return ret
        # make sure we have DB connection
        if not CFG.SEND_MESSAGE_TO_ALL:
            try:
                rhnSQL.initDB()
            except rhnSQL.SQLConnectError:
                rhnTB.Traceback(mail=1, req=req, severity="schema")
                return apache.HTTP_INTERNAL_SERVER_ERROR
        else:
            # If in outage mode, close the DB connections
            rhnSQL.closeDB()

        # Store client capabilities
        client_cap_header = "X-RHN-Client-Capability"
        if client_cap_header in req.headers_in:
            client_caps = req.headers_in[client_cap_header]
            client_caps = [_f.strip() for _f in client_caps.split(",") if _f.strip()]
            rhnCapability.set_client_capabilities(client_caps)

        # Enabling the input header flags associated with the redirects/newer clients
        redirect_support_flags = ["X-RHN-Redirect", "X-RHN-Transport-Capability"]
        for flag in redirect_support_flags:
            if flag in req.headers_in:
                rhnFlags.set(flag, str(req.headers_in[flag]))

        return apache.OK

    def _init_request_processor(self, req):
        log_debug(3)
        # Override the parent class's behaviour
        # figure out what kind of request handler we need to instantiate
        if req.method == "POST":
            self._req_processor = apachePOST(self.clientVersion, req)
            return apache.OK
        if req.method == "GET":
            try:
                self._req_processor = apacheGET(self.clientVersion, req)
            except HandlerNotFoundError:
                e = sys.exc_info()[1]
                # pylint: disable-next=consider-using-f-string
                log_error("Unable to handle GET request for server %s" % (e.args[0],))
                return apache.HTTP_METHOD_NOT_ALLOWED
            token = self._setSessionToken(req.headers_in)
            if token is None:
                return apache.HTTP_METHOD_NOT_ALLOWED
            return apache.OK

        log_error("Method not allowed", req.method)
        return apache.HTTP_METHOD_NOT_ALLOWED

    def _cleanup_request_processor(self):
        """Clean up the request processor"""
        self._req_processor = None
        return apache.OK

    def handler(self, req):
        """main Apache handler"""
        log_debug(2)
        ret = apacheSession.handler(self, req)
        if ret != apache.OK:
            return ret

        if not CFG.SEND_MESSAGE_TO_ALL:
            # Need to get any string template overrides here, before any app
            # code gets executed, as the rhnFault error messages use the
            # templates
            # If send_message_to_all, we don't have DB connectivity though
            h = rhnSQL.prepare("select label, value from rhnTemplateString")
            h.execute()

            templateStrings = {}
            while 1:
                row = h.fetchone_dict()
                if not row:
                    break

                templateStrings[row["label"]] = row["value"]

            if templateStrings:
                rhnFlags.set("templateOverrides", templateStrings)

            # pylint: disable-next=consider-using-f-string
            log_debug(4, "template strings:  %s" % templateStrings)

        if not CFG.SECRET_KEY:
            # Secret key not defined, complain loudly
            try:
                raise rhnException("Secret key not found!")
            # pylint: disable-next=bare-except
            except:
                rhnTB.Traceback(mail=1, req=req, severity="schema")
                req.status = 500
                req.send_http_header()
                return apache.OK

        # Try to authenticate the proxy if it this request passed
        # through a proxy.
        if self.proxyVersion:
            try:
                ret = self._req_processor.auth_proxy()
            except rhnFault:
                f = sys.exc_info()[1]
                return self._req_processor.response(f.getxml())

        # Decide what to do with the request: try to authenticate the client.
        # NOTE: only upon GET requests is there Signature information to
        #       authenticate. XMLRPC requests DO NOT use signature
        #       authentication.
        if req.method == "GET":
            try:
                ret = self._req_processor.auth_client()
            except rhnFault:
                f = sys.exc_info()[1]
                return self._req_processor.response(f.getxml())
            # be safe rather than sorry
            if not ret:
                log_error("Got a GET call, but auth_client declined", req.path_info)
                return apache.HTTP_METHOD_NOT_ALLOWED

        # Avoid leaving Oracle deadlocks
        try:
            ret = self._req_processor.process()
            rhnSQL.rollback()
        # pylint: disable-next=unused-variable
        except Exception as exc:
            if not CFG.SEND_MESSAGE_TO_ALL:
                rhnSQL.rollback()
            raise
        log_debug(4, "Leave with return value", ret)
        return ret

    # pylint: disable-next=arguments-renamed
    def cleanupHandler(self, req):
        """Clean up stuff before we close down the session when we are called
        from apacheServer.Cleanup()"""
        log_debug(2)
        # kill all of our child processes (if any)
        while 1:
            pid = status = -1
            try:
                (pid, status) = os.waitpid(-1, 0)
            except OSError:
                break
            else:
                # pylint: disable-next=consider-using-f-string
                log_error("Reaped child process %d with status %d" % (pid, status))
        ret = apacheSession.cleanupHandler(self, req)
        return ret
