#!/usr/bin/python3

import os
import shlex
import sys

from debmutate._rules import Makefile, Rule, dh_invoke_get_with
from debmutate.control import (
    PkgRelation,
    add_dependency,
    is_relation_implied,
    parse_relations,
)

from lintian_brush.fixer import LintianIssue, control, report_result
from lintian_brush.lintian import LINTIAN_DATA_PATH, dh_addons, dh_commands

COMMAND_TO_DEP = {}


if not os.path.isdir(LINTIAN_DATA_PATH):
    # lintian doesn't appear to be installed
    sys.exit(2)


for command, info in dh_commands().items():
    COMMAND_TO_DEP[command] = " | ".join(info["installed_by"])

# Copied from /usr/share/lintian/lib/Lintian/Check/Debhelper.pm
COMMAND_TO_DEP.update(
    {
        "dh_apache2": "dh-apache2 | apache2-dev",
        "dh_autoreconf_clean": "dh-autoreconf | debhelper (>= 9.20160403~) | debhelper-compat",
        "dh_autoreconf": "dh-autoreconf | debhelper (>= 9.20160403~) | debhelper-compat",
        "dh_dkms": "dkms | dh-sequence-dkms",
        "dh_girepository": "gobject-introspection | dh-sequence-gir",
        "dh_gnome": "gnome-pkg-tools | dh-sequence-gnome",
        "dh_gnome_clean": "gnome-pkg-tools | dh-sequence-gnome",
        "dh_lv2config": "lv2core",
        "dh_make_pgxs": "postgresql-server-dev-all | postgresql-all",
        "dh_nativejava": "gcj-native-helper | default-jdk-builddep",
        "dh_pgxs_test": "postgresql-server-dev-all | postgresql-all",
        "dh_python2": "dh-python | dh-sequence-python2",
        "dh_python3": "dh-python | dh-sequence-python3",
        "dh_sphinxdoc": "sphinx | python-sphinx | python3-sphinx",
        "dh_xine": "libxine-dev | libxine2-dev",
    }
)


ADDON_TO_DEP = {}
for addon, info in dh_addons().items():
    ADDON_TO_DEP[addon] = " | ".join(info["installed_by"])


ADDON_TO_DEP.update(
    {
        # Copied from /usr/share/lintian/lib/Lintian/Check/Debhelper.pm
        "ada_library": "dh-ada-library | dh-sequence-ada-library",
        "apache2": "dh-apache2 | apache2-dev",
        "autoreconf": "dh-autoreconf | debhelper (>= 9.20160403~) | debhelper-compat",
        "cli": "cli-common-dev | dh-sequence-cli",
        "dwz": "debhelper | debhelper-compat | dh-sequence-dwz",
        "installinitramfs": "debhelper | debhelper-compat | dh-sequence-installinitramfs",
        "gnome": "gnome-pkg-tools | dh-sequence-gnome",
        "lv2config": "lv2core",
        "nodejs": "pkg-js-tools | dh-sequence-nodejs",
        "perl_dbi": "libdbi-perl | dh-sequence-perl-dbi",
        "perl_imager": "libimager-perl | dh-sequence-perl-imager",
        "pgxs": "postgresql-server-dev-all | postgresql-all",
        "pgxs_loop": "postgresql-server-dev-all | postgresql-all",
        "pypy": "dh-python | dh-sequence-pypy",
        "python2": "python2:any | python2-dev:any | dh-sequence-python2",
        "python3": "python3:any | python3-all:any | python3-dev:any | "
        "python3-all-dev:any | dh-sequence-python3",
        "scour": "scour | python-scour | dh-sequence-scour",
        "sphinxdoc": "sphinx | python-sphinx | python3-sphinx | dh-sequence-sphinxdoc",
        "systemd": "debhelper (>= 9.20160709~) | debhelper-compat | "
        "dh-sequence-systemd | dh-systemd",
        "vim_addon": "dh_vim-addon | dh-sequence-vim-addon",
    }
)

need = []

try:
    mf = Makefile.from_path("debian/rules")
except FileNotFoundError:
    sys.exit(0)

for entry in mf.contents:
    if isinstance(entry, Rule):
        for command in entry.commands():
            if command.lstrip().startswith(b"#"):
                continue
            try:
                executable = shlex.split(command.decode("utf-8", "replace"))[0]
            except (ValueError, IndexError):
                continue
            try:
                dep = COMMAND_TO_DEP[executable]
            except KeyError:
                pass
            else:
                issue = LintianIssue(
                    "source",
                    "missing-build-dependency-for-dh_-command",
                    f"{executable} => {dep}",
                )
                if not issue.should_fix():
                    continue
                need.append((dep, [issue], "command", executable))
            if executable == "dh" or executable.startswith("dh_"):
                for addon in dh_invoke_get_with(command):
                    try:
                        dep = ADDON_TO_DEP[addon]
                    except KeyError:
                        pass
                    else:
                        issue = LintianIssue(
                            "source",
                            "missing-build-dependency-for-dh-addon",
                            f"{addon} => {dep}",
                        )
                        need.append((dep, [issue], "addon", addon))


if not need:
    sys.exit(0)

changed = []

with control as updater:
    for deps, issues, kind, name in need:
        parsed = PkgRelation.parse(deps)
        is_implied = False

        if is_relation_implied(parsed, "debhelper"):
            is_implied = True

        for field in [
            "Build-Depends",
            "Build-Depends-Indep",
            "Build-Depends-Arch",
        ]:
            for _unused1, existing, _unused2 in parse_relations(
                updater.source.get(field, "")
            ):
                if is_relation_implied(parsed, existing):
                    is_implied = True

        if not is_implied:
            build_deps = updater.source.get("Build-Depends", "")
            updater.source["Build-Depends"] = add_dependency(build_deps, deps)
            for issue in issues:
                issue.report_fixed()
            changed.append((deps, issue, kind, name))

if len(changed) == 1:
    (dep, issue, kind, name) = changed[0]
    report_result(f"Add missing build dependency on {dep} for {kind} {name}.")
else:
    report_result(
        "Add missing build dependencies:"
        + "\n".join(
            f"* {dep} for {kind} {name}"
            for (dep, issue, kind, name) in changed
        )
    )
