# Copyright 2023 Oliver Smith
# SPDX-License-Identifier: GPL-3.0-or-later
import logging

import pmb.build
import pmb.helpers.package
import pmb.helpers.pmaports


def filter_missing_packages(args, arch, pkgnames):
    """Create a subset of pkgnames with missing or outdated binary packages.

    :param arch: architecture (e.g. "armhf")
    :param pkgnames: list of package names (e.g. ["hello-world", "test12"])
    :returns: subset of pkgnames (e.g. ["hello-world"])
    """
    ret = []
    for pkgname in pkgnames:
        binary = pmb.parse.apkindex.package(args, pkgname, arch, False)
        must_exist = False if binary else True
        pmaport = pmb.helpers.pmaports.get(args, pkgname, must_exist)
        if pmaport and pmb.build.is_necessary(args, arch, pmaport):
            ret.append(pkgname)
    return ret


def filter_aport_packages(args, arch, pkgnames):
    """Create a subset of pkgnames where each one has an aport.

    :param arch: architecture (e.g. "armhf")
    :param pkgnames: list of package names (e.g. ["hello-world", "test12"])
    :returns: subset of pkgnames (e.g. ["hello-world"])
    """
    ret = []
    for pkgname in pkgnames:
        if pmb.helpers.pmaports.find(args, pkgname, False):
            ret += [pkgname]
    return ret


def filter_arch_packages(args, arch, pkgnames):
    """Create a subset of pkgnames with packages removed that can not be built for a certain arch.

    :param arch: architecture (e.g. "armhf")
    :param pkgnames: list of package names (e.g. ["hello-world", "test12"])
    :returns: subset of pkgnames (e.g. ["hello-world"])
    """
    ret = []
    for pkgname in pkgnames:
        if pmb.helpers.package.check_arch(args, pkgname, arch, False):
            ret += [pkgname]
    return ret


def get_relevant_packages(args, arch, pkgname=None, built=False):
    """Get all packages that can be built for the architecture in question.

    :param arch: architecture (e.g. "armhf")
    :param pkgname: only look at a specific package (and its dependencies)
    :param built: include packages that have already been built
    :returns: an alphabetically sorted list of pkgnames, e.g.:
        ["devicepkg-dev", "hello-world", "osk-sdl"]
    """
    if pkgname:
        if not pmb.helpers.package.check_arch(args, pkgname, arch, False):
            raise RuntimeError(pkgname + " can't be built for " + arch + ".")
        ret = pmb.helpers.package.depends_recurse(args, pkgname, arch)
    else:
        ret = pmb.helpers.pmaports.get_list(args)
        ret = filter_arch_packages(args, arch, ret)
    if built:
        ret = filter_aport_packages(args, arch, ret)
        if not len(ret):
            logging.info("NOTE: no aport found for any package in the"
                         " dependency tree, it seems they are all provided by"
                         " upstream (Alpine).")
    else:
        ret = filter_missing_packages(args, arch, ret)
        if not len(ret):
            logging.info("NOTE: all relevant packages are up to date, use"
                         " --built to include the ones that have already been"
                         " built.")

    # Sort alphabetically (to get a deterministic build order)
    ret.sort()
    return ret


def generate_output_format(args, arch, pkgnames):
    """Generate the detailed output format.

    :param arch: architecture
    :param pkgnames: list of package names that should be in the output,
        e.g.: ["hello-world", "pkg-depending-on-hello-world"]
        :returns: a list like the following:
        [{"pkgname": "hello-world",
        "repo": "main",
        "version": "1-r4",
        "depends": []},
        {"pkgname": "pkg-depending-on-hello-world",
        "version": "0.5-r0",
        "repo": "main",
        "depends": ["hello-world"]}]
    """
    ret = []
    for pkgname in pkgnames:
        entry = pmb.helpers.package.get(args, pkgname, arch, True)
        ret += [{"pkgname": entry["pkgname"],
                 "repo": pmb.helpers.pmaports.get_repo(args, pkgname),
                 "version": entry["version"],
                 "depends": entry["depends"]}]
    return ret


def generate(args, arch, overview, pkgname=None, built=False):
    """Get packages that need to be built, with all their dependencies.

    :param arch: architecture (e.g. "armhf")
    :param pkgname: only look at a specific package
    :param built: include packages that have already been built
    :returns: a list like the following:
        [{"pkgname": "hello-world", "repo": "main", "version": "1-r4"},
        {"pkgname": "package-depending-on-hello-world", "version": "0.5-r0", "repo": "main"}]
    """
    # Log message
    packages_str = pkgname if pkgname else "all packages"
    logging.info("Calculate packages that need to be built ({}, {})"
                 "".format(packages_str, arch))

    # Order relevant packages
    ret = get_relevant_packages(args, arch, pkgname, built)

    # Output format
    if overview:
        return ret
    return generate_output_format(args, arch, ret)
