#!/bin/bash
set -euo pipefail

# ========================================
# Script: propagate_module_from_json.sh
# Descripción: Propaga módulos desde modules.json a destinos definidos en destinations.json
# Utiliza partial clone + sparse-checkout en carpetas temporales, sin afectar tu repo principal
# Con paralelización y interfaz mejorada
# ========================================

# Colores para la interfaz
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
PURPLE='\033[0;35m'
CYAN='\033[0;36m'
NC='\033[0m' # No Color

ROOT_DIR=$(pwd)
MODULES_FILE="$ROOT_DIR/tools/modules.json"
DESTINATIONS_FILE="$ROOT_DIR/tools/destinations.json"
TMP_ORIGIN="$ROOT_DIR/tmp_origin_repo"
TMP_DIR="$ROOT_DIR/tmp_dest_repo"
LOG_DIR="$HOME/XAMPP/htdocs/propagation_logs"

mkdir -p "$LOG_DIR"
start_time=$(date +%s)

# Función para mostrar barra de progreso
show_progress() {
    local current=$1
    local total=$2
    local width=50
    local percentage=$((current * 100 / total))
    local completed=$((current * width / total))
    local remaining=$((width - completed))
    
    printf "\r${CYAN}["
    printf "%${completed}s" | tr ' ' '█'
    printf "%${remaining}s" | tr ' ' '░'
    printf "] %d%% (%d/%d)${NC}" $percentage $current $total
}

# Función para logging con colores
log_message() {
    local level=$1
    local message=$2
    local timestamp=$(date '+%H:%M:%S')
    
    case $level in
        "INFO")  echo -e "${GREEN}[INFO]${NC} ${timestamp} - $message" ;;
        "WARN")  echo -e "${YELLOW}[WARN]${NC} ${timestamp} - $message" ;;
        "ERROR") echo -e "${RED}[ERROR]${NC} ${timestamp} - $message" ;;
        "SUCCESS") echo -e "${GREEN}[✓]${NC} ${timestamp} - $message" ;;
        *)       echo -e "${BLUE}[LOG]${NC} ${timestamp} - $message" ;;
    esac
}

# 1️⃣ Verificar jq
if ! command -v jq &> /dev/null; then
  log_message "ERROR" "Este script requiere 'jq'."
  exit 1
fi

# 2️⃣ Verificar configuración
for f in "$MODULES_FILE" "$DESTINATIONS_FILE"; do
  [ -f "$f" ] || { log_message "ERROR" "No se encuentra $f"; exit 1; }
done

# 3️⃣ Pedir origen
echo -e "${YELLOW}🟡 URL del repo origen:${NC}"
read -p "> " ORIGIN_REPO
echo -e "${YELLOW}🔀 Rama origen:${NC}"
read -p "> " ORIGIN_BRANCH

# 4️⃣ Seleccionar módulos
echo -e "\n${GREEN}✅ Módulos disponibles:${NC}"
jq -r 'keys_unsorted[]' "$MODULES_FILE" | while read -r module; do
    echo -e "${CYAN}- ${module}${NC}"
done
echo
echo -e "${YELLOW}🚀 Módulos a propagar (comma o *):${NC}"
read -p "> " MODULES_INPUT

if [[ "$MODULES_INPUT" == "*" ]]; then
  mapfile -t MODULES < <(jq -r 'keys_unsorted[]' "$MODULES_FILE")
else
  IFS=',' read -ra MODULES <<< "$MODULES_INPUT"
fi

