#!/usr/bin/env bash
# freeze_python_runtime.sh — genera <app_dir>/runtime/python/ embebido
# para distribuir graph_explorer (u otra app) sin dependencia de WSL
# ni del .venv del registry.
#
# Issue 0033 fase B.
#
# Uso:
#   tools/freeze_python_runtime.sh <app_dir> <platform>
#     <platform> = linux | windows
#
# Lee `python_runtime_deps` del frontmatter de <app_dir>/app.md
# (lista YAML inline). Tambien acepta override via env var:
#   PY_DEPS="requests certifi urllib3" tools/freeze_python_runtime.sh ...
#
# Idempotente — calcula un hash de (PY_VERSION + deps + platform) y
# lo guarda en runtime/.lock. Si coincide con el actual, no rehace.
#
# Salida (Windows):
#   <app_dir>/runtime/python/python.exe
#   <app_dir>/runtime/python/Lib/site-packages/<deps>/...
#
# Salida (Linux):
#   <app_dir>/runtime/python/bin/python3
#   <app_dir>/runtime/python/lib/...

set -euo pipefail

PY_VERSION="${PY_VERSION:-3.12.7}"
APP_DIR="${1:?app_dir requerido}"
PLATFORM="${2:?platform requerido (linux|windows)}"

if [[ ! -d "$APP_DIR" ]]; then
    echo "ERROR: $APP_DIR no es un directorio" >&2
    exit 1
fi

RUNTIME_DIR="$APP_DIR/runtime/python"
APP_MD="$APP_DIR/app.md"

# ----------------------------------------------------------------------------
# Resolver dependencias: PY_DEPS (env) > frontmatter de app.md > vacio.
# ----------------------------------------------------------------------------
deps_from_env="${PY_DEPS:-}"
deps_from_md=""

