#!/bin/bash

#############################
# CONFIG
#############################

# Répertoire contenant tes containers
SOURCE_DIR="/home/docker"

# Répertoire où stocker les sauvegardes
BACKUP_DIR="/home/backup/docker"

# Nombre max de backups à conserver
MAX_BACKUPS=5

# Date format
DATE="$(date +'%Y%m%d_%H%M%S')"

# Fichier TAR final
BACKUP_FILE="$BACKUP_DIR/docker_backup_${DATE}.tar.gz"

# Dossier de configs des compose à côté du TAR
CONFIG_DIR="$BACKUP_DIR/config_${DATE}"

# Dossiers à exclure (chemins relatifs à $SOURCE_DIR)
EXCLUDE_DIRS=(
    "docker_var_lib"
    "casaos_data/*"
    "nginx_proxy/data/logs/*"
    "yt-dl/video/*"
)

# LOGS
LOG_FILE="/var/log/docker_backup.log"

#############################
# COULEURS
#############################
NC="\e[0m"
GREEN="\e[32m"
YELLOW="\e[33m"
CYAN="\e[36m"
RED="\e[31m"

#############################
# FONCTIONS
#############################

log_message() {
    local msg="$1"
    local now
    now="$(date +'%Y-%m-%d %H:%M:%S')"
    echo "$now - $msg" >> "$LOG_FILE"
    echo -e "$now - $msg"
}

format_time() {
  local t=$1
  ((h=t/3600))
  ((m=(t%3600)/60))
  ((s=t%60))
  printf "%02d:%02d:%02d\n" $h $m $s
}

check_pigz() {
    if ! command -v pigz >/dev/null 2>&1; then
        echo -e "${YELLOW}pigz n'est pas installé. Voulez-vous l’installer ? (y/n)${NC}"
        read rep
        if [ "$rep" = "y" ]; then
            sudo apt update && sudo apt install -y pigz || {
                echo -e "${RED}Échec de l’installation de pigz. Abandon.${NC}"
                exit 1
            }
        else
            echo -e "${RED}pigz est requis pour ce script. Abandon.${NC}"
            exit 1
        fi
    fi
}

rotate_backups() {
    log_message "Nettoyage des anciens backups (conserver les $MAX_BACKUPS plus récents)..."

    # TAR
    mapfile -t backups_tar < <(ls -1t "$BACKUP_DIR"/docker_backup_*.tar.gz 2>/dev/null || true)
    if [ "${#backups_tar[@]}" -gt "$MAX_BACKUPS" ]; then
        for f in "${backups_tar[@]:$MAX_BACKUPS}"; do
            log_message "Suppression ancien backup tar : $f"
            rm -f "$f"
        done
    fi

    # Dossiers config_
    mapfile -t backups_cfg < <(ls -1td "$BACKUP_DIR"/config_* 2>/dev/null || true)
    if [ "${#backups_cfg[@]}" -gt "$MAX_BACKUPS" ]; then
        for d in "${backups_cfg[@]:$MAX_BACKUPS}"; do
            log_message "Suppression ancien dossier config : $d"
            rm -rf "$d"
        done
    fi
}

human_date() {
    # Convertit 20251203_172811 → "03/12/2025 17:28:11"
    local d="$1"
    local date_part="${d:0:8}"
    local time_part="${d:9:6}"
    echo "${date_part:6:2}/${date_part:4:2}/${date_part:0:4} ${time_part:0:2}:${time_part:2:2}:${time_part:4:2}"
}

get_size() {
    # Taille lisible
    du -sh "$1" 2>/dev/null | awk '{print $1}'
}

#############################
# DÉBUT SCRIPT
#############################

SECONDS=0

check_pigz

mkdir -p "$BACKUP_DIR" "$CONFIG_DIR"
touch "$LOG_FILE"

echo -e "${CYAN}=== Backup Docker + Configs (Portainer / CasaOS) ===${NC}"
log_message "Début sauvegarde Docker + configs Portainer & CasaOS..."

##############################################
# 1) Sauvegarde TAR via pigz + exclusions
##############################################
log_message "Création de l’archive : $BACKUP_FILE"

# Construction des arguments d'exclusion dynamiques pour le TAR
EXCLUDE_ARGS=(
    "--exclude=*.log"
    "--exclude=.composer"
    "--exclude=.aptitude"
    "--exclude=.cache"
    "--exclude=.cmake"
    "--exclude=.yarn"
    "--exclude=.w3m"
    "--exclude=.pip"
    "--exclude=.pm2"
    "--exclude=.pm"
    "--exclude=.bundle"
    "--exclude=.gem"
    "--exclude=.cpan"
    "--exclude=.cpanm"
    "--exclude=.git"
    "--exclude=.local"
    "--exclude=.npm"
    "--exclude=.nvm"
    "--exclude=.rvm"
    "--exclude=node_modules"
    "--exclude=lost+found"
)

