#!/bin/bash
# PHP Analytics Library
# Analyzes real usage data to make intelligent optimization decisions
# Parses logs, process memory, and builds accurate domain profiles

# ============================================================================
# ERROR LOG ANALYSIS - Find memory-related issues
# ============================================================================

# Parse PHP-FPM error logs for memory exhaustion errors
analyze_memory_errors_from_logs() {
    local username="$1"
    local domain="$2"
    local days="${3:-7}"

    local log_files
    log_files=$(find_php_error_logs "$username" "$domain")

    local memory_exhausted_count=0
    local memory_limit_errors=0
    local peak_memory_seen=0

    # Look for memory exhaustion patterns
    while IFS= read -r log_file; do
        [ -z "$log_file" ] && continue
        [ ! -f "$log_file" ] && continue

        # Count "Allowed memory size exhausted" errors
        local exhausted_in_file
        exhausted_in_file=$(\grep -c "Allowed memory size of" "$log_file" 2>/dev/null || echo 0)
        exhausted_in_file=${exhausted_in_file##[[:space:]]}
        exhausted_in_file=${exhausted_in_file%%[[:space:]]}
        memory_exhausted_count=$((memory_exhausted_count + exhausted_in_file))

        # Count memory limit exceeded
        local limit_errors_in_file
        limit_errors_in_file=$(\grep -c "memory_limit" "$log_file" 2>/dev/null || echo 0)
        limit_errors_in_file=${limit_errors_in_file##[[:space:]]}
        limit_errors_in_file=${limit_errors_in_file%%[[:space:]]}
        memory_limit_errors=$((memory_limit_errors + limit_errors_in_file))

        # Extract peak memory from logs (format: "Allowed memory size of 134217728 bytes exhausted")
        local mem_values
        mem_values=$(\grep -o "Allowed memory size of [0-9]* bytes" "$log_file" 2>/dev/null | \grep -o "[0-9]*" | sort -rn | head -1)

        if [ -n "$mem_values" ]; then
            # Convert bytes to MB
            local mem_mb=$((mem_values / 1048576))
            if [ "$mem_mb" -gt "$peak_memory_seen" ]; then
                peak_memory_seen=$mem_mb
            fi
        fi
    done <<< "$log_files"

    # Return: exhausted_count|limit_errors|peak_memory_mb
    echo "$memory_exhausted_count|$memory_limit_errors|$peak_memory_seen"
}

# Find PHP error log files for a domain
find_php_error_logs() {
    local username="$1"
    local domain="$2"

    # cPanel locations
    if [ -d "/home/$username" ]; then
        find "/home/$username" -name "error_log" 2>/dev/null | head -5
    fi

    # PHP-FPM error logs
    if [ -d "/var/log/php-fpm" ]; then
        find "/var/log/php-fpm" -name "*error*" 2>/dev/null | head -5
    fi

    # Common log locations
    [ -f "/var/log/php.log" ] && echo "/var/log/php.log"
    [ -f "/var/log/php-errors.log" ] && echo "/var/log/php-errors.log"
}

# ============================================================================
# PROCESS MEMORY ANALYSIS - Measure actual memory usage
# ============================================================================

# Analyze PHP process memory for a domain
analyze_process_memory_usage() {
    local username="$1"

    # Get current running PHP processes for this user
    local processes
    processes=$(ps aux | \grep -E "php-fpm.*$username|_www.*php" | \grep -v grep)

    if [ -z "$processes" ]; then
        echo "0|0|0|0"  # min|max|avg|count
        return
    fi

    local mem_values=()
    local min_mem=999999
    local max_mem=0
    local total_mem=0
    local count=0

    # Extract memory (RSS) from ps output
    while IFS= read -r line; do
        local rss=$(echo "$line" | awk '{print $6}')
        if [ -n "$rss" ] && [[ "$rss" =~ ^[0-9]+$ ]]; then
            mem_values+=("$rss")
            total_mem=$((total_mem + rss))
            count=$((count + 1))

            if [ "$rss" -lt "$min_mem" ]; then
                min_mem=$rss
            fi
            if [ "$rss" -gt "$max_mem" ]; then
                max_mem=$rss
            fi
        fi
    done <<< "$processes"

    if [ "$count" -eq 0 ]; then
        echo "0|0|0|0"
        return
    fi

    local avg_mem=$((total_mem / count))

    # Convert to MB
    min_mem=$((min_mem / 1024))
    max_mem=$((max_mem / 1024))
    avg_mem=$((avg_mem / 1024))

    # Return: min_mb|max_mb|avg_mb|count
    echo "$min_mem|$max_mem|$avg_mem|$count"
}

# ============================================================================
# TRAFFIC PATTERN ANALYSIS - Understand domain load
# ============================================================================

# Get peak concurrent requests from access logs
get_peak_concurrent_detailed() {
    local username="$1"
    local domain="$2"

    local log_file
    log_file=$(find_domain_access_log "$domain" "$username")

    if [ -z "$log_file" ] || [ ! -f "$log_file" ]; then
        echo "0|0|0"  # peak|avg|stddev
        return
    fi

    # Analyze timestamps to find peak concurrency
    local timestamps
    timestamps=$(awk '{print $4}' "$log_file" 2>/dev/null | sed 's/\[//;s/\/.*//' | sort | uniq -c | sort -rn | head -1)

    local peak_concurrent=$(echo "$timestamps" | awk '{print $1}')
    peak_concurrent=${peak_concurrent:-0}

    # Calculate average concurrent
    local total_hits=$(wc -l < "$log_file")
    local unique_seconds=$(awk '{print $4}' "$log_file" 2>/dev/null | sed 's/\[//;s/\/.*//' | sort -u | wc -l)
    local avg_concurrent=0

    if [ "$unique_seconds" -gt 0 ]; then
        avg_concurrent=$((total_hits / unique_seconds))
    fi

    # Return: peak|avg|total_hits
    echo "$peak_concurrent|$avg_concurrent|$total_hits"
}

# ============================================================================
# MEMORY GROWTH DETECTION - Find memory leaks
# ============================================================================

# Detect if domain has memory leak pattern
detect_memory_leak_pattern() {
    local username="$1"
    local domain="$2"

    # Check error logs for progressive memory growth
    local error_analysis
    error_analysis=$(analyze_memory_errors_from_logs "$username" "$domain")

    local memory_exhausted_count=$(echo "$error_analysis" | cut -d'|' -f1)
    local peak_memory=$(echo "$error_analysis" | cut -d'|' -f3)

    # If many memory exhausted errors with growing peak memory, likely a leak
    if [ "$memory_exhausted_count" -gt 5 ] && [ "$peak_memory" -gt 200 ]; then
        echo "LIKELY_LEAK|High memory exhaustion errors ($memory_exhausted_count) detected"
        return 0
    fi

    # Check if max_requests is 0 (process never recycled)
    local pool_config
    pool_config=$(find_fpm_pool_config "$username")

    if [ -n "$pool_config" ] && [ -f "$pool_config" ]; then
        local max_requests
        max_requests=$(\grep "^pm.max_requests" "$pool_config" | awk -F'=' '{print $2}' | tr -d ' ')

        if [ "$max_requests" = "0" ]; then
            echo "NEEDS_RECYCLING|pm.max_requests is disabled (0) - processes never recycled"
            return 0
        fi
    fi

    echo "NO_LEAK|Normal memory patterns"
    return 1
}

# ============================================================================
# DOMAIN PROFILE BUILDER - Comprehensive analysis
# ============================================================================

# Build complete profile for a domain
build_domain_profile() {
    local username="$1"
    local domain="$2"

    # Get memory errors
    local memory_errors
    memory_errors=$(analyze_memory_errors_from_logs "$username" "$domain")
    local mem_exhausted=$(echo "$memory_errors" | cut -d'|' -f1)
    local mem_limit_errors=$(echo "$memory_errors" | cut -d'|' -f2)
    local peak_mem_seen=$(echo "$memory_errors" | cut -d'|' -f3)

    # Get current process memory
    local process_mem
    process_mem=$(analyze_process_memory_usage "$username")
    local min_mem=$(echo "$process_mem" | cut -d'|' -f1)
    local max_mem=$(echo "$process_mem" | cut -d'|' -f2)
    local avg_mem=$(echo "$process_mem" | cut -d'|' -f3)
    local proc_count=$(echo "$process_mem" | cut -d'|' -f4)

    # Get traffic patterns
    local traffic
    traffic=$(get_peak_concurrent_detailed "$username" "$domain")
    local peak_concurrent=$(echo "$traffic" | cut -d'|' -f1)
    local avg_concurrent=$(echo "$traffic" | cut -d'|' -f2)
    local total_hits=$(echo "$traffic" | cut -d'|' -f3)

    # Detect memory leaks
    local leak_status
    leak_status=$(detect_memory_leak_pattern "$username" "$domain")
    local leak_type=$(echo "$leak_status" | cut -d'|' -f1)
    local leak_note=$(echo "$leak_status" | cut -d'|' -f2)

    # Get current settings
    local current_memory_limit
    current_memory_limit=$(get_effective_php_setting "$username" "memory_limit")
    local pool_config
    pool_config=$(find_fpm_pool_config "$username")
    local current_max_children="?"
    if [ -n "$pool_config" ] && [ -f "$pool_config" ]; then
        current_max_children=$(\grep "^pm.max_children" "$pool_config" | awk -F'=' '{print $2}' | tr -d ' ')
    fi

    # Format: domain|username|peak_concurrent|avg_concurrent|total_hits|min_mem|max_mem|avg_mem|proc_count|mem_exhausted|peak_mem_seen|leak_type|current_memory_limit|current_max_children
    echo "$domain|$username|$peak_concurrent|$avg_concurrent|$total_hits|$min_mem|$max_mem|$avg_mem|$proc_count|$mem_exhausted|$peak_mem_seen|$leak_type|$current_memory_limit|$current_max_children"
}

# ============================================================================
# INTELLIGENT RECOMMENDATIONS - Based on real data
# ============================================================================

# Calculate memory_limit based on ACTUAL usage, not thresholds
calculate_memory_limit_from_actual_usage() {
    local username="$1"
    local domain="$2"

    # Get real data
    local memory_errors
    memory_errors=$(analyze_memory_errors_from_logs "$username" "$domain")
    local peak_mem_seen=$(echo "$memory_errors" | cut -d'|' -f3)

    local process_mem
    process_mem=$(analyze_process_memory_usage "$username")
    local max_mem=$(echo "$process_mem" | cut -d'|' -f2)

    # Determine optimal memory_limit
    local recommended_memory=128

    # If we've seen memory exhaustion, use observed peak + 20% buffer
    if [ "$peak_mem_seen" -gt 0 ]; then
        recommended_memory=$((peak_mem_seen + (peak_mem_seen / 5)))
    elif [ "$max_mem" -gt 0 ]; then
        # Use max observed process memory + 30% buffer for growth
        recommended_memory=$((max_mem + (max_mem / 3)))
    fi

    # Ensure minimum of 64M and maximum of 1024M
    [ "$recommended_memory" -lt 64 ] && recommended_memory=64
    [ "$recommended_memory" -gt 1024 ] && recommended_memory=1024

    echo "${recommended_memory}M"
}

# Calculate max_children based on ACTUAL peak concurrent
calculate_max_children_from_actual_usage() {
    local username="$1"
    local domain="$2"

    # Get real peak concurrent from logs
    local traffic
    traffic=$(get_peak_concurrent_detailed "$username" "$domain")
    local peak_concurrent=$(echo "$traffic" | cut -d'|' -f1)

    # Add 30% safety margin for traffic spikes
    local recommended_max_children=$((peak_concurrent + (peak_concurrent / 3)))

    # Minimum of 5, maximum of 100
    [ "$recommended_max_children" -lt 5 ] && recommended_max_children=5
    [ "$recommended_max_children" -gt 100 ] && recommended_max_children=100

    echo "$recommended_max_children"
}

# Calculate max_requests based on memory leak patterns
calculate_max_requests_from_actual_usage() {
    local username="$1"
    local domain="$2"

    # Default: recycle every 500 requests
    local recommended_requests=500

    # Check if memory leak detected
    local leak_status
    leak_status=$(detect_memory_leak_pattern "$username" "$domain")
    local leak_type=$(echo "$leak_status" | cut -d'|' -f1)

    # If leak detected, recycle more frequently
    if [ "$leak_type" = "LIKELY_LEAK" ]; then
        recommended_requests=250  # Recycle more often
    fi

    echo "$recommended_requests"
}

# ============================================================================
# PROFILE STORAGE AND RETRIEVAL
# ============================================================================

# Store domain profile to file
store_domain_profile() {
    local profile="$1"
    local profile_dir="/tmp/php-domain-profiles"

    mkdir -p "$profile_dir" 2>/dev/null

    local domain=$(echo "$profile" | cut -d'|' -f1)
    echo "$profile" > "$profile_dir/$domain.profile"
}

# Retrieve stored profile
get_stored_profile() {
    local domain="$1"
    local profile_dir="/tmp/php-domain-profiles"

    [ -f "$profile_dir/$domain.profile" ] && cat "$profile_dir/$domain.profile"
}

# Get all stored profiles
get_all_stored_profiles() {
    local profile_dir="/tmp/php-domain-profiles"

    [ -d "$profile_dir" ] && cat "$profile_dir"/*.profile 2>/dev/null
}

# Clear old profiles (older than 24 hours)
cleanup_old_profiles() {
    local profile_dir="/tmp/php-domain-profiles"

    [ ! -d "$profile_dir" ] && return

    find "$profile_dir" -name "*.profile" -mtime +0 -delete 2>/dev/null
}

export -f analyze_memory_errors_from_logs
export -f analyze_process_memory_usage
export -f get_peak_concurrent_detailed
export -f detect_memory_leak_pattern
export -f build_domain_profile
export -f calculate_memory_limit_from_actual_usage
export -f calculate_max_children_from_actual_usage
export -f calculate_max_requests_from_actual_usage
export -f store_domain_profile
export -f get_stored_profile
export -f get_all_stored_profiles
export -f cleanup_old_profiles
