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

from uyuni.common.usix import ListType

from spacewalk.common import rhnFlags
from spacewalk.common.rhnLog import log_debug, log_error
from spacewalk.common.rhnConfig import cfg_component
from spacewalk.common.rhnException import rhnFault
from spacewalk.common.rhnTranslate import _
from spacewalk.server import rhnSQL
from .rhnLib import parseRPMFilename

#
# Functions that deal with the database
#


# New client
# Returns a package path, given a server_id, package filename and channel label
def get_package_path(server_id, pkg_spec, channel):
    log_debug(3, server_id, pkg_spec, channel)
    checksum = None
    if isinstance(pkg_spec, ListType):
        pkg = pkg_spec[:4]
        # Insert EPOCH
        pkg.insert(1, None)
    else:
        if "/" in pkg_spec:
            # pylint: disable-next=unused-variable
            org, checksum, pkg_spec = pkg_spec.split("/", 2)
        pkg = parseRPMFilename(pkg_spec)
        if pkg is None:
            log_debug(4, "Error", "Requested weird package", pkg_spec)
            raise rhnFault(17, _("Invalid RPM package %s requested") % pkg_spec)

    statement = """
        select  p.id, p.path path, pe.epoch epoch
        from
                rhnPackageArch pa,
                rhnChannelPackage cp,
                rhnPackage p,
                rhnChecksum cs,
                rhnPackageEVR pe,
                rhnServerChannel sc,
                rhnPackageName pn,
                rhnChannel c
        where 1=1
            and c.label = :channel
            and pn.name = :name
            and sc.server_id = :server_id
            and pe.version = :ver
            and pe.release = :rel
            and c.id = sc.channel_id
            and c.id = cp.channel_id
            and pa.label = :arch
            and pn.id = p.name_id
            and p.id = cp.package_id
            and p.evr_id = pe.id
            and sc.channel_id = cp.channel_id
            and p.package_arch_id = pa.id
            and p.checksum_id = cs.id
            and (:checksum IS NULL OR cs.checksum = :checksum)
    """
    pkg = list(map(str, pkg))
    h = rhnSQL.prepare(statement)
    h.execute(
        name=pkg[0],
        ver=pkg[2],
        rel=pkg[3],
        arch=pkg[4],
        channel=channel,
        server_id=server_id,
        checksum=checksum,
    )
    rs = h.fetchall_dict()
    if not rs:
        log_debug(
            4, "Error", "Non-existant package requested", server_id, pkg_spec, channel
        )
        raise rhnFault(17, _("Invalid RPM package %s requested") % pkg_spec)
    # It is unlikely for this query to return more than one row,
    # but it is possible
    # (having two packages with the same n, v, r, a and different epoch in
    # the same channel is prohibited by the RPM naming scheme; but extra
    # care won't hurt)
    max_row = rs[0]
    for each in rs[1:]:
        # Compare the epoch as string
        if _none2emptyString(each["epoch"]) > _none2emptyString(max_row["epoch"]):
            max_row = each

    # Set the flag for the proxy download accelerator
    rhnFlags.set("Download-Accelerator-Path", max_row["path"])
    return check_package_file(max_row["path"], max_row["id"], pkg_spec), max_row["id"]


def check_package_file(rel_path, logpkg, raisepkg):
    if rel_path is None:
        log_error("Package path null for package id", logpkg)
        raise rhnFault(17, _("Invalid RPM package %s requested") % raisepkg)
    with cfg_component("server.satellite") as CFG:
        filePath = f"{CFG.MOUNT_POINT}/{rel_path}"
    if not os.access(filePath, os.R_OK):
        # Package not found on the filesystem
        log_error("Package not found", filePath)
        raise rhnFault(17, _("Package not found"))

    return filePath


def unlink_package_file(path):
    try:
        os.unlink(path)
    except OSError:
        # pylint: disable-next=consider-using-f-string
        log_debug(1, "Error unlinking %s;" % path)
    dirname = os.path.dirname(path)
    with cfg_component("server.satellite") as CFG:
        base_dirs = (CFG.MOUNT_POINT + "/" + CFG.PREPENDED_DIR, CFG.MOUNT_POINT)
    while dirname not in base_dirs:
        try:
            os.rmdir(dirname)
        except OSError:
            e = sys.exc_info()[1]
            if e.errno == 39:  # OSError: [Errno 39] Directory not empty
                break
            else:
                raise
        dirname = os.path.dirname(dirname)


def get_all_package_paths(server_id, pkg_spec, channel):
    """
    return the remote path if available and localpath
    for the requested package with respect to package id
    """
    log_debug(3, server_id, pkg_spec, channel)
    remotepath = None
    # get the path and package
    # pylint: disable-next=unused-variable
    localpath, pkg_id = get_package_path(server_id, pkg_spec, channel)

    return remotepath, localpath


# New client
# Returns the path to a source rpm


