import os.path
import subprocess
from typing import Optional

from debian.debian_support import Version

from debputy.analysis.debian_dir import resolve_debhelper_config_files
from debputy.dh.debhelper_emulation import dhe_pkgfile
from debputy.dh.dh_assistant import read_dh_addon_sequences, extract_dh_compat_level
from debputy.dh_migration.models import AcceptableMigrationIssues, FeatureMigration
from debputy.highlevel_manifest import HighLevelManifest
from debputy.plugin.api import VirtualPath
from debputy.plugin.api.impl import plugin_metadata_for_debputys_own_plugin
from debputy.plugin.api.spec import DebputyIntegrationMode


def _installed_debhelper_version_is_at_least(desired_version: str) -> bool:
    try:
        output = subprocess.check_output(
            [
                "dpkg-query",
                "-W",
                "--showformat=${Version} ${db:Status-Status}\n",
                "debhelper",
            ]
        ).decode("utf-8")
    except (FileNotFoundError, subprocess.CalledProcessError):
        return False
    else:
        parts = output.split()
        if len(parts) != 2:
            return False
        if parts[1] != "installed":
            return False
        return Version(parts[0]) >= Version(desired_version)


def dh_mbm_migrate_package_files(
    debian_dir: VirtualPath,
    manifest: HighLevelManifest,
    _acceptable_migration_issues: AcceptableMigrationIssues,
    feature_migration: FeatureMigration,
    _migration_target: Optional[DebputyIntegrationMode],
) -> None:
    feature_migration.tagline = "Add explicit package name to debhelper config files"
    debputy_plugin_metadata = plugin_metadata_for_debputys_own_plugin()
    dh_compat_level, _ = extract_dh_compat_level()
    r = read_dh_addon_sequences(debian_dir)
    if r is not None:
        bd_sequences, dr_sequences, uses_dh_sequencer = r
        dh_sequences = bd_sequences | dr_sequences
        uses_dh_sequencer = True
    else:
        dh_sequences = set()
        uses_dh_sequencer = False

    (
        all_dh_ppfs,
        missing_introspection,
        _,
        _,
    ) = resolve_debhelper_config_files(
        debian_dir,
        {p.name: p for p in manifest.all_packages},
        debputy_plugin_metadata,
        manifest.plugin_provided_feature_set,
        dh_sequences,
        dh_compat_level,
        uses_dh_sequencer,
    )
    for ppf in all_dh_ppfs:
        if (
            ppf.uses_explicit_package_name
            or ppf.definition.packageless_is_fallback_for_all_packages
        ):
            continue
        basename = ppf.path.name
        new_basename = f"{ppf.package_name}.{basename}"
        ppf_parent = ppf.path.parent_dir
        assert ppf_parent is not None
        parent_path = ppf_parent.path
        new_path = os.path.join(parent_path, new_basename)
        feature_migration.rename_on_success(ppf.path.path, new_path)

    if not missing_introspection and not _installed_debhelper_version_is_at_least(
        "13.26~"
    ):
        feature_migration.warn(
            "Please upgrade to `debhelper/13.26` to ensure reliability. Some files might be silently missed."
        )

    for command in missing_introspection:
        feature_migration.warn(
            f"The command `{command}` is used but `dh_assistant` was not aware of whether it had configuration files."
        )

    if missing_introspection:
        feature_migration.warn(
            "Please file a bug against the provider of the commands asking them to supply the relevant hints (see doc/PROGRAMMING.md in debhelper/13.26+) with the debhelper maintainers in CC."
        )

    feature_migration.warn(
        "Any architecture restricted file (such `debian/install.amd64`) and templating based files (such as debian/install.tmpl) may be missed. Please validate these manually."
    )


def dh_mbm_migrate_manual_warnings(
    debian_dir: VirtualPath,
    manifest: HighLevelManifest,
    _acceptable_migration_issues: AcceptableMigrationIssues,
    feature_migration: FeatureMigration,
    _migration_target: Optional[DebputyIntegrationMode],
) -> None:
    feature_migration.tagline = "Manual parts"
    r = read_dh_addon_sequences(debian_dir)
    if r is not None:
        bd_sequences, dr_sequences, uses_dh_sequencer = r
        dh_sequences = bd_sequences | dr_sequences
        uses_dh_sequencer = True
    else:
        dh_sequences = set()
        uses_dh_sequencer = False
    drules = debian_dir.get("rules")
    if drules and drules.is_file:
        with drules.open() as fd:
            migrated_auto_install = 0
            for line in fd:
                line = line.lstrip()
                if not line or line.startswith("#"):
                    continue
                if line.startswith("\t") and "dh_auto_install" in line:
                    migrated_auto_install += 1 if "--destdir" in line else 0
    used_dh_install_files = 0
    for dctrl_bin in manifest.all_packages:
        dh_config_file = dhe_pkgfile(debian_dir, dctrl_bin, "install")
        if dh_config_file is not None:
            used_dh_install_files += 1
    main_pkg = [p for p in manifest.all_packages if p.is_main_package][0]
    if not migrated_auto_install and not used_dh_install_files:
        feature_migration.warn(
            f"Ensure `dh_auto_install --destdir debian/{main_pkg.name}` or `dh_install` is used. Remember Replaces/Breaks if files are moved"
        )
    if uses_dh_sequencer and "single-binary" in dh_sequences:
        feature_migration.warn("Remove `single-binary` add-on")
