#!/bin/bash

# Скрипт для редактирования docker-compose.yml, полного бэкапа и обновления remnanode
set -e
set -o pipefail

# ========== НАСТРОЙКИ ==========
SERVICE_DIR="/opt"
DOCKER_COMPOSE_FILE="$SERVICE_DIR/docker-compose.yml"
BACKUP_BASE_DIR="/opt/remnanode/backups"
LOG_DIR="/opt/log"
LOG_FILE="$LOG_DIR/remnanode_update.log"
TIMESTAMP=$(date +%Y%m%d_%H%M%S)
BACKUP_DIR="$BACKUP_BASE_DIR/backup_$TIMESTAMP"

# Определяем какую команду docker compose использовать
if command -v docker &> /dev/null && docker compose version &> /dev/null 2>&1; then
    DOCKER_COMPOSE="docker compose"
elif command -v docker-compose &> /dev/null; then
    DOCKER_COMPOSE="docker-compose"
else
    echo "ERROR: Docker Compose не установлен!"
    exit 1
fi

# Цвета для вывода в консоль
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m'

# ========== ФУНКЦИИ ДЛЯ ЛОГИРОВАНИЯ ==========
mkdir -p "$LOG_DIR"
mkdir -p "$BACKUP_BASE_DIR"

log_info() {
    local msg="[INFO] $(date '+%Y-%m-%d %H:%M:%S') - $1"
    echo -e "${GREEN}$msg${NC}" | tee -a "$LOG_FILE"
}

log_error() {
    local msg="[ERROR] $(date '+%Y-%m-%d %H:%M:%S') - $1"
    echo -e "${RED}$msg${NC}" | tee -a "$LOG_FILE"
}

log_warn() {
    local msg="[WARN] $(date '+%Y-%m-%d %H:%M:%S') - $1"
    echo -e "${YELLOW}$msg${NC}" | tee -a "$LOG_FILE"
}

log_step() {
    local msg="[STEP] $(date '+%Y-%m-%d %H:%M:%S') - $1"
    echo -e "${BLUE}$msg${NC}" | tee -a "$LOG_FILE"
}

# ========== ФУНКЦИЯ ДЛЯ РЕДАКТИРОВАНИЯ DOCKER-COMPOSE.YML ==========
edit_docker_compose() {
    log_step "Редактирование файла $DOCKER_COMPOSE_FILE"
    
    # Проверяем, есть ли уже cap_add в секции remnanode
    if grep -A 30 "^  remnanode:" "$DOCKER_COMPOSE_FILE" | grep -q "cap_add:"; then
        log_warn "В секции remnanode уже есть cap_add, проверяем наличие NET_ADMIN..."
        
        if grep -A 30 "^  remnanode:" "$DOCKER_COMPOSE_FILE" | grep -q "NET_ADMIN"; then
            log_info "NET_ADMIN уже добавлен в cap_add, пропускаем"
            return 0
        else
            log_info "Добавляем NET_ADMIN в существующий cap_add"
            sed -i '/^  remnanode:/,/^  [a-z]/ {
                /cap_add:/ {
                    n
                    s/^\(.*\)- .*$/\1- NET_ADMIN\n\1- \2/
                }
            }' "$DOCKER_COMPOSE_FILE"
        fi
    else
        log_info "Добавляем секцию cap_add с NET_ADMIN"
        
        awk '
        /^  remnanode:/ { found=1; print; next }
        found && /^    volumes:/ {
            print "    cap_add:";
            print "      - NET_ADMIN";
            found=0;
            print;
            next
        }
        found && !/^    / {
            print "    cap_add:";
            print "      - NET_ADMIN";
            found=0;
            print;
            next
        }
        { print }
        ' "$DOCKER_COMPOSE_FILE" > "$DOCKER_COMPOSE_FILE.tmp"
        
        mv "$DOCKER_COMPOSE_FILE.tmp" "$DOCKER_COMPOSE_FILE"
    fi
    
    if grep -A 30 "^  remnanode:" "$DOCKER_COMPOSE_FILE" | grep -q "NET_ADMIN"; then
        log_info "✅ NET_ADMIN успешно добавлен в секцию remnanode"
        return 0
    else
        log_error "❌ Не удалось добавить NET_ADMIN"
        return 1
    fi
}

