#!/bin/bash
# bajar-paquetes: baja paquetes del servidor de descargas, comprobando su md5,
# y si se pide los instala. La contraparte de subir-paquetes. Reemplaza a
# download.sh e installsumma.sh, que bajaban sin comprobar el certificado y
# averiguando la IP del servidor con 8.8.8.8 (07/10/2026, ver BIN.md).
#
#   bajar-paquetes [opciones] PROYECTO[/PAQUETE]...
#   bajar-paquetes [opciones] ARCHIVO.summa...
#
# Primero baja PROYECTO.summa (queda en el directorio) y despues, con lo que
# dice ese .summa (PACKAGES y VERSION), los binarios. Con un .summa que ya se
# tiene, baja directamente los binarios que el dice.
#
#   PROYECTO      todos sus paquetes (los dice PROYECTO.summa, arriba en el
#                 servidor): tabular2 baja libtabular2-0, libtabular2-dev y
#                 tabular; summapack baja summapack, libmem-util0 y -dev
#   PROYECTO/PAQUETE   uno solo: tabular2/libtabular2-0
#   --formato F   deb, rpm, tgz, tar.gz o summa. Por omision, el del sistema:
#                 deb en Debian y Ubuntu, rpm en Fedora, Red Hat y SUSE, tgz
#                 en los demas
#   --inestable   la ultima subida, o la estable si no hay nada mas nuevo;
#                 por omision, la estable (la VERSION de PROYECTO.summa)
#   --version V   una version dada (6.0-38)
#   --dir D       donde dejarlos (por omision ~/.cache/summapack)
#   --instalar    instalarlos: apt, dnf/zypper, o instalar-tgz para los .tgz
#   --prueba      mostrar que bajaria e instalaria, sin hacerlo
#
# En el servidor (ver subir-paquetes): PROYECTO.summa arriba (dice los
# paquetes y la version estable), los estables en PROYECTO/, y en
# PROYECTO/otros/ los .md5, lo demas y PAQ_UNSTABLE... mientras haya algo
# inestable. Cada archivo se baja con su .md5 y queda con su nombre de verdad
# (calcula_6.0-38_amd64.deb). Si ya esta en el directorio con el md5
# correcto, no se vuelve a bajar.
#
# El servidor: BAJAR_URL en el entorno o en ~/.config/simusol/subir; por
# omision https://simusol.org/downloads. Admite file:// para probar.
#
# Es un solo archivo, sin mas que bash, curl y md5sum: installsumma lo baja
# del servidor para instalar summapack en una maquina que no lo tiene.
set -euo pipefail

# ~/.config/simusol/subir solo completa lo que no venga en el entorno: el
# entorno manda (07/10/2026: al reves, una prueba con SUBIR_SERVIDOR=local
# termino corriendo contra el servidor real)
for _v in BAJAR_URL BAJAR_DIR; do eval "_entorno_$_v=\${$_v-}"; done
[ -f "$HOME/.config/simusol/subir" ] && . "$HOME/.config/simusol/subir"
for _v in BAJAR_URL BAJAR_DIR; do eval "[ -z \"\$_entorno_$_v\" ] || $_v=\$_entorno_$_v"; done
URL=${BAJAR_URL:-https://simusol.org/downloads}
DIR=${BAJAR_DIR:-$HOME/.cache/summapack}

formato=""; etiqueta=estable; instalar=0; prueba=0; nombres=()
while [ $# -gt 0 ]; do
    case "$1" in
        --formato)   formato=$2; shift ;;
        --inestable) etiqueta=UNSTABLE ;;
        --version)   etiqueta=$2; shift ;;
        --dir)       DIR=$2; shift ;;
        --instalar)  instalar=1 ;;
        --prueba)    prueba=1 ;;
        -h|--help)   sed -n '2,41s/^# \{0,1\}//p' "$0"; exit 0 ;;
        -*)          echo "bajar-paquetes: no conozco $1 (--help)" >&2; exit 2 ;;
        *)           nombres+=("$1") ;;
    esac
    shift
