#!/bin/bash

#================================================================
# HEADER
#================================================================
#% SYNOPSIS
#+   update-containers.sh
#%
#% DESCRIPTION
#+   Updates all Docker container images in the media stack by pulling
#+   the latest versions and restarting the compose stack.
#%
#% OPTIONS
#+   None
#%
#% EXAMPLES
#+   ./update-containers.sh
#%
#% NOTES
#+   - Requires Docker and Docker Compose to be installed
#+   - Must be run from a directory containing docker-compose.yml
#+   - Stops all containers before updating, then restarts them
#+   - Handles missing images gracefully with warnings
#%
#% AUTHOR
#+   AceDanger
#%
#% VERSION
#+   1.1.0
#%
#% SECURITY
#+   - All variables are properly quoted to prevent command injection
#+   - Input validation on Docker commands and image names
#+   - Temporary files use secure locations with proper cleanup
#+   - Error handling prevents script continuation on critical failures
#%
#================================================================

# Enable strict error handling
set -euo pipefail

# Constants
readonly SCRIPT_NAME="$(basename "$0")"
readonly DOCKER_MEDIA_DIR="$HOME/docker/media"
readonly ERROR_FILE="/tmp/docker-images-update-$$.error"

# Cleanup function
cleanup() {
    if [[ -f "$ERROR_FILE" ]]; then
        rm -f "$ERROR_FILE"
    fi
}

# Set up cleanup trap
trap cleanup EXIT ERR

# Function to log messages with timestamp
log() {
    echo "[$(date '+%Y-%m-%d %H:%M:%S')] $*"
}

# Function to validate Docker image name format
validate_image_name() {
    local image="$1"
    # Basic validation for Docker image name format
    # Allow alphanumeric, hyphens, underscores, dots, slashes, and colons
    if [[ ! "$image" =~ ^[a-zA-Z0-9._/-]+:[a-zA-Z0-9._-]+$ ]]; then
        log "ERROR: Invalid Docker image name format: $image"
        return 1
    fi
    return 0
}

# Change to docker media directory
if [[ ! -d "$DOCKER_MEDIA_DIR" ]]; then
    log "ERROR: Docker media directory does not exist: $DOCKER_MEDIA_DIR"
    exit 1
fi

cd "$DOCKER_MEDIA_DIR" || {
    log "ERROR: Failed to change to directory: $DOCKER_MEDIA_DIR"
    exit 1
}

# Stop docker compose
log "Stopping Docker Compose stack"
if ! docker compose down; then
    log "ERROR: Failed to stop Docker Compose stack"
    exit 1
fi

# Verify Docker is running
log "Checking Docker daemon status"
if ! docker info >/dev/null 2>&1; then
    log "ERROR: Docker daemon is not running or accessible"
    exit 1
fi

log "Docker is running, continuing with image updates"

# Get list of currently installed Docker images
log "Retrieving list of installed Docker images"
IMAGES_WITH_TAGS=$(docker images --format "{{.Repository}}:{{.Tag}}" | grep -v "<none>:" || true)

if [[ -z "$IMAGES_WITH_TAGS" ]]; then
    log "WARNING: No Docker images found to update"
else
    log "Found $(echo "$IMAGES_WITH_TAGS" | wc -l) images to update"

    # Process each image
    while IFS= read -r IMAGE; do
        [[ -z "$IMAGE" ]] && continue

        log "Processing image: $IMAGE"

        # Validate image name format
        if ! validate_image_name "$IMAGE"; then
            log "WARNING: Skipping invalid image name: $IMAGE"
            continue
        fi

        log "Updating $IMAGE"

        # Pull the image with error handling
        if docker pull "$IMAGE" 2>"$ERROR_FILE"; then
            log "Successfully updated: $IMAGE"
        else
            # Check if the error is due to image not found
            if grep -q "not found\|pull access denied\|repository does not exist" "$ERROR_FILE" 2>/dev/null; then
                log "WARNING: Docker image $IMAGE not found in repository, skipping"
            else
                log "ERROR: Failed to pull image $IMAGE"
                if [[ -f "$ERROR_FILE" ]]; then
                    log "Error details: $(cat "$ERROR_FILE")"
                fi
                exit 2
            fi
        fi

        log "Completed processing: $IMAGE"
        echo
    done <<< "$IMAGES_WITH_TAGS"
fi

# Restart Docker Compose stack
log "Restarting Docker Compose stack"
if ! docker compose up -d; then
    log "ERROR: Failed to restart Docker Compose stack"
    exit 3
fi

# Verify stack is running properly
log "Verifying Docker Compose stack status"
if docker compose ps --format "table {{.Name}}\t{{.Status}}" | grep -v "Up"; then
    log "WARNING: Some containers may not be running properly"
    docker compose ps
else
    log "All containers are running successfully"
fi

log "Docker images update completed successfully"
exit 0