for d in "${EXCLUDE_DIRS[@]}"; do
    EXCLUDE_ARGS+=( "--exclude=$d" )
done

tar -cf "$BACKUP_FILE" -I pigz \
 --directory="$SOURCE_DIR" \
 "${EXCLUDE_ARGS[@]}" \
 . 2>> "$LOG_FILE"

if [ $? -ne 0 ]; then
    log_message "ERREUR lors de la création du tar."
    echo -e "${RED}Erreur lors de la création de l’archive.${NC}"
    exit 1
fi

##############################################
# 2) Extraction des docker-compose
#    - CasaOS:   config_<date>/casaos/<container>/
#    - Docker:   config_<date>/docker/<container>/
##############################################

log_message "Extraction des docker-compose CasaOS..."
if [ -d "$SOURCE_DIR/casaos_data" ]; then
    find "$SOURCE_DIR/casaos_data" -maxdepth 3 -type f -name "docker-compose.y*ml" | while read file; do
        container_name=$(basename "$(dirname "$file")")
        dest_dir="$CONFIG_DIR/casaos/$container_name"
        mkdir -p "$dest_dir"
        cp "$file" "$dest_dir/"
        log_message "  [CasaOS] → Compose trouvé : $file"
    done
fi

log_message "Extraction des docker-compose Docker (hors casaos_data & dossiers exclus)..."
find "$SOURCE_DIR" -mindepth 2 -maxdepth 2 -type f -name "docker-compose.y*ml" \
    ! -path "$SOURCE_DIR/casaos_data/*" \
    ! -path "$SOURCE_DIR/docker_var_lib/*" \
    | while read file; do
        container_name=$(basename "$(dirname "$file")")
        dest_dir="$CONFIG_DIR/docker/$container_name"
        mkdir -p "$dest_dir"
        cp "$file" "$dest_dir/"
        log_message "  [Docker] → Compose trouvé : $file"
    done

##############################################
# 3) Config Portainer (full)
##############################################
PORTAINER_CONFIG_DIR="$CONFIG_DIR/portainer/full_config"
PORTAINER_FILES=0

