import json
import os
import subprocess
from pathlib import Path

from clarabot_nano.utility.java import get_java_version_output, parse_java_major_version
from clarabot_nano.utility.path.java import java_compatible_absolute_path
from dev_tools.build.sign.file import sign_file_for_authenticity


def get_java_version(major_version_only=False):
    output = get_java_version_output()
    major = parse_java_major_version(output)
    assert major and major >= 11, f"{major}\n{output}"
    if major_version_only:
        return major
    return "\t" + "\n\t".join(line.strip() for line in output.split("\n") if line)


def get_java_modules():
    return [
        line.rpartition("@")[0]
        for line in subprocess.check_output(
            ["java", "--list-modules"], encoding="utf-8", universal_newlines=True
        ).split("\n")
        if line
    ]


def get_java_module_deps_of_jars(jar_paths):
    """
    This has issues with some jars. It's bad packaging/version conflicts mostly, but they cannot be easily solved.
    If this fails the get_java_modules() should be used to include all modules until the issue can be resolved.
    """
    args = [
        "jdeps",
        "-quiet",
        "--list-deps",
        # Ignoring missing dependencies should be fine. Right now we're only interested in the used modules from
        # the builtin set of the JRE, which cannot be not found. All other dependencies that a project has and may
        # not be able to find are meaningless because each JAR is inspected individually.
        # This is an OpenJDK 11.0.11+ feature.
        "--ignore-missing-deps",
        "-recursive",
        "--multi-release",
        str(get_java_version(major_version_only=True)),
    ]

    def _call_jdeps(jars_):
        lines = []
        for line in subprocess.check_output(args + jars_, encoding="utf-8", universal_newlines=True).split("\n"):
            line = line.strip()
            if line and line.find(" ") == -1:
                lines.append(line.partition("/")[0])
        return lines

    jars = []
    deps = set()
    for jar_path in jar_paths:
        jar_name = jar_path.name
        if jar_name.startswith("asm-"):
            # The "asm" libraries depend on each other in some way that allows some of them to be absent. (some
            # dynamic loading I guess) Since jdeps cannot produce a successful output, all the libraries were
            # acquired and jdeps-ed separately. (from https://repo1.maven.org/maven2/org/ow2/asm/)
            lib_name, _, lib_version = jar_name.rpartition(".")[0].rpartition("-")
            if lib_name in ("asm", "asm-commons", "asm-tree", "asm-analysis"):
                if lib_version == "8.0.1":
                    deps.update(["java.base"])
                else:
                    raise Exception(
                        "Unknown version of the asm libs found. Please update the build script with their dependencies."
                    )
                continue
        jars.append(java_compatible_absolute_path(jar_path))
    deps.update(_call_jdeps(jars))
    # Remove some dependencies that are... what even is this? Why can't Java dependencies just behave sanely?
    removed_deps = [
        "jdk8internals",  # This must be some mistake or internal quackery, because we're not using Java 8 anymore...
        "kotlin.stdlib.jdk7",  # This must be some mistake or internal quackery again... jdk7, for what?
    ]
    deps.difference_update(removed_deps)
    return sorted(deps)


def get_jar_paths_from_folder(path):
    jars = []
    for root, dirs, files in os.walk(path):
        for file in files:
            if file.lower().endswith(".jar"):
                jars.append(Path(root, file))
    return jars


def cache_discover_java_modules_from_jars(
    cache_dir: Path, discover_dir: Path, name, version, java_version, force=False
):
    version = [version, java_version]  # include the java version as well for cache dependency
    cache_deps_dir = cache_dir / "java_mod_deps"
    cache_deps_dir.mkdir(parents=True, exist_ok=True)
    cache_file = cache_deps_dir / f"{name}.json"
    rerun = True
    if cache_file.exists() and not force:
        data = json.loads(cache_file.read_text("utf-8"))
        if data.get("version") == version:
            modules = data["modules"]
            rerun = False
    if rerun:
        jar_paths = get_jar_paths_from_folder(discover_dir)
        modules = get_java_module_deps_of_jars(jar_paths)
        cache_file.write_text(json.dumps({"version": version, "modules": modules}), "utf-8")
    return modules


def build_deps_jre(deps_dir: Path, java_modules, skip_signing: bool = False):
    jre_dir = deps_dir / "jre"
    subprocess.check_call(
        [
            "jlink",
            "--no-header-files",
            "--no-man-pages",
            "--compress=2",
            "--strip-debug",
            "--add-modules",
            ",".join(java_modules),
            "--output",
            java_compatible_absolute_path(jre_dir),
        ]
    )
    if not skip_signing:
        sign_file_for_authenticity(*[item for item in (jre_dir / "bin").iterdir() if item.name.endswith(".exe")])
    return jre_dir
