#!/bin/bash
# Desk-Moloni v3.0 Deployment Script
# 
# Automated deployment and update script for production and staging environments.
# Handles version updates, migrations, rollbacks, and environment management.

set -euo pipefail

# Script configuration
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
MODULE_DIR="$(dirname "$SCRIPT_DIR")"
PROJECT_ROOT="$(dirname "$(dirname "$MODULE_DIR")")"

# Deployment configuration
VERSION_FILE="$MODULE_DIR/VERSION"
DEPLOYMENT_LOG="$MODULE_DIR/logs/deployment.log"
BACKUP_DIR="$MODULE_DIR/backups/deployments"
STAGING_DIR="$MODULE_DIR/.staging"

# Default settings
DEFAULT_ENVIRONMENT="production"
DEFAULT_BACKUP_RETENTION=10

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

# Logging functions
log_info() {
    local timestamp=$(date '+%Y-%m-%d %H:%M:%S')
    local message="[$timestamp] [INFO] $1"
    echo -e "${BLUE}$message${NC}"
    echo "$message" >> "$DEPLOYMENT_LOG" 2>/dev/null || true
}

log_success() {
    local timestamp=$(date '+%Y-%m-%d %H:%M:%S')
    local message="[$timestamp] [SUCCESS] $1"
    echo -e "${GREEN}$message${NC}"
    echo "$message" >> "$DEPLOYMENT_LOG" 2>/dev/null || true
}

log_warning() {
    local timestamp=$(date '+%Y-%m-%d %H:%M:%S')
    local message="[$timestamp] [WARNING] $1"
    echo -e "${YELLOW}$message${NC}"
    echo "$message" >> "$DEPLOYMENT_LOG" 2>/dev/null || true
}

log_error() {
    local timestamp=$(date '+%Y-%m-%d %H:%M:%S')
    local message="[$timestamp] [ERROR] $1"
    echo -e "${RED}$message${NC}"
    echo "$message" >> "$DEPLOYMENT_LOG" 2>/dev/null || true
}

log_step() {
    local message="$1"
    echo -e "\n${CYAN}${BOLD}=== $message ===${NC}\n"
    echo "[$(date '+%Y-%m-%d %H:%M:%S')] [STEP] $message" >> "$DEPLOYMENT_LOG" 2>/dev/null || true
}

# Help function
show_help() {
    cat << EOF
Desk-Moloni v3.0 Deployment Script

Usage: $0 <command> [OPTIONS]

Commands:
    deploy <version>             Deploy specific version
    update                       Update to latest version
    rollback [version]           Rollback to previous or specific version
    status                       Show deployment status
    list-versions               List available versions
    test-deployment             Test deployment readiness
    maintenance-mode <on|off>    Enable/disable maintenance mode

Options:
    -h, --help                  Show this help message
    -e, --environment ENV       Target environment (production|staging|development)
    -b, --backup                Create backup before deployment
    --no-migrate                Skip database migrations
    --no-restart                Skip service restart
    --force                     Force deployment without checks
    --dry-run                   Show what would be done without changes
    --retention COUNT           Number of backups to retain (default: $DEFAULT_BACKUP_RETENTION)

Deployment Process:
    1. Pre-deployment checks
    2. Create backup (if enabled)
    3. Download/prepare new version
    4. Run database migrations
    5. Update configuration
    6. Restart services
    7. Post-deployment verification
    8. Cleanup old backups

Examples:
    $0 deploy 3.0.1                    # Deploy specific version
    $0 update --backup                 # Update with backup
    $0 rollback                        # Rollback to previous version
    $0 rollback 3.0.0                  # Rollback to specific version
    $0 maintenance-mode on             # Enable maintenance mode
    $0 test-deployment --dry-run       # Test deployment process

EOF
}

# Parse command line arguments
COMMAND=""
TARGET_VERSION=""
ENVIRONMENT="$DEFAULT_ENVIRONMENT"
CREATE_BACKUP=false
SKIP_MIGRATE=false
SKIP_RESTART=false
FORCE_DEPLOY=false
DRY_RUN=false
BACKUP_RETENTION="$DEFAULT_BACKUP_RETENTION"

