#  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 re
import sys

# common module
from spacewalk.common import rhnFlags
from spacewalk.common.rhnLog import log_debug

# local module
from . import rhnSQL

# Globally store the parsed capabilities in rhnFlags


def set_client_capabilities(capabilities):
    if not capabilities:
        # No capabilities presented; don't set the global flag
        return

    caps = {}
    regexp = re.compile(
        r"^(?P<name>[^(]*)\((?P<version>[^)]*)\)\s*=\s*(?P<value>\S.*)$"
    )
    for cap in capabilities:
        mo = regexp.match(cap)
        if not mo:
            # XXX Just ignoring it, for now
            continue
        # pylint: disable-next=redefined-builtin
        dict = mo.groupdict()
        name = dict["name"].strip()
        version = dict["version"].strip()
        value = dict["value"].strip()

        caps[name] = {
            "version": version,
            "value": value,
        }

    rhnFlags.set("client-capabilities", caps)
    log_debug(4, "Client capabilities", caps)


def get_client_capabilities():
    return rhnFlags.get("client-capabilities")


def update_client_capabilities(server_id):
    caps = get_client_capabilities()

    if caps is None:
        caps = {}

    caps = caps.copy()

    h = rhnSQL.prepare(
        """
        select cc.capability_name_id, ccn.name capability, cc.version
        from rhnClientCapability cc, rhnClientCapabilityName ccn
        where cc.server_id = :server_id
        and cc.capability_name_id = ccn.id
    """
    )

    updates = {"server_id": [], "capability_name_id": [], "version": []}
    deletes = {"server_id": [], "capability_name_id": []}
    inserts = {"server_id": [], "capability": [], "version": []}

    h.execute(server_id=server_id)
    while 1:
        row = h.fetchone_dict()
        if not row:
            break

        name = row["capability"]
        version = row["version"]
        capability_name_id = row["capability_name_id"]

        if name in caps:
            local_ver = caps[name]["version"]
            del caps[name]
            if local_ver == version:
                # Nothing to do - same version
                continue

            updates["server_id"].append(server_id)
            updates["capability_name_id"].append(capability_name_id)
            updates["version"].append(local_ver)
            continue

        # Have to delete it
        deletes["server_id"].append(server_id)
        deletes["capability_name_id"].append(capability_name_id)

    # Everything else has to be inserted
    # pylint: disable-next=redefined-builtin
    for name, hash in list(caps.items()):
        inserts["server_id"].append(server_id)
        inserts["capability"].append(name)
        inserts["version"].append(hash["version"])

    log_debug(5, "Deletes:", deletes)
    log_debug(5, "Updates:", updates)
    log_debug(5, "Inserts:", inserts)

    if deletes["server_id"]:
        h = rhnSQL.prepare(
            """
            delete from rhnClientCapability
            where server_id = :server_id
            and capability_name_id = :capability_name_id
        """
        )
        h.executemany(**deletes)

    if updates["server_id"]:
        h = rhnSQL.prepare(
            """
            update rhnClientCapability
            set version = :version
            where server_id = :server_id
            and capability_name_id = :capability_name_id
        """
        )
        h.executemany(**updates)

    if inserts["server_id"]:
        h = rhnSQL.prepare(
            """
            insert into rhnClientCapability
            (server_id, capability_name_id, version)
            values (:server_id, LOOKUP_CLIENT_CAPABILITY(:capability), :version)
        """
        )
        h.executemany(**inserts)

    # Commit work. This can be dangerous if there is previously uncommited
    # work
    rhnSQL.commit()


def set_server_capabilities():
    try:
        _set_server_capabilities()
    except rhnSQL.SQLError:
        e = sys.exc_info()[1]
        if e.args[0] != 1:
            # Not a unique constraint violation
            raise
        # Try again
        _set_server_capabilities()


def _set_server_capabilities():
    # XXX Will have to figure out how to define this
    capabilities = {
        "registration.register_osad": {"version": 1, "value": 1},
        "registration.finish_message": {"version": 1, "value": 1},
        "registration.remaining_subscriptions": {"version": 1, "value": 1},
        "registration.update_contact_info": {"version": 1, "value": 1},
        "registration.delta_packages": {"version": 1, "value": 1},
        "registration.extended_update_support": {"version": 1, "value": 1},
        "registration.smbios": {"version": 1, "value": 1},
        "registration.update_systemid": {"version": 1, "value": 1},
        "xmlrpc.login.extra_data": {"version": 1, "value": 1},
        "rhncfg.content.base64_decode": {"version": 1, "value": 1},
        "rhncfg.filetype.directory": {"version": 1, "value": 1},
        "xmlrpc.packages.extended_profile": {"version": "1-2", "value": 1},
        "xmlrpc.packages.suse_products": {"version": 1, "value": 1},
        "xmlrpc.packages.checksums": {"version": 1, "value": 1},
        "xmlrpc.errata.patch_names": {"version": 1, "value": 1},
        "staging_content": {"version": 1, "value": 1},
        "ipv6": {"version": 1, "value": 1},
        "cpu_sockets": {"version": 1, "value": 1},
        "queue.update_status": {"version": 1, "value": 1},
        "mainframe_sysinfo": {"version": 1, "value": 1},
        "machine_info": {"version": 1, "value": 1},
    }
    l = []
    for name, hashval in list(capabilities.items()):
        # pylint: disable-next=consider-using-f-string
        l.append("%s(%s)=%s" % (name, hashval["version"], hashval["value"]))

    log_debug(4, "Setting capabilities", l)
    rhnFlags.get("outputTransportOptions")["X-RHN-Server-Capability"] = l