# 5️⃣ Armar lista de archivos
ALL_FILES=()
for MODULE in "${MODULES[@]}"; do
  FILES=( $(jq -r --arg module "$MODULE" '.[$module][]?' "$MODULES_FILE") )
  if [ ${#FILES[@]} -gt 0 ]; then
    ALL_FILES+=("${FILES[@]}")
  else
    log_message "WARN" "Módulo '$MODULE' sin archivos o no existe."
  fi
done
[ ${#ALL_FILES[@]} -gt 0 ] || { log_message "ERROR" "No hay archivos para propagar."; exit 1; }

log_message "INFO" "Total de archivos a propagar: ${#ALL_FILES[@]}"

# 6️⃣ Partial clone + sparse-checkout en TEMPORALES
sparse_clone(){
  local url=$1 branch=$2 dest=$3
  rm -rf "$dest"
  git clone \
    --no-checkout \
    --depth=1 \
    --filter=blob:none \
    --branch "$branch" \
    "$url" "$dest" &> /dev/null
  pushd "$dest" > /dev/null
    git sparse-checkout init --cone
    git sparse-checkout set "${ALL_FILES[@]}"
    git checkout "$branch" &> /dev/null
  popd > /dev/null
}

# Función para verificar un destino en paralelo
check_destination() {
    local index=$1
    local total=$2
    local repo_url=$(jq -r ".[$index].repo" "$DESTINATIONS_FILE")
    local branch_dest=$(jq -r ".[$index].branch" "$DESTINATIONS_FILE")
    local tmp_current="${TMP_DIR}_$(basename "$repo_url" .git)_$branch_dest"
    local result_file="/tmp/check_result_$index"
    
    # Clonar destino
    if sparse_clone "$repo_url" "$branch_dest" "$tmp_current" 2>/dev/null; then
        local needs=false
        for file in "${ALL_FILES[@]}"; do
            if [ -f "$TMP_ORIGIN/$file" ] && { [ ! -f "$tmp_current/$file" ] || ! diff -q "$TMP_ORIGIN/$file" "$tmp_current/$file" &> /dev/null; }; then
                needs=true
                break
            fi
        done
        
        if $needs; then
            echo "OUTDATED|$repo_url|$branch_dest" > "$result_file"
        else
            echo "UPDATED|$repo_url|$branch_dest" > "$result_file"
        fi
    else
        echo "ERROR|$repo_url|$branch_dest" > "$result_file"
    fi
    
    rm -rf "$tmp_current"
}

# 7️⃣ Clonar origen
echo -e "\n${PURPLE}🌱 Clonando origen (parcial)...${NC}"
sparse_clone "$ORIGIN_REPO" "$ORIGIN_BRANCH" "$TMP_ORIGIN"
log_message "SUCCESS" "Origen clonado exitosamente"

# 8️⃣ Comparar destinos en paralelo
TOTAL=$(jq length "$DESTINATIONS_FILE")
LOG_FILE="$LOG_DIR/propagation_$(date +%F_%H%M%S).log"
echo -e "\n${BLUE}💡 Verificando $TOTAL destino(s) en paralelo...${NC}"
echo -e "${CYAN}📋 Log: $LOG_FILE${NC}"
echo "--- Propagando [$(IFS=,; echo "${MODULES[*]}")] desde '$ORIGIN_BRANCH' ---" >> "$LOG_FILE"

# Limpiar archivos de resultados anteriores
rm -f /tmp/check_result_*

# Ejecutar verificaciones en paralelo
pids=()
for ((i=0;i<TOTAL;i++)); do
    check_destination $i $TOTAL &
    pids+=($!)
done

# Mostrar progreso mientras esperamos
completed=0
echo -e "\n${YELLOW}Verificando destinos...${NC}"
while [ $completed -lt $TOTAL ]; do
    completed=0
    for pid in "${pids[@]}"; do
        if ! kill -0 $pid 2>/dev/null; then
            ((completed++))
        fi
    done
    show_progress $completed $TOTAL
    sleep 0.5
done
echo # Nueva línea después de la barra de progreso

# Esperar a que terminen todos los procesos
wait

# Recopilar resultados
OUTDATED=()
UPDATED=0
ERRORS=0

for ((i=0;i<TOTAL;i++)); do
    if [ -f "/tmp/check_result_$i" ]; then
        result=$(cat "/tmp/check_result_$i")
        status=${result%%|*}
        repo_info=${result#*|}
        
        case $status in
            "OUTDATED")
                OUTDATED+=("$repo_info")
                repo_url=${repo_info%%|*}
                branch_dest=${repo_info##*|}
                log_message "WARN" "Necesita actualización: $repo_url ($branch_dest)"
                echo "[!] $repo_url ($branch_dest): desactualizado" >> "$LOG_FILE"
                ;;
            "UPDATED")
                ((UPDATED++))
                repo_url=${repo_info%%|*}
                branch_dest=${repo_info##*|}
                log_message "SUCCESS" "Al día: $repo_url ($branch_dest)"
                echo "[=] $repo_url ($branch_dest): sin cambios" >> "$LOG_FILE"
                ;;
            "ERROR")
                ((ERRORS++))
                repo_url=${repo_info%%|*}
                branch_dest=${repo_info##*|}
                log_message "ERROR" "Error verificando: $repo_url ($branch_dest)"
                echo "[X] $repo_url ($branch_dest): error" >> "$LOG_FILE"
                ;;
        esac
    fi
done

# Limpiar archivos temporales de resultados
rm -f /tmp/check_result_*

# Mostrar resumen
echo -e "\n${PURPLE}📊 Resumen de verificación:${NC}"
echo -e "${GREEN}  ✓ Actualizados: $UPDATED${NC}"
echo -e "${YELLOW}  ⚠ Desactualizados: ${#OUTDATED[@]}${NC}"
echo -e "${RED}  ✗ Errores: $ERRORS${NC}"

# 9️⃣ Si nada que actualizar
if [ ${#OUTDATED[@]} -eq 0 ]; then
  log_message "SUCCESS" "Todos los destinos actualizados."
  echo "🕓 Duración: $(( $(date +%s) - start_time )) seg" >> "$LOG_FILE"
  rm -rf "$TMP_ORIGIN"
  exit 0
fi

# 10️⃣ Confirmar
echo -e "\n${YELLOW}🚀 ${#OUTDATED[@]} destino(s) desactualizados:${NC}"
for d in "${OUTDATED[@]}"; do 
    echo -e "${CYAN} • ${d%%|*} (${d##*|})${NC}"
done
echo -e "${YELLOW}🚧 Continuar? (s/n):${NC}"
read -p "> " GO
[[ "$GO" != "s" ]] && { echo -e "${RED}Cancelado.${NC}"; exit 0; }

# Función para propagar en paralelo
propagate_destination() {
    local dest=$1
    local repo_url=${dest%%|*}
    local branch_dest=${dest##*|}
    local tmp_current="${TMP_DIR}_$(basename "$repo_url" .git)_$branch_dest"
    local result_file="/tmp/propagate_result_$(basename "$repo_url" .git)_$branch_dest"
    
    if sparse_clone "$repo_url" "$branch_dest" "$tmp_current" 2>/dev/null; then
        pushd "$tmp_current" > /dev/null
            git tag rollback_before_update 2>/dev/null || true
            local changes=false
            for file in "${ALL_FILES[@]}"; do
                if [ -f "$TMP_ORIGIN/$file" ]; then
                    mkdir -p "$(dirname "$file")"
                    cp "$TMP_ORIGIN/$file" "$file"
                    changes=true
                fi
            done
            
            if $changes; then
                git config user.name  "AutoUpdater"
                git config user.email "autoupdater@lacompaniadigital.com"
                git add .
                if ! git diff --cached --quiet; then
                    local msg="UPDATE [$(IFS=,; echo "${MODULES[*]}")] from $ORIGIN_BRANCH"
                    git commit -m "$msg"
                    if git push origin "$branch_dest" 2>/dev/null; then
                        echo "SUCCESS|$repo_url|$branch_dest|$msg" > "$result_file"
                    else
                        echo "PUSH_ERROR|$repo_url|$branch_dest" > "$result_file"
                    fi
                else
                    echo "NO_CHANGES|$repo_url|$branch_dest" > "$result_file"
                fi
            else
                echo "NO_FILES|$repo_url|$branch_dest" > "$result_file"
            fi
        popd > /dev/null
    else
        echo "CLONE_ERROR|$repo_url|$branch_dest" > "$result_file"
    fi
    
    rm -rf "$tmp_current"
}

# 11️⃣ Propagar en paralelo
echo -e "\n${PURPLE}🔄 Iniciando propagación en paralelo...${NC}"

# Limpiar archivos de resultados anteriores
rm -f /tmp/propagate_result_*

# Ejecutar propagaciones en paralelo
pids=()
for dest in "${OUTDATED[@]}"; do
    propagate_destination "$dest" &
    pids+=($!)
done

# Mostrar progreso
completed=0
echo -e "\n${YELLOW}Propagando cambios...${NC}"
while [ $completed -lt ${#OUTDATED[@]} ]; do
    completed=0
    for pid in "${pids[@]}"; do
        if ! kill -0 $pid 2>/dev/null; then
            ((completed++))
        fi
    done
    show_progress $completed ${#OUTDATED[@]}
    sleep 0.5
done
echo # Nueva línea después de la barra de progreso

# Esperar a que terminen todos los procesos
wait

# Recopilar resultados de propagación
SUCCESSFUL=0
FAILED=0

for dest in "${OUTDATED[@]}"; do
    repo_url=${dest%%|*}
    branch_dest=${dest##*|}
    result_file="/tmp/propagate_result_$(basename "$repo_url" .git)_$branch_dest"
    
    if [ -f "$result_file" ]; then
        result=$(cat "$result_file")
        status=${result%%|*}
        
        case $status in
            "SUCCESS")
                ((SUCCESSFUL++))
                msg=${result##*|}
                log_message "SUCCESS" "Propagado: $repo_url ($branch_dest) - $msg"
                ;;
            "NO_CHANGES"|"NO_FILES")
                ((SUCCESSFUL++))
                log_message "INFO" "Sin cambios: $repo_url ($branch_dest)"
                ;;
            *)
                ((FAILED++))
                log_message "ERROR" "Falló: $repo_url ($branch_dest) - $status"
                ;;
        esac
    else
        ((FAILED++))
        log_message "ERROR" "Sin resultado: $repo_url ($branch_dest)"
    fi
done

# Limpiar archivos temporales de resultados
rm -f /tmp/propagate_result_*

# 12️⃣ Resumen final y limpieza
echo -e "\n${PURPLE}📊 Resumen de propagación:${NC}"
echo -e "${GREEN}  ✓ Exitosos: $SUCCESSFUL${NC}"
echo -e "${RED}  ✗ Fallidos: $FAILED${NC}"

duration=$(( $(date +%s) - start_time ))
echo -e "\n${GREEN}🎉 ¡Propagación completada en ${duration} segundos!${NC}"
echo "🕓 Duración total: $duration seg" >> "$LOG_FILE"

rm -rf "$TMP_ORIGIN" "$ROOT_DIR/tmp_dest_repo_"*