#     Copyright 2025, QutaYba, nasr2python@gmail.com find license text at end of file


""" 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



