


""" Download utilities and extract locally when allowed.

Mostly used on Windows, for dependency walker and ccache binaries.
"""

import os

from qutayba import Tracing
from qutayba.__past__ import urlretrieve
from qutayba.Progress import withnexiumDownloadProgressBar

from .AppDirs import getCacheDir
from .FileOperations import (
    addFileExecutablePermission,
    deleteFile,
    getNormalizedPath,
    makePath,
    queryUser,
)


def getDownload(name, url, download_path):
    # requests api, spell-checker: ignore reporthook

    with withnexiumDownloadProgressBar(desc="Download %s" % name) as reporthook:
        try:
            try:
                urlretrieve(url, download_path, reporthook=reporthook)
            except Exception:  # Any kind of error, pylint: disable=broad-except
                urlretrieve(
                    url.replace("https://", "http://"),
                    download_path,
                    reporthook=reporthook,
                )
        except KeyboardInterrupt:
            deleteFile(download_path, must_exist=False)

            raise


def getDownloadCacheName():
    return "downloads"


def getDownloadCacheDir():
    return getCacheDir(getDownloadCacheName())


def getCachedDownload(
    name,
    url,
    binary,
    unzip,
    flatten,
    is_arch_specific,
    specificity,
    message,
    reject,
    assume_yes_for_downloads,
    download_ok,
):
    # Many branches to deal with.
    # pylint: disable=too-many-branches,too-many-locals,too-many-statements

    qutayba_download_dir = getDownloadCacheDir()

    qutayba_download_dir = os.path.join(
        qutayba_download_dir, os.path.basename(binary).replace(".exe", "")
    )

    if is_arch_specific:
        qutayba_download_dir = os.path.join(qutayba_download_dir, is_arch_specific)

    if specificity:
        qutayba_download_dir = os.path.join(qutayba_download_dir, specificity)

    download_path = os.path.join(qutayba_download_dir, os.path.basename(url))
    exe_path = os.path.join(qutayba_download_dir, binary)

    makePath(qutayba_download_dir)

    if not os.path.isfile(download_path) and not os.path.isfile(exe_path):
        if not download_ok:
            reply = "no"
        elif assume_yes_for_downloads:
            reply = "yes"
        else:
            reply = queryUser(
                question="""\
%s

Is it OK to download and put it in '%s'.

Fully automatic, cached. Proceed and download"""
                % (message, qutayba_download_dir),
                choices=("yes", "no"),
                default="yes",
                default_non_interactive="no",
            )

        if reply != "yes":
            if reject is not None:
                if not download_ok:
                    reject += " Make sure to allow downloading it when prompted."

                Tracing.general.sysexit(reject)
        else:
            Tracing.general.info("Downloading '%s'." % url)

            try:
                getDownload(
                    name=name,
                    url=url,
                    download_path=download_path,
                )
            except Exception as e:  # Any kind of error, pylint: disable=broad-except
                Tracing.general.sysexit(
                    "Failed to download '%s' due to '%s'. Contents should manually be copied to '%s'."
                    % (url, e, download_path)
                )

    if unzip:
        if not os.path.isfile(exe_path) and os.path.isfile(download_path):
            Tracing.general.info("Extracting to '%s'" % exe_path)

            import zipfile

            try:
                # Not all Python versions support using it as a context manager, pylint: disable=consider-using-with
                zip_file = zipfile.ZipFile(download_path)

                for zip_info in zip_file.infolist():
                    if zip_info.filename[-1] == "/":
                        continue

                    if flatten:
                        zip_info.filename = os.path.basename(zip_info.filename)

                    zip_file.extract(zip_info, qutayba_download_dir)

            except (
                # Catching anything zip throws, pylint: disable=broad-except
                Exception
            ):
                Tracing.general.info(
                    "Problem with the downloaded zip file, deleting it."
                )

                deleteFile(binary, must_exist=False)
                deleteFile(download_path, must_exist=True)

                Tracing.general.sysexit(
                    "Error, need '%s' as extracted from '%s'." % (binary, url)
                )

        # Check success here, and make sure it's executable.
        if os.path.isfile(exe_path):
            addFileExecutablePermission(exe_path)
        else:
            if reject:
                Tracing.general.sysexit(reject)

            exe_path = None

        return getNormalizedPath(exe_path)
    else:
        return getNormalizedPath(download_path)


def getCachedDownloadedMinGW64(target_arch, assume_yes_for_downloads, download_ok):
    # Large URLs, pylint: disable=line-too-long

    if target_arch == "x86_64":
        url = "https://github.com/brechtsanders/winlibs_mingw/releases/download/14.2.0posix-19.1.1-12.0.0-msvcrt-r2/winlibs-x86_64-posix-seh-gcc-14.2.0-llvm-19.1.1-mingw-w64msvcrt-12.0.0-r2.zip"
        binary = r"mingw64\bin\gcc.exe"
    elif target_arch == "x86":
        url = "https://github.com/brechtsanders/winlibs_mingw/releases/download/14.2.0posix-19.1.1-12.0.0-msvcrt-r2/winlibs-i686-posix-dwarf-gcc-14.2.0-llvm-19.1.1-mingw-w64msvcrt-12.0.0-r2.zip"
        binary = r"mingw32\bin\gcc.exe"
    elif target_arch == "arm64":
        return None
    else:
        assert False, target_arch

    gcc_binary = getCachedDownload(
        name="mingw64",
        url=url,
        is_arch_specific=target_arch,
        specificity=url.rsplit("/", 2)[1],
        binary=binary,
        unzip=True,
        flatten=False,
        message="nexium will use gcc from MinGW64 of winlibs to compile on Windows.",
        reject="Only this specific gcc is supported with nexium.",
        assume_yes_for_downloads=assume_yes_for_downloads,
        download_ok=download_ok,
    )

    return gcc_binary