if docker volume inspect portainer_data >/dev/null 2>&1; then
    log_message "Sauvegarde config Portainer..."
    mkdir -p "$PORTAINER_CONFIG_DIR"
    cp -r /var/lib/docker/volumes/portainer_data/_data/* "$PORTAINER_CONFIG_DIR/" 2>>"$LOG_FILE"
    PORTAINER_FILES=$(find "$PORTAINER_CONFIG_DIR" -type f 2>/dev/null | wc -l)
else
    PORTAINER_CONFIG_DIR=""
fi

##############################################
# 4) Config CasaOS (full)
##############################################
CASAOS_CONFIG_DIR="$CONFIG_DIR/casaos/full_config"
CASAOS_FILES=0

if [ -d "/var/lib/casaos" ]; then
    log_message "Sauvegarde config CasaOS (full)..."
    mkdir -p "$CASAOS_CONFIG_DIR"
    cp -r /var/lib/casaos/* "$CASAOS_CONFIG_DIR/" 2>>"$LOG_FILE"
fi

##############################################
# 5) Comptages finaux (sur le backup courant)
##############################################

# Compose dans le backup (docker + casaos compose seulement)
CASAOS_COMPOSE_BACKUP=$(find "$CONFIG_DIR/casaos" -maxdepth 3 -type f -name "docker-compose.y*ml" ! -path "$CASAOS_CONFIG_DIR/*" 2>/dev/null | wc -l)
DOCKER_COMPOSE_BACKUP=$(find "$CONFIG_DIR/docker" -type f -name "docker-compose.y*ml" 2>/dev/null | wc -l)
TOTAL_COMPOSE_BACKUP=$((CASAOS_COMPOSE_BACKUP + DOCKER_COMPOSE_BACKUP))

# Compose dans le dossier source (uniquement /home/docker/<container>/docker-compose.*)
SOURCE_COMPOSE_COUNT=$(find "$SOURCE_DIR" -mindepth 2 -maxdepth 2 -type f -name "docker-compose.y*ml" \
    ! -path "$SOURCE_DIR/casaos_data/*" \
    ! -path "$SOURCE_DIR/docker_var_lib/*" \
    2>/dev/null | wc -l)

# Containers Docker "valides" (dossiers de premier niveau, hors exclusions évidentes)
CONTAINERS_DOCKER=$(find "$SOURCE_DIR" -mindepth 1 -maxdepth 1 -type d \
    ! -path "$SOURCE_DIR/casaos_data" \
    ! -path "$SOURCE_DIR/docker_var_lib" \
    ! -path "$SOURCE_DIR/portainer" 2>/dev/null | wc -l)

# Rotation des anciens backups
rotate_backups

# Nombre total de tar.gz
TOTAL_TAR=$(ls -1 "$BACKUP_DIR"/docker_backup_*.tar.gz 2>/dev/null | wc -l)

# Oldest / newest après rotation
OLDEST_BACKUP=$(ls -1 "$BACKUP_DIR"/docker_backup_*.tar.gz 2>/dev/null | head -n1)
NEWEST_BACKUP=$(ls -1 "$BACKUP_DIR"/docker_backup_*.tar.gz 2>/dev/null | tail -n1)

DURATION="$(format_time $SECONDS)"

# Tailles des backups
TAR_SIZE=$(get_size "$BACKUP_FILE")
CONFIG_SIZE=$(get_size "$CONFIG_DIR")

# Docker compose directory size
DOCKER_CONFIG_SIZE=$(get_size "$CONFIG_DIR/docker")

# CasaOS full config size
if [ -d "$CASAOS_CONFIG_DIR" ]; then
    CASAOS_CONFIG_SIZE=$(get_size "$CASAOS_CONFIG_DIR")
else
    CASAOS_CONFIG_SIZE=""
fi

# Portainer full config size
if [ -n "$PORTAINER_CONFIG_DIR" ] && [ -d "$PORTAINER_CONFIG_DIR" ]; then
    PORTAINER_CONFIG_SIZE=$(get_size "$PORTAINER_CONFIG_DIR")
else
    PORTAINER_CONFIG_SIZE=""
fi

# Dates humaines
HUMAN_OLDEST=$( [ -n "$OLDEST_BACKUP" ] && human_date "$(basename "$OLDEST_BACKUP" | sed 's/docker_backup_//; s/.tar.gz//')" )
HUMAN_NEWEST=$( [ -n "$NEWEST_BACKUP" ] && human_date "$(basename "$NEWEST_BACKUP" | sed 's/docker_backup_//; s/.tar.gz//')" )

##############################################
# 6) RÉSUMÉS
##############################################

echo -e "${GREEN}===== BACKUP TERMINÉ =====${NC}"

##############################################
# LISTE DES DOSSIERS DOCKER PAR TAILLE (ASCII)
##############################################

echo -e "\n${CYAN}----- LISTE/TAILLE DES RÉPERTOIRES DOCKER -----${NC}"

DOCKER_DIRS=$(find "$SOURCE_DIR" -mindepth 1 -maxdepth 1 -type d \
    ! -path "$SOURCE_DIR/casaos_data" \
    ! -path "$SOURCE_DIR/docker_var_lib" \
    ! -path "$SOURCE_DIR/portainer" 2>/dev/null)

if [ -z "$DOCKER_DIRS" ]; then
    echo -e "${YELLOW}(Aucun dossier Docker trouvé)${NC}"
else
    echo -e "┌──────────────────────────┬─────────┬────────────────────────────────────────────┐"
    printf "│ %-24s │ %-7s │ %-38s │\n" "Container" "Taille" "Chemin"
    echo -e "├──────────────────────────┼─────────┼────────────────────────────────────────────┤"
    du -sh $DOCKER_DIRS 2>/dev/null | sort -hr | while read -r size path; do
        name=$(basename "$path")
        printf "│ %-24s │ %-7s │ %-38s │\n" "$name" "$size" "$path"
    done
    echo -e "└──────────────────────────┴─────────┴────────────────────────────────────────────┘"
fi

#################################
# DOCKER
#################################
echo -e "${CYAN}\n----- DOCKER -----${NC}"
echo -e "${CYAN}Containers Docker détectés   : ${GREEN}$CONTAINERS_DOCKER${NC}"
echo -e "${CYAN}Compose source (Docker)      : ${GREEN}$SOURCE_COMPOSE_COUNT${NC}"
echo -e "${CYAN}Compose backup (Docker)      : ${GREEN}$DOCKER_COMPOSE_BACKUP${NC}"
echo -e "${CYAN}Dossier compose Docker       : ${YELLOW}$CONFIG_DIR/docker${NC}"
echo -e "${CYAN}Taille compose Docker        : ${GREEN}$DOCKER_CONFIG_SIZE${NC}"

#################################
# CASAOS (si présent)
#################################
if [ -d "$CASAOS_CONFIG_DIR" ] || [ "$CASAOS_COMPOSE_BACKUP" -gt 0 ]; then
    echo -e "${CYAN}\n----- CASAOS -----${NC}"
    echo -e "${CYAN}Compose CasaOS backup        : ${GREEN}$CASAOS_COMPOSE_BACKUP${NC}"
    echo -e "${CYAN}Dossier CasaOS backup        : ${YELLOW}$CONFIG_DIR/casaos${NC}"
    [ -n "$CASAOS_CONFIG_SIZE" ] && echo -e "${CYAN}Taille config CasaOS (full)  : ${GREEN}$CASAOS_CONFIG_SIZE${NC}"
fi

#################################
# PORTAINER (si présent)
#################################
if [ -n "$PORTAINER_CONFIG_DIR" ] && [ -d "$PORTAINER_CONFIG_DIR" ]; then
    echo -e "${CYAN}\n----- PORTAINER -----${NC}"
    echo -e "${CYAN}Fichiers config Portainer    : ${GREEN}$PORTAINER_FILES${NC}"
    echo -e "${CYAN}Dossier Portainer backup     : ${YELLOW}$PORTAINER_CONFIG_DIR${NC}"
    [ -n "$PORTAINER_CONFIG_SIZE" ] && echo -e "${CYAN}Taille Portainer (full)      : ${GREEN}$PORTAINER_CONFIG_SIZE${NC}"
fi

#################################
# GLOBAL
#################################
echo -e "${CYAN}\n----- GLOBAL -----${NC}"
echo -e "${CYAN}Taille archive tar.gz        : ${GREEN}$TAR_SIZE${NC}"
echo -e "${CYAN}Taille dossier config        : ${GREEN}$CONFIG_SIZE${NC}"
echo -e "${CYAN}Nombre de .tar.gz            : ${GREEN}$TOTAL_TAR${NC}"
echo -e "${CYAN}Oldest backup                : ${YELLOW}$OLDEST_BACKUP${NC}"
echo -e "${CYAN}Oldest backup (human)        : ${GREEN}${HUMAN_OLDEST:-N/A}${NC}"
echo -e "${CYAN}Newest backup                : ${YELLOW}$NEWEST_BACKUP${NC}"
echo -e "${CYAN}Newest backup (human)        : ${GREEN}${HUMAN_NEWEST:-N/A}${NC}"
echo -e "${CYAN}Archive courante             : ${YELLOW}$BACKUP_FILE${NC}"
echo -e "${CYAN}Dossier config courant       : ${YELLOW}$CONFIG_DIR${NC}"
echo -e "${CYAN}Durée totale                 : ${GREEN}$DURATION${NC}"

#################################
# JSON (optionnel : ./script.sh --json)
#################################
if [ "$1" = "--json" ]; then
    echo ""
    echo "{"
    echo "  \"date\": \"$DATE\","
    echo "  \"backup_file\": \"$BACKUP_FILE\","
    echo "  \"backup_size\": \"$TAR_SIZE\","
    echo "  \"config_dir\": \"$CONFIG_DIR\","
    echo "  \"config_size\": \"$CONFIG_SIZE\","
    echo "  \"docker\": {"
    echo "    \"containers\": $CONTAINERS_DOCKER,"
    echo "    \"compose_source\": $SOURCE_COMPOSE_COUNT,"
    echo "    \"compose_backup\": $DOCKER_COMPOSE_BACKUP"
    echo "  },"
    echo "  \"casaos\": {"
    echo "    \"compose_backup\": $CASAOS_COMPOSE_BACKUP"
    echo "  },"
    echo "  \"portainer\": {"
    echo "    \"files\": $PORTAINER_FILES"
    echo "  },"
    echo "  \"tar_count\": $TOTAL_TAR,"
    echo "  \"oldest_backup\": \"$OLDEST_BACKUP\","
    echo "  \"oldest_backup_human\": \"${HUMAN_OLDEST:-N/A}\","
    echo "  \"newest_backup\": \"$NEWEST_BACKUP\","
    echo "  \"newest_backup_human\": \"${HUMAN_NEWEST:-N/A}\","
    echo "  \"duration\": \"$DURATION\""
    echo "}"
fi
