#!/bin/bash

# Constants
COMMAND="$1"
IMAGE_NAME="$2"
IMAGE_TAG="$3"

REGISTRY_URL="nexus.saymon.info"
TARGET_USER="saymon"
ROOT_DIR=$(pwd) 
LOG_FILE="${ROOT_DIR}/spm.log"
# REGISTRY_AUTH should be set as env

CURRENT_USER=$(whoami)

if [ -z "$IMAGE_TAG" ]; then
    IMAGE_TAG='latest'
fi

# Get actual user (even when running with sudo)
get_actual_user() {
    if [ -n "$SUDO_USER" ]; then
        CURRENT_USER="$SUDO_USER"
    fi
}

# Cache sudo credentials at script start
cache_sudo_credentials() {
    # Check if we already have sudo access
    if sudo -n true 2>/dev/null; then
        log "Sudo credentials already available"
        return 0
    fi

    # Prompt for sudo password
    read -s -p "Sudo password required: " sudo_pass
    echo
    
    # Verify password
    if ! echo "$sudo_pass" | sudo -S true 2>/dev/null; then
        log "Error: Incorrect sudo password"
        exit 1
    fi
    
    export SUDO_PASS="$sudo_pass"
    log "Sudo credentials cached for session"
}

# Secure sudo wrapper
safe_sudo() {
    if [ -z "$SUDO_PASS" ]; then
        sudo "$@"
    else
        echo "$SUDO_PASS" | sudo -S "$@"
    fi
}


# Function to check and authenticate with Docker Registry
docker_registry_auth() {    
    # Check if we're already authenticated
    if grep -q "$REGISTRY_URL" ~/.docker/config.json 2>/dev/null; then
        echo "Already authenticated with registry: $REGISTRY_URL"
        return 0
    fi

    # Try to get credentials from environment
    if [ -z "$REGISTRY_AUTH" ]; then
        echo "Error: Not authenticated with registry $REGISTRY_URL and REGISTRY_AUTH not set"
        return 1
    fi

    # Extract username and password
    local auth_user=$(echo "$REGISTRY_AUTH" | cut -d':' -f1)
    local auth_pass=$(echo "$REGISTRY_AUTH" | cut -d':' -f2-)

    if [ -z "$auth_user" ] || [ -z "$auth_pass" ]; then
        echo "Error: Invalid REGISTRY_AUTH format. Expected user:pass"
        return 1
    fi

    echo "Authenticating with registry: $REGISTRY_URL"
    if ! echo "$auth_pass" | docker login "$REGISTRY_URL" --username "$auth_user" --password-stdin; then
        echo "Error: Docker login failed for registry $REGISTRY_URL"
        return 1
    fi

    echo "Successfully authenticated with registry: $REGISTRY_URL"
    return 0
}

# Log file initialization
init_log_file() {    
    # Create directory if not exists
    if [ ! -d "$ROOT_DIR" ]; then
        if ! safe_sudo -n mkdir -p "$ROOT_DIR" 2>/dev/null; then
            echo "Error: Failed to create directory $ROOT_DIR"
            exit 1
        fi
    fi
    
    # Create log file if not exists
    if [ ! -f "$LOG_FILE" ]; then
        if ! safe_sudo -n touch "$LOG_FILE" 2>/dev/null; then
            echo "Error: Failed to create log file $LOG_FILE"
            exit 1
        fi
        safe_sudo -n chmod 0666 "$LOG_FILE" 2>/dev/null
    fi
    
    # Check write permissions
    if [ ! -w "$LOG_FILE" ]; then
        echo "Error: No write permissions for log file $LOG_FILE"
        exit 1
    fi
}