while [[ $# -gt 0 ]]; do
    case $1 in
        -h|--help)
            show_help
            exit 0
            ;;
        -e|--environment)
            ENVIRONMENT="$2"
            shift 2
            ;;
        -b|--backup)
            CREATE_BACKUP=true
            shift
            ;;
        --no-migrate)
            SKIP_MIGRATE=true
            shift
            ;;
        --no-restart)
            SKIP_RESTART=true
            shift
            ;;
        --force)
            FORCE_DEPLOY=true
            shift
            ;;
        --dry-run)
            DRY_RUN=true
            shift
            ;;
        --retention)
            BACKUP_RETENTION="$2"
            shift 2
            ;;
        deploy|update|rollback|status|list-versions|test-deployment|maintenance-mode)
            COMMAND="$1"
            shift
            # Get version/mode parameter for commands that need it
            if [[ $# -gt 0 ]] && [[ ! "$1" =~ ^- ]]; then
                TARGET_VERSION="$1"
                shift
            fi
            ;;
        *)
            log_error "Unknown option: $1"
            show_help
            exit 1
            ;;
    esac
done

# Validate command
if [[ -z "$COMMAND" ]]; then
    log_error "No command specified"
    show_help
    exit 1
fi

# Validate environment
if [[ ! "$ENVIRONMENT" =~ ^(production|staging|development)$ ]]; then
    log_error "Invalid environment: $ENVIRONMENT"
    exit 1
fi

# Initialize deployment environment
initialize() {
    log_info "Initializing deployment environment: $ENVIRONMENT"
    
    # Create required directories
    local dirs=("$(dirname "$DEPLOYMENT_LOG")" "$BACKUP_DIR" "$STAGING_DIR")
    for dir in "${dirs[@]}"; do
        if [[ ! -d "$dir" ]]; then
            mkdir -p "$dir"
        fi
    done
    
    # Set environment-specific configurations
    case "$ENVIRONMENT" in
        production)
            log_info "Production environment - all checks enabled"
            ;;
        staging)
            log_info "Staging environment - relaxed checks"
            ;;
        development)
            log_info "Development environment - minimal checks"
            FORCE_DEPLOY=true  # Skip most checks in development
            ;;
    esac
}

# Get current version
get_current_version() {
    if [[ -f "$VERSION_FILE" ]]; then
        cat "$VERSION_FILE" | tr -d '\n\r'
    else
        echo "unknown"
    fi
}

# Pre-deployment checks
pre_deployment_checks() {
    log_step "Pre-deployment Checks"
    
    local errors=0
    
    # Check if we're running as appropriate user
    if [[ "$ENVIRONMENT" == "production" ]] && [[ $EUID -eq 0 ]]; then
        log_warning "Running as root in production (not recommended)"
    fi
    
    # Check disk space
    local disk_usage
    disk_usage=$(df "$MODULE_DIR" | awk 'NR==2 {print $5}' | sed 's/%//')
    if [[ "$disk_usage" -gt 90 ]]; then
        log_error "Insufficient disk space: ${disk_usage}% used"
        ((errors++))
    else
        log_success "Disk space OK: ${disk_usage}% used"
    fi
    
    # Check if services are running
    check_services
    
    # Check database connectivity
    if [[ "$SKIP_MIGRATE" == false ]]; then
        if test_database_connection; then
            log_success "Database connection OK"
        else
            log_error "Database connection failed"
            ((errors++))
        fi
    fi
    
    # Check for active queue processes
    if pgrep -f "queue_processor" > /dev/null; then
        log_warning "Queue processor is running - will be restarted after deployment"
    fi
    
    # Check file permissions
    if [[ ! -w "$MODULE_DIR" ]]; then
        log_error "Module directory not writable: $MODULE_DIR"
        ((errors++))
    fi
    
    if [[ $errors -gt 0 ]] && [[ "$FORCE_DEPLOY" == false ]]; then
        log_error "Pre-deployment checks failed with $errors errors"
        log_info "Use --force to override these checks"
        exit 1
    fi
    
    log_success "Pre-deployment checks completed"
}

