#!/bin/bash
# PHP-FPM Action Executor Module
# Handles optimization application, change tracking, and rollback
# Part of PHP Optimizer - Phase 3 Refactoring

# ============================================================================
# CHANGE TRACKING
# ============================================================================

# Initialize change tracking for a session
init_change_tracking() {
    local session_id="${1:-$(date +%s)}"
    local tracking_dir="/var/log/php-optimizer/changes"

    mkdir -p "$tracking_dir" 2>/dev/null || true
    export EXECUTOR_SESSION_ID="$session_id"
    export EXECUTOR_TRACKING_DIR="$tracking_dir"
    export EXECUTOR_CHANGE_LOG="${tracking_dir}/change-${session_id}.log"

    > "$EXECUTOR_CHANGE_LOG"  # Clear the log file
}

# Log a change for audit trail
log_change() {
    local domain="$1"
    local action="$2"
    local before="$3"
    local after="$4"
    local status="${5:-pending}"

    if [ -z "$EXECUTOR_CHANGE_LOG" ]; then
        init_change_tracking
    fi

    local timestamp
    timestamp=$(date '+%Y-%m-%d %H:%M:%S')

    cat >> "$EXECUTOR_CHANGE_LOG" << EOF
$timestamp|$domain|$action|$status
Before: $before
After: $after
---
EOF
}

# Get change history
get_change_history() {
    local domain="${1:-all}"
    local limit="${2:-50}"

    if [ -z "$EXECUTOR_TRACKING_DIR" ]; then
        return 1
    fi

    if [ "$domain" = "all" ]; then
        tail -n "$limit" "$EXECUTOR_TRACKING_DIR"/change-*.log 2>/dev/null || true
    else
        grep "^[^|]*|$domain|" "$EXECUTOR_TRACKING_DIR"/change-*.log 2>/dev/null | tail -n "$limit" || true
    fi
}

# Get list of all changes from a specific date
get_changes_since() {
    local since_date="$1"
    [ -z "$since_date" ] && return 1

    if [ -z "$EXECUTOR_TRACKING_DIR" ]; then
        return 1
    fi

    find "$EXECUTOR_TRACKING_DIR" -name "change-*.log" -newer /tmp/php-optimizer-since-"$since_date" 2>/dev/null | \
        xargs cat 2>/dev/null || true
}

# ============================================================================
# BACKUP & ROLLBACK
# ============================================================================

# Create backup of a domain's FPM pool config before making changes
backup_domain_config() {
    local domain="$1"
    local username="${2:-}"

    local pool_config
    if [ -n "$username" ]; then
        pool_config=$(find_fpm_pool_config "$username" "$domain" 2>/dev/null)
    else
        pool_config=$(find_fpm_pool_by_domain "$domain" 2>/dev/null)
    fi

    if [ -z "$pool_config" ] || [ ! -f "$pool_config" ]; then
        return 1
    fi

    local backup_dir="/var/lib/php-optimizer/backups"
    mkdir -p "$backup_dir" 2>/dev/null || true

    local backup_file
    backup_file="${backup_dir}/${domain}-$(date +%Y%m%d-%H%M%S).conf"

    cp "$pool_config" "$backup_file" 2>/dev/null || return 1
    echo "$backup_file"
}

# Rollback a domain's config to a specific backup
rollback_domain_config() {
    local domain="$1"
    local backup_file="$2"

    [ -z "$domain" ] || [ -z "$backup_file" ] && return 1
    [ ! -f "$backup_file" ] && return 1

    local pool_config
    pool_config=$(find_fpm_pool_by_domain "$domain" 2>/dev/null)

    if [ -z "$pool_config" ] || [ ! -f "$pool_config" ]; then
        return 1
    fi

    cp "$backup_file" "$pool_config" 2>/dev/null || return 1
    log_change "$domain" "rollback" "current" "restored_from_backup"

    # Reload PHP-FPM
    reload_php_fpm
    return 0
}

# ============================================================================
# CONFIGURATION MODIFICATION
# ============================================================================

# Update a PHP pool configuration parameter
update_pool_parameter() {
    local pool_config="$1"
    local parameter="$2"
    local value="$3"

    [ -z "$pool_config" ] || [ -z "$parameter" ] || [ -z "$value" ] && return 1
    [ ! -f "$pool_config" ] && return 1

    # Check if parameter exists
    if grep -q "^${parameter}\s*=" "$pool_config"; then
        # Update existing parameter
        sed -i.bak "s/^${parameter}\s*=.*/${parameter} = ${value}/" "$pool_config"
    else
        # Add new parameter
        echo "${parameter} = ${value}" >> "$pool_config"
    fi

    return 0
}

