#!/usr/bin/python3

import argparse
import json
import os
import sys
from os.path import join

from debian import deb822

DEFAULT_BOM_DIR = "/usr/share/doc"
DEFAULT_DPKG_STATUS = "/var/lib/dpkg/status"
DEFAULT_APT_LIST_DIR = "/var/lib/apt/lists"

VERBOSE_IMAGE = 0
VERBOSE_PACKAGE = 1
VERBOSE_BINARY = 2
VERBOSE_SOURCE = 3

ERROR_NONE = 0
ERROR_ERROR = 1
ERROR_WARN = 2


class BomChecker:
    def __init__(
        self,
        bom_file,
        apt_list_dir,
        verbose,
        error_level=ERROR_NONE,
        whitelisted_packages="",
    ):
        self.bom_file = bom_file
        self.apt_list_dir = apt_list_dir
        self.verbose = verbose
        self.error_level = error_level
        self.error = False
        self.whitelisted_packages = whitelisted_packages

        self.packages = set()

    def get_packages(self):
        for f in os.listdir(self.apt_list_dir):
            if not f.endswith("Sources"):
                continue

            sources = open(join(self.apt_list_dir, f)).read()
            source_packages = deb822.Sources(sources)

            for p in source_packages.iter_paragraphs(sources, use_apt_pkg=False):
                self.packages.add(p["Package"])

    def check_bom(self):
        bom = json.load(self.bom_file)
        for package in bom:
            external_files = bom[package]["external_files"]
            for external_file in external_files:
                referenced_package = external_file["name"]
                if referenced_package not in self.packages:
                    if self.error_level >= ERROR_ERROR:
                        self.error = True
                    print(
                        f"WARNING Package {referenced_package} is referenced by {package} but is not in apt sources",
                        file=sys.stderr,
                    )


def main(argv):
    parser = argparse.ArgumentParser()
    parser.add_argument("bom_file", type=open, help="BOM file to check")
    parser.add_argument(
        "-e",
        "--error-level",
        type=int,
        default=ERROR_NONE,
        help="type of error that triggers a return code unsuccessful 0: none , 1: error, 2: warning",
    )
    parser.add_argument(
        "-l",
        "--apt-list-dir",
        default=DEFAULT_APT_LIST_DIR,
        help="directory with apt lists",
    )
    parser.add_argument(
        "-v",
        "--verbose",
        type=int,
        default=VERBOSE_IMAGE,
        help="verbose use in output 0: image, 1: package, 2: binary, 3: source",
    )
    parser.add_argument(
        "-w",
        "--whitelisted-packages",
        default="",
        help="file containing a list of whitelisted packages",
    )

    args = parser.parse_args()

    bom_checker = BomChecker(
        args.bom_file,
        args.apt_list_dir,
        args.verbose,
        args.error_level,
        args.whitelisted_packages,
    )
    bom_checker.get_packages()
    bom_checker.check_bom()

    if bom_checker.error:
        print("BOM check has failed", file=sys.stderr)
        sys.exit(1)


if __name__ == "__main__":
    main(sys.argv[1:])