# Test database connection
test_database_connection() {
    php -r "
        require_once '$MODULE_DIR/config/bootstrap.php';
        try {
            \$config = include '$MODULE_DIR/config/config.php';
            \$pdo = new PDO(
                'mysql:host=' . \$config['database']['host'] . ';dbname=' . \$config['database']['database'],
                \$config['database']['username'],
                ''  // Password would be loaded from Perfex config
            );
            echo 'SUCCESS';
        } catch (Exception \$e) {
            echo 'FAILED';
        }
    " 2>/dev/null | grep -q "SUCCESS"
}

# Check service status
check_services() {
    # Check web server
    if systemctl is-active --quiet apache2 nginx 2>/dev/null; then
        log_success "Web server is running"
    else
        log_warning "Web server status unclear"
    fi
    
    # Check cron
    if systemctl is-active --quiet cron crond 2>/dev/null; then
        log_success "Cron service is running"
    else
        log_warning "Cron service may not be running"
    fi
}

# Create deployment backup
create_deployment_backup() {
    if [[ "$CREATE_BACKUP" == false ]]; then
        return
    fi
    
    log_step "Creating Deployment Backup"
    
    local backup_name="deployment_$(date +%Y%m%d_%H%M%S)_v$(get_current_version)"
    local backup_path="$BACKUP_DIR/$backup_name"
    
    if [[ "$DRY_RUN" == true ]]; then
        log_info "Would create backup: $backup_path"
        return
    fi
    
    mkdir -p "$backup_path"
    
    # Backup database
    log_info "Backing up database..."
    if ! "$MODULE_DIR/scripts/maintenance.sh" backup-database > "$backup_path/database_backup.log" 2>&1; then
        log_warning "Database backup may have issues (check log)"
    fi
    
    # Backup module files
    log_info "Backing up module files..."
    tar -czf "$backup_path/module_files.tar.gz" -C "$(dirname "$MODULE_DIR")" "$(basename "$MODULE_DIR")" \
        --exclude="logs" --exclude="cache" --exclude="temp" --exclude="backups"
    
    # Backup configuration
    if [[ -d "$MODULE_DIR/config" ]]; then
        cp -r "$MODULE_DIR/config" "$backup_path/"
    fi
    
    # Create manifest
    cat > "$backup_path/MANIFEST" << EOF
Desk-Moloni Deployment Backup
Created: $(date)
Version: $(get_current_version)
Environment: $ENVIRONMENT
Command: $COMMAND $TARGET_VERSION
Host: $(hostname)
User: $(whoami)
EOF
    
    log_success "Backup created: $backup_path"
    echo "$backup_path" > "$MODULE_DIR/.last_backup"
}