def get_source_package_path(server_id, pkgFilename, channel):
    log_debug(3, server_id, pkgFilename, channel)
    rs = __query_source_package_path_by_name(server_id, pkgFilename, channel)
    if rs is None:
        log_debug(
            4,
            "Error",
            "Non-existant package requested",
            server_id,
            pkgFilename,
            channel,
        )
        raise rhnFault(17, _("Invalid RPM package %s requested") % pkgFilename)

    # Set the flag for the proxy download accelerator
    rhnFlags.set("Download-Accelerator-Path", rs["path"])
    return check_package_file(rs["path"], pkgFilename, pkgFilename)


# 0 or 1: is this source in this channel?
def package_source_in_channel(server_id, pkgFilename, channel):
    log_debug(3, server_id, pkgFilename, channel)
    rs = __query_source_package_path_by_name(server_id, pkgFilename, channel)
    if rs is None:
        return 0
    return 1


# The query used both in get_source_package_path and package_source_in_channel
def __query_source_package_path_by_name(server_id, pkgFilename, channel):
    statement = """
    select
            unique ps.path
    from
            rhnSourceRPM sr,
            rhnPackageSource ps,
            rhnPackage p,
            rhnChannelPackage cp,
            rhnChannel c,
            rhnServerChannel sc
    where
                sc.server_id = :server_id
            and sc.channel_id = cp.channel_id
            and cp.channel_id = c.id
            and c.label = :channel
            and cp.package_id = p.id
            and p.source_rpm_id = sr.id
            and sr.name = :name
            and p.source_rpm_id = ps.source_rpm_id
            and ((p.org_id is null and ps.org_id is null)
                or p.org_id = ps.org_id)
    """
    h = rhnSQL.prepare(statement)
    h.execute(name=pkgFilename, channel=channel, server_id=server_id)
    return h.fetchone_dict()


def get_info_for_package(pkg, channel_id, org_id):
    log_debug(3, pkg)
    pkg = list(map(str, pkg))
    params = {
        "name": pkg[0],
        "ver": pkg[1],
        "rel": pkg[2],
        "epoch": pkg[3],
        "arch": pkg[4],
        "channel_id": channel_id,
        "org_id": org_id,
    }
    # yum repo has epoch="0" not only when epoch is "0" but also if it's NULL
    # in DB we cannot insert an empty string, so we check for NULL or '0'
    if pkg[3] == "0" or pkg[3] == "" or pkg[3] is None:
        epochStatement = "(epoch is null or epoch = '0')"
    else:
        epochStatement = "epoch = :epoch"
    if params["org_id"]:
        orgStatement = "(org_id = :org_id or org_id is null)"
    else:
        orgStatement = "org_id is null"

    # pylint: disable-next=consider-using-f-string
    statement = """
    select p.path, cp.channel_id,
           cv.checksum_type, cv.checksum, p.org_id, pe.epoch, p.id as package_id
      from rhnPackage p
      join rhnPackageName pn
        on p.name_id = pn.id
      join rhnPackageEVR pe
        on p.evr_id = pe.id
      join rhnPackageArch pa
        on p.package_arch_id = pa.id
      left join rhnChannelPackage cp
        on p.id = cp.package_id
       and cp.channel_id = :channel_id
      join rhnChecksumView cv
        on p.checksum_id = cv.id
     where pn.name = :name
       and pe.version = :ver
       and pe.release = :rel
       and %s
       and pa.label = :arch
       and %s
     order by p.org_id nulls first, cp.channel_id nulls last,
              p.id desc
    """ % (
        epochStatement,
        orgStatement,
    )

    h = rhnSQL.prepare(statement)
    h.execute(**params)

    ret = h.fetchall_dict() or []
    if not ret:
        return ret
    for i in ret:
        # pylint: disable-next=singleton-comparison
        if i["org_id"] == None:
            i["org_id"] = ""
        else:
            i["org_id"] = str(i["org_id"])
    return ret


def _none2emptyString(foo):
    if foo is None:
        return ""
    return str(foo)


def add_eula_to_package(package_id, eula_id):
    """Associates an EULA to a package"""

    h = rhnSQL.prepare("""
        SELECT *
          FROM susePackageEula
         WHERE package_id = :package_id
           AND eula_id    = :eula_id
    """)
    h.execute(package_id=package_id, eula_id=eula_id)
    ret = h.fetchone_dict()

    if not ret:
        h = rhnSQL.prepare("""
            INSERT INTO susePackageEula (package_id, eula_id)
                 VALUES (:package_id, :eula_id)
        """)
        h.execute(package_id=package_id, eula_id=eula_id)


if __name__ == "__main__":
    # pylint: disable-next=pointless-string-statement
    """Test code."""
    from spacewalk.common.rhnLog import initLOG

    initLOG("stdout", 1)
    rhnSQL.initDB()
    print("")
    # new client
    print(
        (
            get_package_path(
                1000463284, "kernel-2.4.2-2.i686.rpm", "redhat-linux-i386-7.1"
            )
        )
    )
    print(
        (
            get_source_package_path(
                1000463284, "kernel-2.4.2-2.i686.rpm", "redhat-linux-i386-7.1"
            )
        )
    )