if [[ -z "$deps_from_env" && -f "$APP_MD" ]]; then
    # Parser ad-hoc del frontmatter (entre el primer y segundo `---`).
    # Busca lineas que empiezan por `  - ` despues de una linea
    # `python_runtime_deps:`. Suficiente para el formato YAML simple
    # que usamos en los app.md del registry. Si necesitamos algo mas
    # complejo (anidados, comentarios), portarlo a Python o usar yq.
    deps_from_md=$(awk '
        /^---$/ { fm = !fm; next }
        !fm { next }
        /^python_runtime_deps:[[:space:]]*$/ { collecting = 1; next }
        collecting && /^[[:space:]]*-[[:space:]]+/ {
            sub(/^[[:space:]]*-[[:space:]]+/, "")
            sub(/[[:space:]]*#.*$/, "")
            gsub(/[\047"]/, "")
            print
            next
        }
        collecting && /^[^[:space:]-]/ { collecting = 0 }
    ' "$APP_MD" | xargs)
fi

DEPS="${deps_from_env:-$deps_from_md}"

# ----------------------------------------------------------------------------
# Hash del estado: si coincide con runtime/.lock no rehacemos nada.
# ----------------------------------------------------------------------------
state_hash=$(echo -n "$PY_VERSION|$PLATFORM|$DEPS" | sha256sum | cut -d' ' -f1)
lock_file="$APP_DIR/runtime/.lock"
if [[ -f "$lock_file" ]]; then
    cur=$(cat "$lock_file" 2>/dev/null || echo "")
    if [[ "$cur" == "$state_hash" ]]; then
        echo "freeze: sin cambios (.lock = $state_hash)"
        exit 0
    fi
fi

# ----------------------------------------------------------------------------
# Limpieza y creacion.
# ----------------------------------------------------------------------------
echo "freeze: $PLATFORM, deps=[$DEPS], py=$PY_VERSION"
rm -rf "$RUNTIME_DIR"
mkdir -p "$RUNTIME_DIR"

if [[ "$PLATFORM" == "windows" ]]; then
    # Windows embedded distribution (zip oficial python.org).
    zip="python-${PY_VERSION}-embed-amd64.zip"
    cache="${TMPDIR:-/tmp}/$zip"
    if [[ ! -f "$cache" ]]; then
        echo "freeze: descargando $zip..."
        curl -sSL --fail \
            "https://www.python.org/ftp/python/$PY_VERSION/$zip" \
            -o "$cache.tmp"
        mv "$cache.tmp" "$cache"
    fi
    unzip -oq "$cache" -d "$RUNTIME_DIR"

    # Habilitar site-packages (el embedded viene con ._pth restrictivo).
    short=$(echo "$PY_VERSION" | awk -F. '{print $1$2}')
    pth="$RUNTIME_DIR/python${short}._pth"
    if [[ -f "$pth" ]]; then
        sed -i 's|^#import site|import site|' "$pth"
    fi

    # Instalar deps con pip "host" usando el embedded como target.
    if [[ -n "$DEPS" ]]; then
        echo "freeze: pip install $DEPS (target=$RUNTIME_DIR/Lib/site-packages)"
        # `--platform win_amd64 --only-binary=:all:` fuerza wheels
        # binarios para Windows aunque pip corra en Linux.
        python3 -m pip install --quiet \
            --target "$RUNTIME_DIR/Lib/site-packages" \
            --platform win_amd64 --only-binary=:all: \
            --python-version "$PY_VERSION" \
            $DEPS
    fi

elif [[ "$PLATFORM" == "linux" ]]; then
    # En Linux preferimos `uv` si esta disponible — descarga un
    # Python standalone (de python-build-standalone) que no depende
    # del Python del sistema y empaqueta todo. Si no, fallback a
    # `python3 -m venv` (requiere python3-venv del sistema).
    if command -v uv >/dev/null 2>&1; then
        # uv mantiene un cache global de Pythons standalone (de
        # python-build-standalone) en ~/.local/share/uv/python/. Para
        # un runtime distribuible copiamos ese arbol completo (~82 MB)
        # y luego instalamos deps con su pip propio. Sin esto el venv
        # quedaria con symlinks al cache de uv que se rompen al
        # mover la carpeta a otra maquina.
        uv python install "$PY_VERSION" >/dev/null 2>&1 || true
        py_root=$(uv python find "$PY_VERSION" 2>/dev/null | xargs -I{} dirname {} | xargs dirname)
        if [[ -z "$py_root" || ! -d "$py_root" ]]; then
            echo "ERROR: no se localizo Python $PY_VERSION via uv" >&2
            exit 4
        fi
        cp -r "$py_root/." "$RUNTIME_DIR/"
        # python-build-standalone deja un marker EXTERNALLY-MANAGED
        # (PEP 668) que bloquea pip install. Es nuestro runtime, no
        # gestion del sistema — lo eliminamos.
        find "$RUNTIME_DIR" -name "EXTERNALLY-MANAGED" -delete 2>/dev/null || true
        py_bin="$RUNTIME_DIR/bin/python3"
        if [[ -n "$DEPS" ]]; then
            echo "freeze: pip install $DEPS"
            "$py_bin" -m pip install --quiet --no-warn-script-location $DEPS
        fi
    else
        python3 -m venv "$RUNTIME_DIR" --copies >/dev/null
        if [[ ! -x "$RUNTIME_DIR/bin/pip" ]]; then
            echo "ERROR: pip no disponible. Instala uv o python3-venv del sistema." >&2
            exit 3
        fi
        if [[ -n "$DEPS" ]]; then
            echo "freeze: pip install $DEPS"
            "$RUNTIME_DIR/bin/pip" install --quiet $DEPS
        fi
    fi
else
    echo "ERROR: platform desconocida: $PLATFORM (esperado linux|windows)" >&2
    exit 2
fi

echo "$state_hash" > "$lock_file"
echo "freeze: OK ($RUNTIME_DIR)"
