# Copyright 2023 Danct12 <danct12@disroot.org>
# SPDX-License-Identifier: GPL-3.0-or-later
from collections.abc import Sequence
from pmb.core.chroot import Chroot
from pmb.core.pkgrepo import (
    pkgrepo_iter_package_dirs,
    pkgrepo_name,
    pkgrepo_names,
    pkgrepo_relative_path,
)
from pmb.helpers import logging
from pmb.helpers.exceptions import NonBugError
from pmb.helpers.toml import load_toml_file
from pmb.types import RunOutputTypeDefault
import os

import pmb.chroot
import pmb.chroot.apk
import pmb.build


def get_custom_valid_options() -> list[str]:
    """Build a list of custom valid APKBUILD options that apkbuild-lint should
    not complain about. The list consists of hardcoded options from
    pmb.config.apkbuild_custom_valid_options like pmb:drm, as well as
    dynamically generated options from kconfigcheck.toml
    (pmb:kconfigcheck-libcamera etc.)."""
    ret = list(pmb.config.apkbuild_custom_valid_options)

    # Load kconfigcheck.toml from current branch
    kconfigcheck_toml = load_toml_file(pmb.parse.kconfigcheck.get_path())
    pmb.parse.kconfigcheck.sanity_check(kconfigcheck_toml)

    # Add options like "pmb:kconfigcheck-libcamera"
    for section in kconfigcheck_toml:
        if not section.startswith("category:"):
            continue
        # section looks like: "category:input.>=0.0.0.all"
        category = section.split(".")[0].replace("category:", "", 1)
        ret += [f"pmb:kconfigcheck-{category}"]

    # Add aliases like "pmb:kconfigcheck-community"
    for alias in kconfigcheck_toml["aliases"]:
        ret += [f"pmb:kconfigcheck-{alias}"]

    return ret


# FIXME: dest_paths[repo], repo expected to be a Literal.
# We should really make Config.mirrors not a TypedDict.
# mypy: disable-error-code="index"
def check(pkgnames: Sequence[str]) -> None:
    """Run apkbuild-lint on the supplied packages.

    :param pkgnames: Names of the packages to lint
    """
    chroot = Chroot.native()
    pmb.chroot.init(chroot)
    pmb.chroot.apk.install(["atools"], chroot)

    # Mount pmaports.git inside the chroot so that we don't have to copy the
    # package folders
    dest_paths = pmb.build.mount_pmaports(chroot)

    # Locate all APKBUILDs and make the paths be relative to the pmaports
    # root
    apkbuilds: dict[str, list[str]] = {x: [] for x in pkgrepo_names()}
    found_pkgnames = set()
    # If a package exists in multiple aports we will lint all of them
    # since.. well, what else do we do?
    for pkgdir in pkgrepo_iter_package_dirs():
        if pkgdir.name not in pkgnames:
            continue

        repo, relpath = pkgrepo_relative_path(pkgdir)
        apkbuilds[pkgrepo_name(repo)].append(os.fspath(relpath / "APKBUILD"))
        found_pkgnames.add(pkgdir.name)

    # Check we found all the packages in pkgnames
    if len(found_pkgnames) != len(pkgnames):
        missing = set(pkgnames) - found_pkgnames
        logging.error(f"Could not find the following packages: {missing}")
        return

    # Run apkbuild-lint in chroot from the pmaports mount point. This will
    # print a nice source identifier à la "./cross/grub-x86/APKBUILD" for
    # each violation.
    pkgstr = ", ".join(pkgnames)
    logging.info(f"(native) linting {pkgstr} with apkbuild-lint")

    # apkbuild-lint output is not colorized, make it easier to spot
    logging.info("*** apkbuild-lint output ***")

    # For each pkgrepo run the linter on the relevant packages
    has_failed = False
    for apkbuild_paths in apkbuilds.values():
        # We search for the pkgnames in both the normal and systemd repository,
        # so unless the pkgname exists in both the apkbuild_paths is empty for
        # one of them and needs to be skipped here. This is not very elegant,
        # let's rework the CI logic for this at some point: pma#3665
        if not apkbuild_paths:
            continue
        if pmb.chroot.user(
            ["apkbuild-lint", *apkbuild_paths],
            check=False,
            output=RunOutputTypeDefault.STDOUT,
            working_dir=dest_paths[pkgrepo_name(repo)],
            env={"CUSTOM_VALID_OPTIONS": " ".join(get_custom_valid_options())},
        ):
            has_failed = True

    logging.info("*** apkbuild-lint output ***")

    if has_failed:
        raise NonBugError("Linter failed!")