# ========== ФУНКЦИИ БЭКАПА ==========
create_full_backup() {
    log_step "Начало создания полного бэкапа в $BACKUP_DIR"
    
    mkdir -p "$BACKUP_DIR"
    
    # 1. Сохраняем docker-compose.yml
    log_info "Сохранение docker-compose.yml..."
    if [ -f "$DOCKER_COMPOSE_FILE" ]; then
        cp "$DOCKER_COMPOSE_FILE" "$BACKUP_DIR/"
        log_info "docker-compose.yml сохранён"
    else
        log_error "Файл docker-compose.yml не найден!"
        exit 1
    fi
    
    # 2. Сохраняем .env файлы если есть
    log_info "Сохранение .env файлов..."
    cp "$SERVICE_DIR"/.env* "$BACKUP_DIR/" 2>/dev/null && log_info ".env файлы сохранены" || log_warn ".env файлы не найдены"
    
    # 3. Сохраняем текущие образы Docker (ИСПРАВЛЕНО)
    log_info "Сохранение Docker образов..."
    cd "$SERVICE_DIR" || exit 1
    
    # Получаем список сервисов
    SERVICES=$($DOCKER_COMPOSE config --services 2>/dev/null || echo "")
    
    if [ -n "$SERVICES" ]; then
        echo "$SERVICES" | while read -r service; do
            if [ -n "$service" ]; then
                # Получаем имя образа с тегом
                IMAGE_NAME=$($DOCKER_COMPOSE images "$service" --format "{{.Repository}}:{{.Tag}}" 2>/dev/null | head -1)
                if [ -n "$IMAGE_NAME" ] && [ "$IMAGE_NAME" != ":" ]; then
                    log_info "Сохранение образа для сервиса: $service -> $IMAGE_NAME"
                    docker save "$IMAGE_NAME" -o "$BACKUP_DIR/${service}.tar" 2>&1 | tee -a "$LOG_FILE"
                    log_info "Образ $service сохранён: $BACKUP_DIR/${service}.tar"
                else
                    log_warn "Не удалось получить имя образа для сервиса $service"
                    # Пробуем получить через ID
                    IMAGE_ID=$($DOCKER_COMPOSE images -q "$service" 2>/dev/null || echo "")
                    if [ -n "$IMAGE_ID" ]; then
                        IMAGE_NAME=$(docker image inspect "$IMAGE_ID" --format '{{index .RepoTags 0}}' 2>/dev/null || echo "")
                        if [ -n "$IMAGE_NAME" ] && [ "$IMAGE_NAME" != "<none>:<none>" ]; then
                            log_info "Найден образ по ID: $IMAGE_NAME"
                            docker save "$IMAGE_NAME" -o "$BACKUP_DIR/${service}.tar" 2>&1 | tee -a "$LOG_FILE"
                        else
                            log_warn "Не удалось определить имя образа для ID: $IMAGE_ID, сохраняем по ID"
                            docker save "$IMAGE_ID" -o "$BACKUP_DIR/${service}_${IMAGE_ID}.tar" 2>&1 | tee -a "$LOG_FILE"
                        fi
                    fi
                fi
            fi
        done
    else
        log_warn "Не удалось получить список сервисов"
    fi
    
    # 4. Сохраняем информацию о текущих контейнерах
    log_info "Сохранение информации о контейнерах..."
    cd "$SERVICE_DIR" || exit 1
    $DOCKER_COMPOSE ps -a > "$BACKUP_DIR/containers_state.txt" 2>/dev/null || true
    
    # 5. Бэкап Docker volumes (только существующие)
    log_info "Бэкап Docker volumes..."
    VOLUMES_BACKUP_DIR="$BACKUP_DIR/volumes"
    mkdir -p "$VOLUMES_BACKUP_DIR"
    
    docker volume ls --format "{{.Name}}" | grep -E "(remnanode|remnawave|3xui)" | while read -r volume; do
        if [ -n "$volume" ]; then
            log_info "Бэкап volume: $volume"
            docker run --rm -v "$volume":/data -v "$VOLUMES_BACKUP_DIR":/backup alpine \
                tar czf "/backup/volume_backup_${volume}.tar.gz" -C /data . 2>/dev/null && \
                log_info "Volume $volume сохранён" || \
                log_warn "Не удалось сохранить volume $volume (возможно пустой)"
        fi
    done
    
    # 6. Сохраняем версии образов
    log_info "Сохранение версий образов..."
    docker images --format "table {{.Repository}}\t{{.Tag}}\t{{.ID}}\t{{.CreatedAt}}" > "$BACKUP_DIR/images_list.txt"
    
    # 7. Создаём скрипт для восстановления
    log_info "Создание скрипта восстановления..."
    cat > "$BACKUP_DIR/restore.sh" << 'RESTORE_SCRIPT'
#!/bin/bash

BACKUP_DIR="$(cd "$(dirname "$0")" && pwd)"
SERVICE_DIR="/opt"

if command -v docker &> /dev/null && docker compose version &> /dev/null 2>&1; then
    DOCKER_COMPOSE="docker compose"
elif command -v docker-compose &> /dev/null; then
    DOCKER_COMPOSE="docker-compose"
else
    echo "ERROR: Docker Compose не установлен!"
    exit 1
fi

echo "=== Восстановление remnanode из бэкапа $BACKUP_DIR ==="

cd "$SERVICE_DIR" || exit 1

echo "Остановка текущих контейнеров..."
$DOCKER_COMPOSE down 2>/dev/null || true

echo "Восстановление docker-compose.yml..."
cp "$BACKUP_DIR/docker-compose.yml" .

echo "Загрузка Docker образов..."
for tar_file in "$BACKUP_DIR"/*.tar; do
    if [ -f "$tar_file" ]; then
        echo "Загрузка: $tar_file"
        docker load -i "$tar_file" 2>&1
    fi
done

if [ -d "$BACKUP_DIR/volumes" ] && [ "$(ls -A $BACKUP_DIR/volumes)" ]; then
    echo "Восстановление Docker volumes..."
    for volume_backup in "$BACKUP_DIR"/volumes/volume_backup_*.tar.gz; do
        if [ -f "$volume_backup" ]; then
            volume_name=$(basename "$volume_backup" | sed 's/volume_backup_//' | sed 's/.tar.gz//')
            echo "Восстановление volume: $volume_name"
            docker volume rm "$volume_name" 2>/dev/null || true
            docker volume create "$volume_name"
            docker run --rm -v "$volume_name":/data -v "$BACKUP_DIR/volumes":/backup alpine \
                tar xzf "/backup/$(basename "$volume_backup")" -C /data 2>&1
        fi
    done
fi

echo "Запуск контейнеров..."
$DOCKER_COMPOSE up -d

echo "Статус контейнеров:"
sleep 3
$DOCKER_COMPOSE ps

echo "=== Восстановление завершено ==="
RESTORE_SCRIPT
    
    chmod +x "$BACKUP_DIR/restore.sh"
    log_info "Скрипт восстановления создан: $BACKUP_DIR/restore.sh"
    
    BACKUP_SIZE=$(du -sh "$BACKUP_DIR" | cut -f1)
    log_info "Размер бэкапа: $BACKUP_SIZE"
    log_info "Полный бэкап завершён успешно"
}

# ========== ФУНКЦИИ ОБНОВЛЕНИЯ ==========
update_service() {
    log_step "Начало обновления сервиса"
    
    cd "$SERVICE_DIR" || exit 1
    
    log_info "Выполнение $DOCKER_COMPOSE pull..."
    if $DOCKER_COMPOSE pull; then
        log_info "Новые образы успешно скачаны"
    else
        log_error "Ошибка при выполнении $DOCKER_COMPOSE pull"
        return 1
    fi
    
    log_info "Выполнение $DOCKER_COMPOSE down..."
    if $DOCKER_COMPOSE down; then
        log_info "Контейнеры остановлены"
    else
        log_error "Ошибка при остановке контейнеров"
        return 1
    fi
    
    log_info "Выполнение $DOCKER_COMPOSE up -d..."
    if $DOCKER_COMPOSE up -d; then
        log_info "Новые контейнеры запущены"
    else
        log_error "Ошибка при запуске контейнеров"
        return 1
    fi
    
    log_info "Проверка статуса контейнеров..."
    sleep 5
    $DOCKER_COMPOSE ps | tee -a "$LOG_FILE"
    
    if $DOCKER_COMPOSE ps | grep -q "Up"; then
        log_info "✅ Контейнеры успешно запущены"
        return 0
    else
        log_error "❌ Контейнеры не запустились!"
        $DOCKER_COMPOSE logs --tail=50 | tee -a "$LOG_FILE"
        return 1
    fi
}

# ========== ФУНКЦИЯ ЗАПИСИ КОМАНД ВОССТАНОВЛЕНИЯ ==========
write_recovery_commands() {
    echo "" | tee -a "$LOG_FILE"
    echo "=========================================" | tee -a "$LOG_FILE"
    echo "=== КОМАНДЫ ДЛЯ ВОССТАНОВЛЕНИЯ ИЗ БЭКАПА ===" | tee -a "$LOG_FILE"
    echo "=========================================" | tee -a "$LOG_FILE"
    echo "" | tee -a "$LOG_FILE"
    echo "Быстрое восстановление одной командой:" | tee -a "$LOG_FILE"
    echo "bash /opt/remnanode/backups/backup_$TIMESTAMP/restore.sh" | tee -a "$LOG_FILE"
    echo "" | tee -a "$LOG_FILE"
    echo "Или вручную:" | tee -a "$LOG_FILE"
    echo "cd /opt && $DOCKER_COMPOSE down" | tee -a "$LOG_FILE"
    echo "cp /opt/remnanode/backups/backup_$TIMESTAMP/docker-compose.yml /opt/" | tee -a "$LOG_FILE"
    echo "for tar in /opt/remnanode/backups/backup_$TIMESTAMP/*.tar; do docker load -i \$tar; done" | tee -a "$LOG_FILE"
    echo "cd /opt && $DOCKER_COMPOSE up -d" | tee -a "$LOG_FILE"
    echo "=========================================" | tee -a "$LOG_FILE"
}

# ========== ОСНОВНАЯ ФУНКЦИЯ ==========
main() {
    > "$LOG_FILE"
    
    log_info "========================================="
    log_info "Запуск скрипта бэкапа и обновления remnanode"
    log_info "Используется команда: $DOCKER_COMPOSE"
    log_info "========================================="
    
    if [ "$EUID" -ne 0 ]; then 
        log_warn "Скрипт запущен не от root. Некоторые операции могут требовать sudo"
    fi
    
    if [ ! -f "$DOCKER_COMPOSE_FILE" ]; then
        log_error "Файл $DOCKER_COMPOSE_FILE не найден!"
        exit 1
    fi
    
    if ! command -v docker &> /dev/null; then
        log_error "Docker не установлен!"
        exit 1
    fi
    
    # ШАГ 1: Редактируем docker-compose.yml
    log_step "ШАГ 1: Редактирование docker-compose.yml"
    if ! edit_docker_compose; then
        log_error "Не удалось отредактировать docker-compose.yml"
        exit 1
    fi
    
    # ШАГ 2: Создаём полный бэкап
    log_step "ШАГ 2: Создание полного бэкапа"
    if create_full_backup; then
        log_info "Бэкап успешно создан в: $BACKUP_DIR"
    else
        log_error "Не удалось создать бэкап!"
        exit 1
    fi
    
    # ШАГ 3: Выполняем обновление
    log_step "ШАГ 3: Обновление сервиса"
    if update_service; then
        log_info "✅ Сервис успешно обновлён!"
    else
        log_error "❌ Ошибка при обновлении сервиса!"
        log_error "Для восстановления используйте: bash $BACKUP_DIR/restore.sh"
        write_recovery_commands
        exit 1
    fi
    
    write_recovery_commands
    
    log_info "========================================="
    log_info "✅ Скрипт успешно завершён!"
    log_info "📁 Лог сохранён в: $LOG_FILE"
    log_info "💾 Бэкап сохранён в: $BACKUP_DIR"
    log_info "🔄 Для восстановления: bash $BACKUP_DIR/restore.sh"
    log_info "========================================="
    
    echo ""
    echo "=== ИНФОРМАЦИЯ О БЭКАПЕ ==="
    echo "Директория бэкапа: $BACKUP_DIR"
    echo "Размер бэкапа: $(du -sh "$BACKUP_DIR" | cut -f1)"
    echo "Скрипт восстановления: $BACKUP_DIR/restore.sh"
    echo ""
    echo "=== ИЗМЕНЕНИЯ В DOCKER-COMPOSE.YML ==="
    echo "Добавлено: cap_add: - NET_ADMIN в секцию remnanode"
    echo ""
    echo "=== ПРОСМОТР ЛОГА ==="
    echo "cat $LOG_FILE"
}

# ========== ЗАПУСК ==========
trap 'log_error "Скрипт прерван пользователем"; exit 1' INT TERM

main
exit 0