# Check for required dependencies
check_dependencies() {
    local missing=()
    
    if ! command -v docker &>/dev/null; then
        missing+=("docker")
    fi
    
    if ! command -v jq &>/dev/null; then
        missing+=("jq")
    fi
    
    if [ ${#missing[@]} -gt 0 ]; then
        log "Error: Missing required dependencies: ${missing[*]}"
        exit 1
    fi
}

# Check if script is run as root
check_root() {
    if [ "$(id -u)" -eq 0 ]; then
        log "Error: Script must not be run as root"
        exit 1
    fi
}

# Verify required constants are set
validate_constants() {
    if [ -z "$REGISTRY_URL" ] || [ -z "$TARGET_USER" ] || [ -z "$ROOT_DIR" ]; then
        log "Error: Constants REGISTRY_URL, TARGET_USER or ROOT_DIR are not set"
        exit 1
    fi
}

# Log messages with timestamp and user
log() {
    local message="$1"
    # Write to stdout without timestamp
    echo "${message}"
    # Write to log file with timestamp and user
    echo "$(date '+%Y-%m-%d %H:%M:%S') - ${CURRENT_USER} - ${message}" >> "$LOG_FILE"
}

# Pull Docker image from registry
pull_image() {
    local image="$1"
    log "Pulling image ${image}"
    if ! docker pull "${image}"; then
        log "Error: Failed to pull image ${image}"
        exit 1
    fi
}

# Get image labels and validate required extension label
validate_image() {
    local image="$1"
    local pattern="^io\.saymon\.(app|ext)\..+\.version$"
    
    # Get labels or empty array if none found
    local labels_json=$(docker inspect --format '{{json .Config.Labels}}' "${image}" 2>/dev/null | \
        jq --arg pattern "$pattern" '
            if . == null then [] else
                to_entries | 
                map(select(.key | test($pattern))) | 
                map({
                    full_label: .key,
                    type: (.key | split(".")[2]),
                    name: (.key | split(".")[3]),
                    version: .value
                })
            end
        ' 2>/dev/null)
    
    # Ensure valid JSON output even on errors
    if [ -z "$labels_json" ] || ! jq -e . >/dev/null 2>&1 <<<"$labels_json"; then
        echo "[]"
        return 1
    fi
    
    echo "$labels_json"
    return 0
}

# Create backup of existing directory
create_backup() {
    local source_dir="$1"
    local backup_dir="$2"
    
    # Remove existing backup directory if present
    if [ -d "$backup_dir" ]; then
        log "Removing existing backup directory: ${backup_dir}"
        if ! safe_sudo rm -rf "$backup_dir"; then
            log "Error: Failed to remove old backup directory"
            exit 1
        fi
    fi
    
    # Create new backup if source exists
    if [ -d "$source_dir" ]; then
        log "Creating backup of ${source_dir} to ${backup_dir}"
        if ! safe_sudo cp -rp "$source_dir" "$backup_dir"; then
            log "Error: Failed to create backup (copy failed)"
            exit 1
        fi
    else
        log "Source directory ${source_dir} does not exist, no backup needed"
    fi
}

# Remove existing directory
clean_target() {
    local target_dir="$1"
    log "Removing directory ${target_dir}"
    if ! safe_sudo -n rm -rf "$target_dir" 2>/dev/null; then
        log "Error: Failed to remove directory ${target_dir}"
        exit 1
    fi
}

# Copy files from Docker image to target directory
copy_from_image() {
    local image="$1"
    local image_dir="$2"
    local host_dir="$3"
    
    log "Copying from image ${image} dir ${image_dir} to ${host_dir}"
    local container_id=$(docker create "${image}")
    
    safe_sudo -n mkdir -p "$host_dir"

    if ! safe_sudo -n docker cp "${container_id}:${image_dir}" "$host_dir"; then
        safe_sudo docker rm -f "$container_id" >/dev/null
        log "Error: Failed to copy files from docker image"
        exit 1
    fi
    
    docker rm -f "$container_id" >/dev/null
}

# Set proper permissions on target directory
set_permissions() {
    local target_dir="$1"
    local user="$2"
    
    log "Setting permissions for ${target_dir} to user ${user}"
    safe_sudo -n chown -R "${user}:${user}" "$target_dir"
    safe_sudo -n chmod -R u+rwX "$target_dir"
}

install_app() {
    local type="$1"
    local name="$2"
    local version="$3"
    log "Install $type $name version $version"

    local image_dir="/${type}/${name}"
    local app_dir="${ROOT_DIR}/${type}/${name}"
    local backup_dir="${ROOT_DIR}/${type}/.${name}"

    create_backup "$app_dir" "$backup_dir"
    clean_target "$app_dir"
    copy_from_image "$full_image_name" "$image_dir" "${ROOT_DIR}/${type}"
    set_permissions "$app_dir" "$TARGET_USER"
    log "Installation of ${type} ${name} completed successfully"
}

# Main installation function
install_from_image() {
    local full_image_name="${REGISTRY_URL}/${IMAGE_NAME}:${IMAGE_TAG}"

    log "~~~~ >-}-}-}-o> ~~~~ Install image ${full_image_name}"

    # Step 1: Pull image
    pull_image "$full_image_name"

    docker inspect --format '{{range $k, $v := .Config.Labels}}{{$k}}={{$v}}{{"\n"}}{{end}}' "${full_image_name}"

    # Step 2: Validate image and get extension name
    labels_json=$(validate_image "$full_image_name") || {
        log "Error: Failed to inspect image or no labels found" >&2
        exit 1
    }

    # Process output
    if [ "$(echo "$labels_json" | jq length)" -eq 0 ]; then
        log "No matching labels found"
        exit 1
    fi

    echo "$labels_json" | jq -c '.[]' | while read -r label; do
        local type=$(echo "$label" | jq -r '.type')
        local name=$(echo "$label" | jq -r '.name')
        local version=$(echo "$label" | jq -r '.version')
        install_app "$type" "$name" "$version"    
    done
}

# Initial checks
cache_sudo_credentials
get_actual_user
init_log_file
check_dependencies
check_root

### Script execution starts here ###

if ! docker_registry_auth; then
    exit 1
fi

case "$COMMAND" in
    tags)
        skopeo list-tags "docker://${URL_REGISTRY}/${IMAGE_NAME}"
        ;;
    install)
        install_from_image
        ;;
    *)
        log "Usage: spm.sh install|tags image-name [tag]"
        exit 1
        ;;
esac