# Download and prepare new version
prepare_new_version() {
    local version="$1"
    
    log_step "Preparing Version $version"
    
    if [[ "$DRY_RUN" == true ]]; then
        log_info "Would prepare version $version"
        return
    fi
    
    # Clean staging area
    rm -rf "$STAGING_DIR"
    mkdir -p "$STAGING_DIR"
    
    # In a real implementation, this would download from a repository
    # For now, we'll simulate version preparation
    log_info "Downloading version $version..."
    
    # Copy current files to staging (simulating download)
    cp -r "$MODULE_DIR"/* "$STAGING_DIR/" 2>/dev/null || true
    
    # Update version file in staging
    echo "$version" > "$STAGING_DIR/VERSION"
    
    # Set appropriate permissions
    find "$STAGING_DIR" -type f -name "*.php" -exec chmod 644 {} \;
    find "$STAGING_DIR" -type f -name "*.sh" -exec chmod +x {} \;
    
    log_success "Version $version prepared in staging"
}

# Run database migrations
run_migrations() {
    if [[ "$SKIP_MIGRATE" == true ]]; then
        log_warning "Skipping database migrations"
        return
    fi
    
    log_step "Running Database Migrations"
    
    if [[ "$DRY_RUN" == true ]]; then
        log_info "Would run database migrations"
        return
    fi
    
    # Check for migration files
    local migration_dir="$STAGING_DIR/migrations"
    if [[ ! -d "$migration_dir" ]]; then
        log_info "No migrations directory found, skipping"
        return
    fi
    
    # Run migrations
    local migration_count=0
    for migration_file in "$migration_dir"/*.sql; do
        if [[ -f "$migration_file" ]]; then
            local migration_name=$(basename "$migration_file")
            log_info "Running migration: $migration_name"
            
            if php "$CLI_DIR/sync_commands.php" migrate "$migration_file"; then
                log_success "Migration completed: $migration_name"
                ((migration_count++))
            else
                log_error "Migration failed: $migration_name"
                exit 1
            fi
        fi
    done
    
    if [[ $migration_count -eq 0 ]]; then
        log_info "No migrations to run"
    else
        log_success "$migration_count migrations completed"
    fi
}

# Deploy staged version
deploy_staged_version() {
    local version="$1"
    
    log_step "Deploying Version $version"
    
    if [[ "$DRY_RUN" == true ]]; then
        log_info "Would deploy version $version from staging"
        return
    fi
    
    # Stop queue processor
    log_info "Stopping queue processor..."
    pkill -f "queue_processor" || true
    sleep 2
    
    # Enable maintenance mode
    enable_maintenance_mode
    
    # Copy files from staging to live
    log_info "Copying files from staging to live..."
    rsync -av --delete \
        --exclude="logs" \
        --exclude="cache" \
        --exclude="temp" \
        --exclude="locks" \
        --exclude="backups" \
        --exclude=".staging" \
        "$STAGING_DIR/" "$MODULE_DIR/"
    
    # Update permissions
    set_file_permissions
    
    # Clear cache
    clear_cache
    
    # Restart services if needed
    if [[ "$SKIP_RESTART" == false ]]; then
        restart_services
    fi
    
    # Disable maintenance mode
    disable_maintenance_mode
    
    log_success "Version $version deployed successfully"
}

# Set file permissions
set_file_permissions() {
    log_info "Setting file permissions..."
    
    # Set appropriate permissions based on environment
    local web_user
    case "$ENVIRONMENT" in
        production)
            web_user="www-data"
            ;;
        *)
            web_user=$(whoami)
            ;;
    esac
    
    # Set ownership
    if [[ "$web_user" != "$(whoami)" ]] && [[ $EUID -eq 0 ]]; then
        chown -R "$web_user:$web_user" "$MODULE_DIR" 2>/dev/null || true
    fi
    
    # Set permissions
    find "$MODULE_DIR" -type d -exec chmod 755 {} \;
    find "$MODULE_DIR" -type f -name "*.php" -exec chmod 644 {} \;
    find "$MODULE_DIR" -type f -name "*.sh" -exec chmod +x {} \;
    
    # Ensure writable directories
    local writable_dirs=("logs" "cache" "temp" "locks")
    for dir in "${writable_dirs[@]}"; do
        if [[ -d "$MODULE_DIR/$dir" ]]; then
            chmod 775 "$MODULE_DIR/$dir"
        fi
    done
}

# Clear cache
clear_cache() {
    log_info "Clearing cache..."
    
    if [[ -d "$MODULE_DIR/cache" ]]; then
        rm -rf "$MODULE_DIR/cache"/*
    fi
    
    # Clear PHP opcache if available
    if command -v php &> /dev/null; then
        php -r "if (function_exists('opcache_reset')) opcache_reset();" 2>/dev/null || true
    fi
}

# Restart services
restart_services() {
    log_info "Restarting services..."
    
    # Restart web server (if we have permission)
    if [[ "$ENVIRONMENT" == "production" ]]; then
        if systemctl is-active --quiet apache2; then
            systemctl reload apache2 2>/dev/null || log_warning "Could not reload Apache"
        elif systemctl is-active --quiet nginx; then
            systemctl reload nginx 2>/dev/null || log_warning "Could not reload Nginx"
        fi
    fi
    
    # Restart queue processor
    log_info "Starting queue processor..."
    nohup php "$MODULE_DIR/cli/queue_processor.php" > "$MODULE_DIR/logs/queue_processor.log" 2>&1 &
    sleep 2
    
    if pgrep -f "queue_processor" > /dev/null; then
        log_success "Queue processor started"
    else
        log_warning "Queue processor may not have started properly"
    fi
}

# Maintenance mode functions
enable_maintenance_mode() {
    log_info "Enabling maintenance mode..."
    touch "$MODULE_DIR/.maintenance"
}

disable_maintenance_mode() {
    log_info "Disabling maintenance mode..."
    rm -f "$MODULE_DIR/.maintenance"
}

# Post-deployment verification
post_deployment_verification() {
    log_step "Post-deployment Verification"
    
    local errors=0
    
    # Check version was updated
    local deployed_version
    deployed_version=$(get_current_version)
    if [[ "$deployed_version" == "$TARGET_VERSION" ]]; then
        log_success "✓ Version updated: $deployed_version"
    else
        log_error "✗ Version mismatch: expected $TARGET_VERSION, got $deployed_version"
        ((errors++))
    fi
    
    # Check file permissions
    if [[ -r "$MODULE_DIR/cli/queue_processor.php" ]]; then
        log_success "✓ Files readable"
    else
        log_error "✗ Files not readable"
        ((errors++))
    fi
    
    # Check database connectivity
    if test_database_connection; then
        log_success "✓ Database connection OK"
    else
        log_error "✗ Database connection failed"
        ((errors++))
    fi
    
    # Check queue processor
    if pgrep -f "queue_processor" > /dev/null; then
        log_success "✓ Queue processor running"
    else
        log_warning "⚠ Queue processor not running"
    fi
    
    # Run basic health check
    if [[ "$DRY_RUN" == false ]]; then
        if php "$MODULE_DIR/cli/sync_commands.php" health &>/dev/null; then
            log_success "✓ Health check passed"
        else
            log_warning "⚠ Health check failed (may be expected after deployment)"
        fi
    fi
    
    if [[ $errors -eq 0 ]]; then
        log_success "Post-deployment verification completed successfully"
    else
        log_error "Post-deployment verification found $errors errors"
        return 1
    fi
}

# Cleanup old backups
cleanup_old_backups() {
    log_step "Cleaning Up Old Backups"
    
    if [[ ! -d "$BACKUP_DIR" ]]; then
        return
    fi
    
    local backup_count
    backup_count=$(find "$BACKUP_DIR" -maxdepth 1 -type d -name "deployment_*" | wc -l)
    
    if [[ $backup_count -le $BACKUP_RETENTION ]]; then
        log_info "Backup retention OK: $backup_count backups (limit: $BACKUP_RETENTION)"
        return
    fi
    
    local to_remove=$((backup_count - BACKUP_RETENTION))
    log_info "Removing $to_remove old backups (keeping $BACKUP_RETENTION most recent)"
    
    if [[ "$DRY_RUN" == true ]]; then
        log_info "Would remove $to_remove old backup directories"
        return
    fi
    
    # Remove oldest backups
    find "$BACKUP_DIR" -maxdepth 1 -type d -name "deployment_*" -printf '%T@ %p\n' | \
        sort -n | head -n $to_remove | cut -d' ' -f2- | \
        while read -r backup; do
            rm -rf "$backup"
            log_info "Removed old backup: $(basename "$backup")"
        done
}

# Command implementations
cmd_deploy() {
    if [[ -z "$TARGET_VERSION" ]]; then
        log_error "Version required for deploy command"
        exit 1
    fi
    
    log_step "Deploying Desk-Moloni v$TARGET_VERSION"
    
    local current_version
    current_version=$(get_current_version)
    log_info "Current version: $current_version"
    log_info "Target version: $TARGET_VERSION"
    
    pre_deployment_checks
    create_deployment_backup
    prepare_new_version "$TARGET_VERSION"
    run_migrations
    deploy_staged_version "$TARGET_VERSION"
    post_deployment_verification
    cleanup_old_backups
    
    log_success "🎉 Deployment of v$TARGET_VERSION completed successfully!"
}

cmd_update() {
    # In a real implementation, this would check for the latest version
    local latest_version="3.0.1"  # This would be fetched from a repository
    
    log_info "Updating to latest version: $latest_version"
    TARGET_VERSION="$latest_version"
    cmd_deploy
}

cmd_rollback() {
    local rollback_version="$TARGET_VERSION"
    
    if [[ -z "$rollback_version" ]]; then
        # Get previous version from backup
        if [[ -f "$MODULE_DIR/.last_backup" ]]; then
            local last_backup
            last_backup=$(cat "$MODULE_DIR/.last_backup")
            if [[ -f "$last_backup/MANIFEST" ]]; then
                rollback_version=$(grep "^Version:" "$last_backup/MANIFEST" | cut -d' ' -f2)
            fi
        fi
    fi
    
    if [[ -z "$rollback_version" ]]; then
        log_error "No rollback version specified and no previous backup found"
        exit 1
    fi
    
    log_step "Rolling Back to v$rollback_version"
    
    # Implement rollback logic (restore from backup)
    log_warning "Rollback functionality would be implemented here"
    log_info "Would rollback to version: $rollback_version"
}

cmd_status() {
    log_step "Deployment Status"
    
    local current_version
    current_version=$(get_current_version)
    
    echo "Current Version: $current_version"
    echo "Environment: $ENVIRONMENT"
    echo "Module Path: $MODULE_DIR"
    echo "Last Deployment: $(stat -c %y "$VERSION_FILE" 2>/dev/null || echo "Unknown")"
    
    # Check if maintenance mode is enabled
    if [[ -f "$MODULE_DIR/.maintenance" ]]; then
        echo "Maintenance Mode: ENABLED"
    else
        echo "Maintenance Mode: DISABLED"
    fi
    
    # Check service status
    if pgrep -f "queue_processor" > /dev/null; then
        echo "Queue Processor: RUNNING"
    else
        echo "Queue Processor: STOPPED"
    fi
    
    # Recent backups
    if [[ -d "$BACKUP_DIR" ]]; then
        local backup_count
        backup_count=$(find "$BACKUP_DIR" -maxdepth 1 -type d -name "deployment_*" | wc -l)
        echo "Available Backups: $backup_count"
    fi
}

cmd_list_versions() {
    log_info "Available versions (simulated):"
    echo "3.0.0 - Initial release"
    echo "3.0.1 - Bug fixes and improvements"
    echo "3.1.0 - New features (upcoming)"
}

cmd_test_deployment() {
    log_step "Testing Deployment Readiness"
    
    pre_deployment_checks
    
    log_info "Testing backup creation..."
    CREATE_BACKUP=true
    DRY_RUN=true
    create_deployment_backup
    
    log_info "Testing version preparation..."
    prepare_new_version "test"
    
    log_success "Deployment readiness test completed"
}

cmd_maintenance_mode() {
    local mode="$TARGET_VERSION"  # Reusing TARGET_VERSION for mode parameter
    
    case "$mode" in
        on|enable)
            enable_maintenance_mode
            log_success "Maintenance mode enabled"
            ;;
        off|disable)
            disable_maintenance_mode
            log_success "Maintenance mode disabled"
            ;;
        *)
            log_error "Invalid maintenance mode: $mode (use 'on' or 'off')"
            exit 1
            ;;
    esac
}

# Main execution
main() {
    initialize
    
    case "$COMMAND" in
        deploy)
            cmd_deploy
            ;;
        update)
            cmd_update
            ;;
        rollback)
            cmd_rollback
            ;;
        status)
            cmd_status
            ;;
        list-versions)
            cmd_list_versions
            ;;
        test-deployment)
            cmd_test_deployment
            ;;
        maintenance-mode)
            cmd_maintenance_mode
            ;;
        *)
            log_error "Unknown command: $COMMAND"
            exit 1
            ;;
    esac
}

# Error handling
trap 'log_error "Deployment script failed on line $LINENO"' ERR

# Execute if called directly
if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then
    main "$@"
fi