#!/usr/bin/env python3
'Adds the latest miniforge and mambaforge releases.'
from pathlib import Path
import logging
import os
import string

import requests

logger = logging.getLogger(__name__)
logging.basicConfig(level=os.environ.get('LOGLEVEL', 'INFO'))

MINIFORGE_REPO = 'conda-forge/miniforge'
DISTRIBUTIONS = ['miniforge']
DISTRIBUTIONS_PRE25  = ['miniforge', 'mambaforge']

SKIPPED_RELEASES = [
    '4.13.0-0',     #has no Mambaforge. We already generated scripts for Miniforge
    '22.11.1-0',    #MacOS packages are broken (have broken dep tarballs, downloading them fails with 403)
    '22.11.1-1',    #MacOS packages are broken (have broken dep tarballs, downloading them fails with 403)
    '22.11.1-2',    #MacOS packages are broken (have broken dep tarballs, downloading them fails with 403)
    '25.3.0-0',     #marked as prerelease, no Linux version
    '25.11.0-0',    #regression reported in constructor, re-released as 25.11.0-1 with hotfix bumping to constructor>=3.14 (was >=3.12, 3.13 implicit)
]

install_script_fmt = """
case "$(anaconda_architecture 2>/dev/null || true)" in
{install_lines}
* )
  {{ echo
    colorize 1 "ERROR"
    echo ": The binary distribution of {flavor} is not available for $(anaconda_architecture 2>/dev/null || true)."
    echo
  }} >&2
  exit 1
  ;;
esac
""".lstrip()

install_line_fmt = """
"{os}-{arch}" )
  install_script "{filename}" "{url}#{sha}" "miniconda" verify_py{py_version}
  ;;
""".strip()

here = Path(__file__).resolve()
out_dir: Path = here.parent.parent / "share" / "python-build"

def download_sha(url):
    logger.info(f'Downloading SHA file {url}')
    tup = tuple(reversed(requests.get(url).text.replace('./', '').rstrip().split()))
    logger.debug(f'Got {tup}')
    return tup

def create_spec(filename, sha, url):
    flavor_with_suffix, version, subversion, os, arch = filename.replace('.sh', '').split('-')
    suffix = flavor_with_suffix[-1]

    if suffix in string.digits:
        flavor = flavor_with_suffix[:-1]
    else:
        flavor = flavor_with_suffix

    spec = {
        'filename': filename,
        'sha': sha,
        'url': url,
        'py_version': py_version(version),
        'flavor': flavor,
        'os': os,
        'arch': arch,
        'installer_filename': f'{flavor_with_suffix.lower()}-{version}-{subversion}',
    }

    logger.debug(f'Created spec {spec}')

    return spec

def version_tuple(version):
    return tuple(int(part) for part in version.split('-')[0].split("."))

def py_version(version):
    """Suffix for `verify_pyXXX` to call in the generated build script"""
    version_tuple_ = version_tuple(version)
    # current version: mentioned under https://github.com/conda-forge/miniforge?tab=readme-ov-file#requirements-and-installers
    # transition points:
    # https://github.com/conda-forge/miniforge/blame/main/Miniforge3/construct.yaml
    # look for "- python <version>" in non-pypy branch and which tag the commit is first in
    if version_tuple_ >= (26,1):
        # https://github.com/conda-forge/miniforge/commit/0016367731e52c67234d6d0e7e6a24c6bf7673e4
        return "313"
    if version_tuple_ >= (24,5):
        # yes, they jumped from 3.10 directly to 3.12
        # https://github.com/conda-forge/miniforge/commit/bddad0baf22b37cfe079e47fd1680fdfb2183590
        return "312"
    if version_tuple_ >= (4,14):
        return "310"
    raise ValueError("Bundled Python version unknown for release `%s'"%version)

def supported(filename):
    return ('pypy' not in filename) and ('Windows' not in filename) and (not filename.endswith('.pkg'))

def add_version(release, distributions):
    tag_name = release['tag_name']
    download_urls = { f['name']: f['browser_download_url'] for f in release['assets'] }
    # can assume that sha files are named similar to release files so can also check supported(on their names)
    shas = dict([download_sha(url) for (name, url) in download_urls.items()
        if name.endswith('.sha256') and supported(os.path.splitext(name)[0]) and tag_name in name])
    specs = [create_spec(filename, sha, download_urls[filename]) for (filename, sha) in shas.items() if supported(filename)]
    

    for distribution in distributions:
        distribution_specs = [spec for spec in specs if distribution in spec['flavor'].lower()]
        count = len(distribution_specs)

        if count > 0:
            output_file = out_dir / distribution_specs[0]['installer_filename']

            logger.info(f'Writing {count} specs for {distribution} to {output_file}')

            script_str = install_script_fmt.format(
                install_lines="\n".join([install_line_fmt.format_map(s) for s in distribution_specs]),
                flavor=distribution_specs[0]['flavor'],
            )

            with open(output_file, 'w') as f:
                f.write(script_str)
        else:
            logger.info(f'Did not find specs for {distribution}')


def main():
    for release in requests.get(f'https://api.github.com/repos/{MINIFORGE_REPO}/releases').json():
        version = release['tag_name']

        if version in SKIPPED_RELEASES:
            continue

        logger.info(f'Looking for {version} in {out_dir}')

        # mambaforge is retired https://github.com/conda-forge/miniforge/releases/tag/24.11.2-0
        if version_tuple(version) >= (24, 11, 2):
            distributions = DISTRIBUTIONS
        else:
            distributions = DISTRIBUTIONS_PRE25

        if any(not list(out_dir.glob(f'{distribution}*-{version}')) for distribution in distributions):
            logger.info(f'Downloading {version}')
            add_version(release, distributions)

if __name__ == '__main__':
    main()
