import subprocess
from functools import lru_cache

import pytest

from typing import Any
from collections.abc import Mapping

from debian.deb822 import Deb822
from debian.debian_support import DpkgArchTable

from debputy.architecture_support import DpkgArchitectureBuildProcessValuesTable
from debputy.packages import BinaryPackage
from debputy.plugin.api.test_api import DEBPUTY_TEST_AGAINST_INSTALLED_PLUGINS
from debputy.util import _error

_DPKG_ARCHITECTURE_TABLE_NATIVE_AMD64 = None
_DPKG_ARCH_QUERY_TABLE = None


def faked_binary_package(
    package, architecture="any", section="misc", is_main_package=False, **fields
) -> BinaryPackage:
    _arch_data_tables_loaded()

    dpkg_arch_table, dpkg_arch_query = _arch_data_tables_loaded()
    return BinaryPackage(
        Deb822(
            {
                "Package": package,
                "Architecture": architecture,
                "Section": section,
                **fields,
            }
        ),
        dpkg_arch_table,
        dpkg_arch_query,
        is_main_package=is_main_package,
    )


def binary_package_table(*args: BinaryPackage) -> Mapping[str, BinaryPackage]:
    packages = list(args)
    if not any(p.is_main_package for p in args):
        p = args[0]
        np = faked_binary_package(
            p.name,
            architecture=p.declared_architecture,
            section=p.archive_section,
            is_main_package=True,
            **{
                k: v
                for k, v in p.fields.items()
                if k.lower() not in ("package", "architecture", "section")
            },
        )
        packages[0] = np
    return {p.name: p for p in packages}


def _arch_data_tables_loaded() -> (
    tuple[DpkgArchitectureBuildProcessValuesTable, DpkgArchTable]
):
    global _DPKG_ARCHITECTURE_TABLE_NATIVE_AMD64
    global _DPKG_ARCH_QUERY_TABLE
    if _DPKG_ARCHITECTURE_TABLE_NATIVE_AMD64 is None:
        _DPKG_ARCHITECTURE_TABLE_NATIVE_AMD64 = DpkgArchitectureBuildProcessValuesTable(
            fake_host="amd64",
        )
    if _DPKG_ARCH_QUERY_TABLE is None:
        # TODO: Make a faked table instead, so we do not have data dependencies in the test.
        _DPKG_ARCH_QUERY_TABLE = DpkgArchTable.load_arch_table()
    return _DPKG_ARCHITECTURE_TABLE_NATIVE_AMD64, _DPKG_ARCH_QUERY_TABLE


def build_time_only(func: Any) -> Any:
    return pytest.mark.skipif(
        DEBPUTY_TEST_AGAINST_INSTALLED_PLUGINS,
        reason="Test makes assumptions only valid during build time tests",
    )(func)


def with_plugins_loaded(*plugin_names: str) -> Any:
    def _wrapper(func: Any) -> Any:
        func._required_plugins = list(plugin_names)
        return func

    return _wrapper


@lru_cache
def is_pkg_installed(pkg: str) -> bool:
    cp = subprocess.run(
        args=(
            "dpkg-query",
            "-Wf${db:Status-Abbrev}\n",
            pkg,
        ),
        capture_output=True,
        check=False,
        text=True,
    )
    # 0: OK    1: unknown package    2: other
    if cp.returncode not in (0, 1):
        _error(f"dpkg-query -W failed (code: {cp.returncode}): {cp.stderr.rstrip()}")

    return len(cp.stdout) > 1 and cp.stdout[1] == "i"