done
set -- "${nombres[@]+"${nombres[@]}"}"
[ $# -gt 0 ] || { echo "uso: bajar-paquetes [opciones] PROYECTO[/PAQUETE]... (--help)" >&2; exit 2; }

# el formato del sistema, como lo diria distro (sin depender de el)
sistema() {
    local id='' like=''
    if [ -r /etc/os-release ]; then
        id=$(. /etc/os-release; echo "${ID:-}")
        like=$(. /etc/os-release; echo "${ID_LIKE:-}")
    fi
    case " $id $like " in
        *" debian "*|*" ubuntu "*) echo deb ;;
        *" fedora "*|*" rhel "*|*" centos "*|*" suse "*|*" opensuse "*|*" mandriva "*) echo rpm ;;
        *) if command -v dpkg >/dev/null; then echo deb
           elif command -v rpm >/dev/null; then echo rpm
           else echo tgz; fi ;;
    esac
}
[ -n "$formato" ] || formato=$(sistema)
case "$formato" in deb|rpm|tgz|tar.gz|summa) ;; *) echo "bajar-paquetes: formato desconocido: $formato" >&2; exit 2 ;; esac

arq_deb=$(dpkg --print-architecture 2>/dev/null || { case "$(uname -m)" in x86_64) echo amd64 ;; aarch64) echo arm64 ;; *) uname -m ;; esac; })
arq_rpm=$(uname -m)

# los nombres de un paquete con una version (o UNSTABLE)
nombres() {
    local n=$1 e=$2
    case "$formato" in
        deb)    echo "${n}_${e}_${arq_deb}.deb ${n}_${e}_all.deb" ;;
        tgz)    echo "${n}_${e}_${arq_deb}.tgz ${n}_${e}_all.tgz" ;;
        rpm)    echo "${n}-${e}.${arq_rpm}.rpm ${n}-${e}.noarch.rpm" ;;
        tar.gz) echo "${n}_${e}.tar.gz" ;;
        summa)  echo "${n}_${e}.summa" ;;
    esac
}

traer() {   # traer ARCHIVO_REMOTO DESTINO: falla en silencio si no esta
    curl -fsSL --retry 2 -o "$2" "$URL/$1" 2>/dev/null
}

mkdir -p "$DIR"

# el PROYECTO.summa del servidor, una vez por proyecto: dice los paquetes
# (PACKAGES) y la version estable (VERSION, la agrega subir-paquetes --estable)
declare -A summas=()
summa_de() {   # deja en SUMMA el .summa del proyecto; no se llama en un $(...)
    local p=$1
    if [ -z "${summas[$p]:-}" ]; then
        # primero el .summa: se baja y queda en DIR, y de el sale todo lo demas
        if ! traer "$p.summa" "$DIR/$p.summa.parcial"; then
            rm -f "$DIR/$p.summa.parcial"
            echo "bajar-paquetes: no hay $p.summa en $URL (se pide el proyecto: tabular2, no libtabular2-0; para uno solo, PROYECTO/PAQUETE)" >&2
            return 1
        fi
        mv "$DIR/$p.summa.parcial" "$DIR/$p.summa"
        echo "summa: $DIR/$p.summa"
        summas[$p]=$DIR/$p.summa
    fi
    SUMMA=${summas[$p]}
}
campo() { sed -n "s/^$2=\"\\(.*\\)\"\$/\\1/p" "$1" | tail -1; }