# Update multiple pool parameters at once
update_pool_parameters() {
    local pool_config="$1"
    shift  # Remove first argument
    local -a params=("$@")

    [ -f "$pool_config" ] || return 1

    # Create backup before making multiple changes
    local backup_file
    backup_file=$(backup_domain_config "temp" 2>/dev/null) || backup_file="${pool_config}.backup"
    cp "$pool_config" "$backup_file" 2>/dev/null

    local all_success=true
    for param_pair in "${params[@]}"; do
        local param_name param_value
        param_name=$(echo "$param_pair" | cut -d'=' -f1)
        param_value=$(echo "$param_pair" | cut -d'=' -f2)

        if ! update_pool_parameter "$pool_config" "$param_name" "$param_value"; then
            all_success=false
        fi
    done

    if [ "$all_success" = false ]; then
        # Restore backup on failure
        cp "$backup_file" "$pool_config" 2>/dev/null
        return 1
    fi

    return 0
}

# Apply max_children optimization
apply_max_children_optimization() {
    local domain="$1"
    local username="$2"
    local new_max_children="$3"

    local pool_config
    pool_config=$(find_fpm_pool_config "$username" "$domain" 2>/dev/null)

    if [ -z "$pool_config" ] || [ ! -f "$pool_config" ]; then
        return 1
    fi

    # Get current value for logging
    local current_value
    current_value=$(grep "^pm.max_children" "$pool_config" 2>/dev/null | awk -F'=' '{print $2}' | tr -d ' ')
    current_value=${current_value:-unknown}

    # Create backup
    local backup_file
    backup_file=$(backup_domain_config "$domain" "$username")

    # Update the parameter
    if ! update_pool_parameter "$pool_config" "pm.max_children" "$new_max_children"; then
        return 1
    fi

    # Log the change
    log_change "$domain" "max_children" "$current_value" "$new_max_children" "completed"

    return 0
}

# Apply PM mode optimization
apply_pm_mode_optimization() {
    local domain="$1"
    local username="$2"
    local pm_mode="$3"
    local min_spare="${4:-10}"
    local max_spare="${5:-20}"

    local pool_config
    pool_config=$(find_fpm_pool_config "$username" "$domain" 2>/dev/null)

    if [ -z "$pool_config" ] || [ ! -f "$pool_config" ]; then
        return 1
    fi

    # Get current values for logging
    local current_mode current_min current_max
    current_mode=$(grep "^pm\s*=" "$pool_config" 2>/dev/null | awk -F'=' '{print $2}' | tr -d ' ')
    current_min=$(grep "^pm.min_spare_servers" "$pool_config" 2>/dev/null | awk -F'=' '{print $2}' | tr -d ' ')
    current_max=$(grep "^pm.max_spare_servers" "$pool_config" 2>/dev/null | awk -F'=' '{print $2}' | tr -d ' ')

    # Create backup
    local backup_file
    backup_file=$(backup_domain_config "$domain" "$username")

    # Update parameters
    local params=(
        "pm=$pm_mode"
        "pm.min_spare_servers=$min_spare"
        "pm.max_spare_servers=$max_spare"
    )

    if ! update_pool_parameters "$pool_config" "${params[@]}"; then
        return 1
    fi

    # Log the change
    log_change "$domain" "pm_mode" "$current_mode/$current_min/$current_max" "$pm_mode/$min_spare/$max_spare" "completed"

    return 0
}

# ============================================================================
# OPTIMIZATION APPLICATION
# ============================================================================

# Apply optimization to a single domain
apply_optimization() {
    local domain="$1"
    local username="$2"
    local optimization_type="${3:-all}"  # all, max_children, pm_mode, opcache
    local dry_run="${4:-false}"

    if [ "$dry_run" = "true" ]; then
        return 0  # Skip actual changes in dry-run mode
    fi

    case "$optimization_type" in
        max_children)
            apply_max_children_optimization "$domain" "$username" "$5" || return 1
            ;;
        pm_mode)
            apply_pm_mode_optimization "$domain" "$username" "$5" "$6" "$7" || return 1
            ;;
        all)
            # Apply all recommendations
            if [ -n "$5" ]; then
                apply_max_children_optimization "$domain" "$username" "$5" || return 1
            fi
            if [ -n "$6" ]; then
                apply_pm_mode_optimization "$domain" "$username" "$6" "$7" "$8" || return 1
            fi
            ;;
    esac

    return 0
}

