#!/usr/bin/env bash
# WHP Shell Script Application Script
# Applies shell scripts between two versions following the same pattern as SQL migrations

# Exit on any error
set -e

# Colors for output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m' # No Color

# Check arguments
if [ $# -ne 3 ]; then
    echo "Usage: $0 <current_version> <new_version> <scripts_dir>"
    echo "Example: $0 2025.01.1 2025.01.2 /root/whp/scripts/updates"
    exit 1
fi

# Sanitize and validate input parameters
CURRENT_VERSION="$1"
NEW_VERSION="$2"
SCRIPTS_DIR="$3"

# Validate version format (YYYY.MM.N)
if [[ ! "$CURRENT_VERSION" =~ ^[0-9]{4}\.[0-9]{2}\.[0-9]+$ ]]; then
    echo -e "${RED}Error: Invalid current version format: $CURRENT_VERSION${NC}"
    echo "Expected format: YYYY.MM.N (e.g., 2025.08.1)"
    exit 1
fi

if [[ ! "$NEW_VERSION" =~ ^[0-9]{4}\.[0-9]{2}\.[0-9]+$ ]]; then
    echo -e "${RED}Error: Invalid new version format: $NEW_VERSION${NC}"
    echo "Expected format: YYYY.MM.N (e.g., 2025.08.1)"
    exit 1
fi

# Sanitize scripts directory path and validate
SCRIPTS_DIR=$(readlink -f "$SCRIPTS_DIR" 2>/dev/null)
if [ $? -ne 0 ] || [ -z "$SCRIPTS_DIR" ]; then
    echo -e "${RED}Error: Invalid scripts directory path${NC}"
    exit 1
fi

if [ ! -d "$SCRIPTS_DIR" ]; then
    echo -e "${RED}Error: Scripts directory not found: $SCRIPTS_DIR${NC}"
    exit 1
fi

# Ensure scripts directory is in expected location
if [[ "$SCRIPTS_DIR" != *"/scripts/updates" ]]; then
    echo -e "${RED}Error: Scripts directory must be under /scripts/updates${NC}"
    exit 1
fi

# Function to compare versions (YYYY.MM.N format)
version_compare() {
    # Convert version to comparable number: YYYYMMN
    local v1=$(echo "$1" | sed 's/\.//g')
    local v2=$(echo "$2" | sed 's/\.//g')
    
    if [ "$v1" -lt "$v2" ]; then
        echo "-1"
    elif [ "$v1" -gt "$v2" ]; then
        echo "1"
    else
        echo "0"
    fi
}

# Function to validate script security
validate_script() {
    local script_file="$1"
    
    # Validate script path to prevent directory traversal
    local canonical_path=$(readlink -f "$script_file")
    local expected_dir=$(readlink -f "$SCRIPTS_DIR")
    if [[ "$canonical_path" != "$expected_dir"/* ]]; then
        echo -e "${RED}Error: Script path outside expected directory${NC}"
        return 1
    fi
    
    # Check if file exists and is readable
    if [[ ! -f "$script_file" ]] || [[ ! -r "$script_file" ]]; then
        echo -e "${RED}Error: Script file not found or not readable${NC}"
        return 1
    fi
    
    # Check shebang (more strict validation)
    local first_line=$(head -n1 "$script_file" 2>/dev/null)
    if [[ ! "$first_line" =~ ^#!/usr/bin/env[[:space:]]+bash$ ]]; then
        echo -e "${RED}Error: Script must start with '#!/usr/bin/env bash'${NC}"
        return 1
    fi
    
    # Enhanced dangerous command detection (whitelist approach for critical paths)
    local dangerous_patterns=(
        "rm[[:space:]]+-[^[:space:]]*r[^[:space:]]*[[:space:]]+/"
        "format[[:space:]]"
        "fdisk[[:space:]]"
        "mkfs\."
        "dd[[:space:]].*if=.*of=/dev/"
        ">[[:space:]]*/dev/(sd[a-z]|hd[a-z]|nvme)"
        "chmod[[:space:]].*777"
        "chown[[:space:]].*root:"
        "su[[:space:]]+-"
        "sudo[[:space:]]+-[istu]"
        "\$\(.*\`.*\`.*\)"
        "eval[[:space:]].*\$"
    )
    
    for pattern in "${dangerous_patterns[@]}"; do
        if grep -E "$pattern" "$script_file" > /dev/null 2>&1; then
            echo -e "${RED}Error: Script contains potentially dangerous command pattern: $pattern${NC}"
            return 1
        fi
    done
    
    # Check file permissions (more comprehensive check)
    if [[ ! -O "$script_file" ]]; then
        echo -e "${RED}Error: Script not owned by current user${NC}"
        return 1
    fi
    
    local perms=$(stat -c "%a" "$script_file" 2>/dev/null)
    if [[ "$perms" =~ [0-9][0-9][2367] ]]; then
        echo -e "${RED}Error: Script is world-writable (permissions: $perms)${NC}"
        return 1
    fi
    
    # Check for suspicious file size (empty or extremely large)
    local file_size=$(stat -c "%s" "$script_file" 2>/dev/null)
    if [[ "$file_size" -eq 0 ]] || [[ "$file_size" -gt 1048576 ]]; then  # 1MB limit
        echo -e "${RED}Error: Script file size suspicious (size: $file_size bytes)${NC}"
        return 1
    fi
    
    return 0
}

echo -e "${BLUE}Applying update scripts from $CURRENT_VERSION to $NEW_VERSION${NC}"

# Get list of all version directories
cd "$SCRIPTS_DIR"
VERSIONS=($(ls -d */ 2>/dev/null | sed 's/\///' | sort -V))

if [ ${#VERSIONS[@]} -eq 0 ]; then
    echo "No script versions found"
    exit 0
fi

# Track if any scripts were applied
SCRIPTS_APPLIED=0

# Apply scripts for each version between current and new
for VERSION in "${VERSIONS[@]}"; do
    # Check if this version is greater than current and less than or equal to new
    COMPARE_CURRENT=$(version_compare "$VERSION" "$CURRENT_VERSION")
    COMPARE_NEW=$(version_compare "$VERSION" "$NEW_VERSION")
    
    if [ "$COMPARE_CURRENT" = "1" ] && [ "$COMPARE_NEW" -le "0" ]; then
        
        echo ""
        echo -e "${BLUE}Processing update scripts for version $VERSION...${NC}"
        
        # Get all shell script files in this version directory
        SCRIPT_FILES=($(find "$VERSION" -name "*.sh" -type f | sort))
        
        if [ ${#SCRIPT_FILES[@]} -eq 0 ]; then
            echo "  No update scripts found for version $VERSION"
            continue
        fi
        
        for SCRIPT_FILE in "${SCRIPT_FILES[@]}"; do
            echo -n "  Validating $(basename "$SCRIPT_FILE")... "
            
            # Validate script security first
            if validate_script "$SCRIPT_FILE"; then
                echo -e "${GREEN}✓${NC}"
            else
                echo -e "${RED}✗${NC}"
                echo -e "${RED}Script validation failed: $SCRIPT_FILE${NC}"
                exit 1
            fi
            
            # Make script executable if not already
            chmod +x "$SCRIPT_FILE"
            
            echo -n "  Executing $(basename "$SCRIPT_FILE")... "
            
            # Create sanitized environment
            script_env=(
                "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
                "HOME=/root"
                "SHELL=/bin/bash"
                "USER=root"
                "LOGNAME=root"
            )
            
            # Execute with timeout (5 minutes default)
            if timeout 300 env -i "${script_env[@]}" bash "./$SCRIPT_FILE" 2>/tmp/script_error.log; then
                echo -e "${GREEN}✓${NC}"
                SCRIPTS_APPLIED=$((SCRIPTS_APPLIED + 1))
            else
                exit_code=$?
                echo -e "${RED}✗${NC}"
                
                if [ $exit_code -eq 124 ]; then
                    echo -e "${RED}Error: Script execution timed out (5 minutes): $SCRIPT_FILE${NC}"
                else
                    echo -e "${RED}Error executing script: $SCRIPT_FILE (exit code: $exit_code)${NC}"
                fi
                
                # Show sanitized error details
                if [ -f /tmp/script_error.log ]; then
                    echo "Error details (first 10 lines):"
                    head -10 /tmp/script_error.log | sed 's/[^[:print:]\t]//g'
                    rm -f /tmp/script_error.log
                fi
                
                exit 1
            fi
        done
    fi
done

# Clean up
rm -f /tmp/script_error.log

# Summary
echo ""
if [ $SCRIPTS_APPLIED -eq 0 ]; then
    echo -e "${YELLOW}No update scripts needed${NC}"
else
    echo -e "${GREEN}Successfully executed $SCRIPTS_APPLIED update script(s)${NC}"
fi