pedidos=()
for a in "$@"; do
    # un .summa que ya se tiene: los binarios que el dice
    if [ -f "$a" ] && [ "${a%.summa}" != "$a" ]; then
        p=$(basename "$a" .summa); p=${p%%_*}
        summas[$p]=$(readlink -f "$a")
        a=$p
    fi
    case "$a" in
        */*) pedidos+=("$a") ;;
        *)   case "$formato" in
                 tar.gz|summa) pedidos+=("$a/$a") ;;
                 *) summa_de "$a" || exit 1
                    for q in $(campo "$SUMMA" PACKAGES); do pedidos+=("$a/$q"); done ;;
             esac ;;
    esac
done

# intentar MD5 ARCHIVO...: baja el .md5 y el archivo (del primer lugar donde
# este) y comprueba la suma; deja en hallado lo bajado
intentar() {
    local m=$1; shift
    local md5tmp suma real u
    if [ $prueba = 1 ]; then echo "  [prueba] probaria $URL/$m y $URL/$1"; return 1; fi
    md5tmp=$(mktemp)
    traer "$m" "$md5tmp" || { rm -f "$md5tmp"; return 1; }
    suma=$(cut -d' ' -f1 < "$md5tmp")
    real=$(awk '{print $2}' < "$md5tmp" | sed 's/^\*//')
    [ -n "$real" ] || real=$(basename "$1")
    real=$(basename "$real")
    if [ -f "$DIR/$real" ] && [ "$(md5sum < "$DIR/$real" | cut -d' ' -f1)" = "$suma" ]; then
        echo "ya estaba: $DIR/$real"
    else
        rm -f "$DIR/$real.parcial"
        for u in "$@"; do traer "$u" "$DIR/$real.parcial" && break; done
        [ -s "$DIR/$real.parcial" ] || { rm -f "$md5tmp" "$DIR/$real.parcial"; echo "bajar-paquetes: esta $m pero no se pudo bajar el archivo" >&2; exit 1; }
        if [ "$(md5sum < "$DIR/$real.parcial" | cut -d' ' -f1)" != "$suma" ]; then
            rm -f "$DIR/$real.parcial" "$md5tmp"
            echo "bajar-paquetes: $real bajo con otro md5 que el del servidor: se descarta" >&2
            exit 1
        fi
        mv "$DIR/$real.parcial" "$DIR/$real"
        echo "bajado: $DIR/$real"
    fi
    cp "$md5tmp" "$DIR/$real.md5"
    rm -f "$md5tmp"
    hallado=$DIR/$real
}

bajados=()
for pq in "${pedidos[@]}"; do
    p=${pq%%/*}; nombre=${pq#*/}
    hallado=''
    # lo inestable: el nombre fijo, mientras haya algo inestable
    if [ "$etiqueta" = UNSTABLE ]; then
        for c in $(nombres "$nombre" UNSTABLE); do
            intentar "$p/otros/$c.md5" "$p/otros/$c" && break
        done
    fi
    # lo estable (o una version dada): el nombre exacto
    if [ -z "$hallado" ]; then
        case "$etiqueta" in
            estable|UNSTABLE) summa_de "$p" || exit 1; v=$(campo "$SUMMA" VERSION)
                           [ -n "$v" ] || { echo "bajar-paquetes: $p.summa no dice VERSION: nada estable todavia (--inestable)" >&2; exit 1; } ;;
            *)             v=$etiqueta ;;
        esac
        for c in $(nombres "$nombre" "$v"); do
            intentar "$p/otros/$c.md5" "$p/$c" "$p/otros/$c" && break
        done
    fi
    if [ $prueba = 0 ]; then
        [ -n "$hallado" ] || { echo "bajar-paquetes: no hay $p/$nombre ($formato, $etiqueta) en $URL" >&2; exit 1; }
        bajados+=("$hallado")
    else
        bajados+=("$DIR/$(nombres "$nombre" "${v:-$etiqueta}" | cut -d' ' -f1)")
    fi
done

[ $instalar = 1 ] || exit 0

orden() {
    if [ $prueba = 1 ]; then echo "  [prueba] $*"; else echo "+ $*"; "$@"; fi
}
sudo_si_hace_falta() { if [ "$(id -u)" = 0 ]; then orden "$@"; else orden sudo "$@"; fi; }

case "$formato" in
    deb)
        rutas=(); for b in "${bajados[@]}"; do rutas+=("$(readlink -f "$b" 2>/dev/null || echo "$b")"); done
        sudo_si_hace_falta apt-get install --yes "${rutas[@]}" ;;
    rpm)
        if command -v dnf >/dev/null; then sudo_si_hace_falta dnf install -y "${bajados[@]}"
        elif command -v zypper >/dev/null; then sudo_si_hace_falta zypper install -y "${bajados[@]}"
        else sudo_si_hace_falta rpm -Uvh "${bajados[@]}"; fi ;;
    tgz)
        it="$(dirname "$0")/instalar-tgz"; [ -x "$it" ] || it=$(command -v instalar-tgz)
        sudo_si_hace_falta "$it" "${bajados[@]}" ;;
    *)
        echo "bajar-paquetes: un $formato no se instala" >&2; exit 2 ;;
esac