# Apply optimizations to multiple domains (batch operation)
apply_batch_optimization() {
    local -a domains=("$@")
    local dry_run="${DRY_RUN:-false}"
    local total_domains=${#domains[@]}
    local current=0
    local successful=0
    local failed=0

    init_change_tracking

    for domain in "${domains[@]}"; do
        [ -z "$domain" ] && continue

        current=$((current + 1))
        show_enumeration_progress "$current" "$total_domains"

        local username
        username=$(find_domain_owner "$domain")

        if [ -z "$username" ]; then
            failed=$((failed + 1))
            log_change "$domain" "batch_optimization" "unknown_user" "skipped" "failed"
            continue
        fi

        # Apply optimization
        if apply_optimization "$domain" "$username" "all" "$dry_run"; then
            successful=$((successful + 1))
            log_change "$domain" "batch_optimization" "started" "completed" "completed"
        else
            failed=$((failed + 1))
            log_change "$domain" "batch_optimization" "attempted" "failed" "failed"
        fi
    done

    echo ""
    return $((failed > 0 ? 1 : 0))
}

# ============================================================================
# VERIFICATION & VALIDATION
# ============================================================================

# Verify that changes were applied correctly
verify_applied_changes() {
    local domain="$1"
    local username="$2"
    local expected_max_children="${3:-}"
    local expected_pm_mode="${4:-}"

    local pool_config
    pool_config=$(find_fpm_pool_config "$username" "$domain" 2>/dev/null)

    if [ -z "$pool_config" ] || [ ! -f "$pool_config" ]; then
        return 1
    fi

    local verify_success=true

    # Verify max_children if expected
    if [ -n "$expected_max_children" ]; then
        local actual_max_children
        actual_max_children=$(grep "^pm.max_children" "$pool_config" | awk -F'=' '{print $2}' | tr -d ' ')

        if [ "$actual_max_children" != "$expected_max_children" ]; then
            verify_success=false
            echo "max_children mismatch: expected $expected_max_children, got $actual_max_children"
        fi
    fi

    # Verify PM mode if expected
    if [ -n "$expected_pm_mode" ]; then
        local actual_pm_mode
        actual_pm_mode=$(grep "^pm\s*=" "$pool_config" | awk -F'=' '{print $2}' | tr -d ' ')

        if [ "$actual_pm_mode" != "$expected_pm_mode" ]; then
            verify_success=false
            echo "pm mode mismatch: expected $expected_pm_mode, got $actual_pm_mode"
        fi
    fi

    if [ "$verify_success" = true ]; then
        return 0
    else
        return 1
    fi
}

# Check if changes are valid (syntax, no conflicts)
validate_pool_config() {
    local pool_config="$1"

    [ ! -f "$pool_config" ] && return 1

    # Basic syntax check
    if grep -q "^[a-z_]*\s*=\s*[^;]*$" "$pool_config"; then
        # Check for common issues
        if grep -q "^pm.max_children\s*=\s*0" "$pool_config"; then
            return 1  # max_children cannot be 0
        fi

        return 0
    fi

    return 1
}

# ============================================================================
# PHP-FPM SERVICE OPERATIONS
# ============================================================================

# Reload PHP-FPM to apply changes
reload_php_fpm() {
    local php_version="${1:-}"

    # Try common PHP-FPM service names
    local service_names=("php-fpm" "php7.4-fpm" "php8.0-fpm" "php8.1-fpm" "php8.2-fpm" "php8.3-fpm")

    if [ -n "$php_version" ]; then
        service_names=("php${php_version}-fpm" "php-fpm")
    fi

    for service in "${service_names[@]}"; do
        if systemctl is-active --quiet "$service" 2>/dev/null; then
            systemctl reload "$service" 2>/dev/null || service "$service" reload 2>/dev/null
            return 0
        fi
    done

    # Fallback: try service command
    service php-fpm reload 2>/dev/null || return 1
}

# Restart PHP-FPM (full restart, not just reload)
restart_php_fpm() {
    local php_version="${1:-}"

    local service_names=("php-fpm" "php7.4-fpm" "php8.0-fpm" "php8.1-fpm" "php8.2-fpm" "php8.3-fpm")

    if [ -n "$php_version" ]; then
        service_names=("php${php_version}-fpm" "php-fpm")
    fi

    for service in "${service_names[@]}"; do
        if systemctl is-active --quiet "$service" 2>/dev/null; then
            systemctl restart "$service" 2>/dev/null || service "$service" restart 2>/dev/null
            return 0
        fi
    done

    return 1
}

# Get PHP-FPM service status
get_php_fpm_status() {
    local service_names=("php-fpm" "php7.4-fpm" "php8.0-fpm" "php8.1-fpm" "php8.2-fpm" "php8.3-fpm")

    for service in "${service_names[@]}"; do
        if systemctl is-active --quiet "$service" 2>/dev/null; then
            systemctl status "$service"
            return 0
        fi
    done

    return 1
}

# ============================================================================
# DRY-RUN MODE (PREVIEW CHANGES)
# ============================================================================

# Preview what changes would be applied (without making them)
preview_changes() {
    local domain="$1"
    local username="$2"
    local -a changes=("${@:3}")

    local pool_config
    pool_config=$(find_fpm_pool_config "$username" "$domain" 2>/dev/null)

    if [ -z "$pool_config" ] || [ ! -f "$pool_config" ]; then
        return 1
    fi

    echo ""
    echo "PREVIEW: Changes that would be applied to $domain:"
    echo ""
    echo "Config file: $pool_config"
    echo ""

    for change in "${changes[@]}"; do
        local param_name param_new_value
        param_name=$(echo "$change" | cut -d'=' -f1)
        param_new_value=$(echo "$change" | cut -d'=' -f2)

        local current_value
        current_value=$(grep "^${param_name}\s*=" "$pool_config" 2>/dev/null | awk -F'=' '{print $2}' | tr -d ' ')

        if [ -z "$current_value" ]; then
            echo "  + $param_name = $param_new_value (NEW)"
        else
            echo "  - $param_name = $current_value"
            echo "  + $param_name = $param_new_value"
        fi
        echo ""
    done

    return 0
}

# ============================================================================
# HELPER FUNCTIONS
# ============================================================================

# Find FPM pool config for a domain
find_fpm_pool_config() {
    local username="$1"
    local domain="$2"

    local pool_config=""

    # Try cPanel paths first (most common)
    # cPanel typically names pools after the domain
    if [ -n "$domain" ]; then
        pool_config=$(find /opt/cpanel/ea-php*/root/etc/php-fpm.d/ -name "$domain.conf" 2>/dev/null | head -1)
        [ -n "$pool_config" ] && { echo "$pool_config"; return 0; }
    fi

    # Try username
    pool_config=$(find /opt/cpanel/ea-php*/root/etc/php-fpm.d/ -name "$username.conf" 2>/dev/null | head -1)
    [ -n "$pool_config" ] && { echo "$pool_config"; return 0; }

    # Try matching any domain under this user
    if [ -n "$domain" ]; then
        pool_config=$(find /opt/cpanel/ea-php*/root/etc/php-fpm.d/ -name "*$domain*" 2>/dev/null | head -1)
        [ -n "$pool_config" ] && { echo "$pool_config"; return 0; }
    fi

    # Try Debian/Ubuntu paths
    local common_paths=(
        "/etc/php-fpm.d/${username}.conf"
        "/etc/php/7.4/fpm/pool.d/${username}.conf"
        "/etc/php/8.0/fpm/pool.d/${username}.conf"
        "/etc/php/8.1/fpm/pool.d/${username}.conf"
        "/etc/php/8.2/fpm/pool.d/${username}.conf"
        "/etc/php/8.3/fpm/pool.d/${username}.conf"
    )

    for path in "${common_paths[@]}"; do
        if [ -f "$path" ]; then
            echo "$path"
            return 0
        fi
    done

    return 1
}

# Find FPM pool config by domain name
find_fpm_pool_by_domain() {
    local domain="$1"

    local owner
    owner=$(find_domain_owner "$domain")

    if [ -n "$owner" ]; then
        find_fpm_pool_config "$owner" "$domain"
    else
        return 1
    fi
}

# ============================================================================
# EXPORT ALL FUNCTIONS
# ============================================================================

export -f init_change_tracking
export -f log_change
export -f get_change_history
export -f get_changes_since
export -f backup_domain_config
export -f rollback_domain_config
export -f update_pool_parameter
export -f update_pool_parameters
export -f apply_max_children_optimization
export -f apply_pm_mode_optimization
export -f apply_optimization
export -f apply_batch_optimization
export -f verify_applied_changes
export -f validate_pool_config
export -f reload_php_fpm
export -f restart_php_fpm
export -f get_php_fpm_status
export -f preview_changes
export -f find_fpm_pool_config
export -f find_fpm_pool_by_domain
