#!/usr/bin/env bash
# Common functions and utilities for Proxmox deployment scripts

# Don't use set -euo pipefail here as this is a library file
# Individual scripts should set this themselves
set +euo pipefail

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

# Logging functions
log_info() {
    echo -e "${BLUE}[INFO]${NC} $1" >&2
}

log_success() {
    echo -e "${GREEN}[✓]${NC} $1" >&2
}

log_warn() {
    echo -e "${YELLOW}[WARNING]${NC} $1" >&2
}

log_error() {
    echo -e "${RED}[ERROR]${NC} $1" >&2
}

log_debug() {
    if [[ "${DEBUG:-}" == "1" ]]; then
        echo -e "${BLUE}[DEBUG]${NC} $1" >&2
    fi
}

# Error handling
error_exit() {
    log_error "$1"
    exit 1
}

# Check if command exists
command_exists() {
    command -v "$1" >/dev/null 2>&1
}

# Check if running as root (for Proxmox host operations)
check_root() {
    if [[ $EUID -ne 0 ]]; then
        error_exit "This script must be run as root for Proxmox host operations"
    fi
}

# Get script directory
get_script_dir() {
    # Find the actual calling script's directory (not this common.sh file)
    local depth=1
    local script_dir
    
    # Walk up the call stack to find the actual script
    while [[ $depth -lt 10 ]]; do
        if [[ -n "${BASH_SOURCE[$depth]:-}" ]]; then
            script_dir="$(cd "$(dirname "${BASH_SOURCE[$depth]}")" && pwd)"
            # If this is not lib/common.sh, use it
            if [[ "$(basename "$script_dir")" != "lib" ]]; then
                echo "$script_dir"
                return 0
            fi
        fi
        depth=$((depth + 1))
    done
    
    # Fallback: use current script directory
    cd "$(dirname "${BASH_SOURCE[0]}")" && pwd
}

# Get project root
get_project_root() {
    local script_dir
    script_dir="$(get_script_dir)"
    
    # If we're in scripts/deployment, go up 2 levels
    # If we're in scripts/, go up 1 level
    # If we're in lib/, go up 1 level
    if [[ "$script_dir" == */scripts/deployment ]]; then
        echo "$(cd "$script_dir/../.." && pwd)"
    elif [[ "$script_dir" == */scripts ]]; then
        echo "$(cd "$script_dir/.." && pwd)"
    elif [[ "$script_dir" == */lib ]]; then
        echo "$(cd "$script_dir/.." && pwd)"
    else
        # Default: go up one level
        echo "$(cd "$script_dir/.." && pwd)"
    fi
}

# Load .env file from ~/.env (standardized location)
load_env_file() {
    local env_file="${HOME}/.env"
    if [[ -f "$env_file" ]]; then
        # Source PROXMOX_* variables from ~/.env
        set -a
        source <(grep -E "^PROXMOX_" "$env_file" 2>/dev/null | sed 's/^/export /' || true)
        set +a
        log_debug "Loaded environment variables from: $env_file"
    fi
}

# Load configuration file
load_config() {
    local config_file="${1:-}"
    local project_root
    
    # Use PROJECT_ROOT if already set (from calling script), otherwise detect it
    if [[ -n "${PROJECT_ROOT:-}" ]]; then
        project_root="$PROJECT_ROOT"
    else
        project_root="$(get_project_root)"
    fi
    
    # First, load from ~/.env (standardized location for credentials)
    load_env_file
    
    if [[ -z "$config_file" ]]; then
        config_file="${project_root}/config/proxmox.conf"
    fi
    
    # If config_file is relative, make it absolute relative to project_root
    if [[ "$config_file" != /* ]]; then
        if [[ "$config_file" == config/* ]]; then
            config_file="${project_root}/${config_file}"
        else
            config_file="${project_root}/config/${config_file}"
        fi
    fi
    
    if [[ ! -f "$config_file" ]]; then
        error_exit "Configuration file not found: $config_file"
    fi
    
    # Source config file (may override or add to .env values)
    # But preserve PROJECT_ROOT if it was already set correctly
    local saved_project_root="${PROJECT_ROOT:-}"
    set -a
    source "$config_file"
    set +a
    
    # If PROJECT_ROOT was set in config but we had a better one, restore it
    if [[ -n "$saved_project_root" ]] && [[ "$saved_project_root" == *"smom-dbis-138-proxmox"* ]]; then
        PROJECT_ROOT="$saved_project_root"
    fi
    
    # Ensure PROXMOX_TOKEN_SECRET is set from PROXMOX_TOKEN_VALUE if needed (for backwards compatibility)
    if [[ -z "${PROXMOX_TOKEN_SECRET:-}" ]] && [[ -n "${PROXMOX_TOKEN_VALUE:-}" ]]; then
        PROXMOX_TOKEN_SECRET="${PROXMOX_TOKEN_VALUE}"
    fi
    
    log_debug "Loaded configuration from: $config_file"
}

# Validate required variables
validate_vars() {
    local missing_vars=()
    local var
    
    for var in "$@"; do
        if [[ -z "${!var:-}" ]]; then
            missing_vars+=("$var")
        fi
    done
    
    if [[ ${#missing_vars[@]} -gt 0 ]]; then
        error_exit "Missing required variables: ${missing_vars[*]}"
    fi
}

# Generate random password
generate_password() {
    openssl rand -base64 32 | tr -d "=+/" | cut -c1-25
}

# Wait for container to be ready
wait_for_container() {
    local vmid="$1"
    local max_attempts="${2:-30}"
    local attempt=1
    
    log_info "Waiting for container $vmid to be ready..."
    
    while [[ $attempt -le $max_attempts ]]; do
        # Check if container exists (status command works)
        if pct status "$vmid" &>/dev/null 2>&1; then
            local status
            status=$(pct status "$vmid" 2>/dev/null | awk '{print $2}' || echo "")
            # Container is ready if it exists (can be stopped or running)
            if [[ -n "$status" ]]; then
                log_success "Container $vmid is ready (status: $status)"
                return 0
            fi
        fi
        sleep 2
        attempt=$((attempt + 1))
    done
    
    error_exit "Container $vmid did not become ready in time"
}

# Get container IP address
get_container_ip() {
    local vmid="$1"
    local interface="${2:-eth0}"
    
    pct exec "$vmid" -- ip -4 addr show "$interface" | grep -oP '(?<=inet\s)\d+(\.\d+){3}' | head -1
}

# Execute command in container
exec_in_container() {
    local vmid="$1"
    shift
    local cmd=("$@")
    
    log_debug "Executing in container $vmid: ${cmd[*]}"
    pct exec "$vmid" -- "${cmd[@]}"
}

# Copy file to container
copy_to_container() {
    local vmid="$1"
    local src="$2"
    local dst="$3"
    
    log_debug "Copying $src to container $vmid:$dst"
    pct push "$vmid" "$src" "$dst"
}

# Create backup directory
create_backup_dir() {
    local backup_base="${1:-/var/lib/vz/backup/smom-dbis-138}"
    local timestamp
    timestamp="$(date +%Y%m%d_%H%M%S)"
    local backup_dir="${backup_base}/${timestamp}"
    
    mkdir -p "$backup_dir"
    echo "$backup_dir"
}

# Validate IP address
validate_ip() {
    local ip="$1"
    if [[ $ip =~ ^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$ ]]; then
        local IFS='.'
        local -a octets
        read -ra octets <<< "$ip"
        for octet in "${octets[@]}"; do
            if [[ $octet -gt 255 ]]; then
                return 1
            fi
        done
        return 0
    fi
    return 1
}

# Convert NETMASK to CIDR prefix length
# Supports both dotted decimal (255.255.255.0) and CIDR prefix (24) formats
get_cidr_prefix() {
    local netmask="${1:-24}"
    
    # If already a number, assume it's a CIDR prefix
    if [[ "$netmask" =~ ^[0-9]+$ ]] && [[ "$netmask" -ge 0 ]] && [[ "$netmask" -le 32 ]]; then
        echo "$netmask"
        return 0
    fi
    
    # Convert dotted decimal to CIDR
    case "$netmask" in
        "255.255.255.255") echo "32" ;;
        "255.255.255.254") echo "31" ;;
        "255.255.255.252") echo "30" ;;
        "255.255.255.248") echo "29" ;;
        "255.255.255.240") echo "28" ;;
        "255.255.255.224") echo "27" ;;
        "255.255.255.192") echo "26" ;;
        "255.255.255.128") echo "25" ;;
        "255.255.255.0") echo "24" ;;
        "255.255.254.0") echo "23" ;;
        "255.255.252.0") echo "22" ;;
        "255.255.248.0") echo "21" ;;
        "255.255.240.0") echo "20" ;;
        "255.255.224.0") echo "19" ;;
        "255.255.192.0") echo "18" ;;
        "255.255.128.0") echo "17" ;;
        "255.255.0.0") echo "16" ;;
        "255.254.0.0") echo "15" ;;
        "255.252.0.0") echo "14" ;;
        "255.248.0.0") echo "13" ;;
        "255.240.0.0") echo "12" ;;
        "255.224.0.0") echo "11" ;;
        "255.192.0.0") echo "10" ;;
        "255.128.0.0") echo "9" ;;
        "255.0.0.0") echo "8" ;;
        *) 
            log_warn "Unknown netmask format: $netmask, defaulting to 24"
            echo "24"
            ;;
    esac
}

# Get next available VMID
get_next_vmid() {
    local start_vmid="${1:-100}"
    local vmid="$start_vmid"
    
    while pct list | grep -q "^\s*$vmid\s"; do
        vmid=$((vmid + 1))
    done
    
    echo "$vmid"
}

# Parse container inventory
parse_inventory() {
    local inventory_file="${1:-}"
    local project_root
    project_root="$(get_project_root)"
    
    if [[ -z "$inventory_file" ]]; then
        inventory_file="${project_root}/config/inventory.conf"
    fi
    
    if [[ ! -f "$inventory_file" ]]; then
        log_warn "Inventory file not found: $inventory_file"
        return 1
    fi
    
    # Source inventory file
    source "$inventory_file"
    return 0
}

# Check and ensure OS template exists
# Extracts template name from CONTAINER_OS_TEMPLATE (e.g., "local:vztmpl/ubuntu-22.04-standard_22.04-1_amd64.tar.zst" -> "ubuntu-22.04-standard_22.04-1_amd64.tar.zst")
# If exact version not found, tries to find and use the latest available version
ensure_os_template() {
    local template="${1:-${CONTAINER_OS_TEMPLATE:-local:vztmpl/ubuntu-22.04-standard_22.04-1_amd64.tar.zst}}"
    
    # Extract template filename from storage:path format
    # Format: "local:vztmpl/ubuntu-22.04-standard_22.04-1_amd64.tar.zst"
    local template_name
    if [[ "$template" == *":"* ]]; then
        # Remove storage prefix (e.g., "local:")
        template_name="${template#*:}"
        # Remove path prefix if present (e.g., "vztmpl/")
        template_name="${template_name##*/}"
    else
        template_name="$template"
    fi
    
    # Extract base name for checking (e.g., "ubuntu-22.04-standard" from "ubuntu-22.04-standard_22.04-1_amd64.tar.zst")
    local template_base="${template_name%%_*}"
    
    log_info "Checking for OS template: $template_name (base: $template_base)"
    
    # Check if template exists using pveam
    if ! command_exists pveam; then
        log_error "pveam command not found. Cannot check/download template."
        return 1
    fi
    
    # Check if exact template exists locally
    # pveam list local format: "local:vztmpl/ubuntu-22.04-standard_22.04-1_amd64.tar.zst"
    local local_list
    local_list=$(pveam list local 2>/dev/null || echo "")
    
    if echo "$local_list" | grep -qE "vztmpl/${template_name}"; then
        log_success "OS template found: $template_name"
        return 0
    fi
    
    # Check if any version of this template base exists locally
    local local_template
    local_template=$(echo "$local_list" | grep -E "vztmpl/${template_base}_" | head -1 | sed 's/.*vztmpl\///' | awk '{print $1}' || echo "")
    if [[ -n "$local_template" ]]; then
        log_warn "Exact template version not found, but found: $local_template"
        log_info "Using available template: $local_template"
        # Update CONTAINER_OS_TEMPLATE to use the found template
        local storage_prefix="${template%%:*}"
        CONTAINER_OS_TEMPLATE="${storage_prefix}:vztmpl/${local_template}"
        log_success "Updated CONTAINER_OS_TEMPLATE to: $CONTAINER_OS_TEMPLATE"
        return 0
    fi
    
    log_warn "OS template not found locally: $template_name"
    log_info "Checking available templates for download..."
    
    # Check available templates and find the best match
    local available_template
    available_template=$(pveam available 2>/dev/null | grep -E "(^|\s)${template_base}_" | head -1 | awk '{print $2}' || echo "")
    
    if [[ -n "$available_template" ]]; then
        log_info "Found available template: $available_template"
        log_info "Downloading template (this may take a few minutes)..."
        
        # Try to download the available template
        local download_output
        download_output=$(pveam download local "$available_template" 2>&1)
        local download_exit=$?
        
        # Wait a moment for download to complete
        sleep 2
        
        # Check if download was successful or if file already exists
        if [[ $download_exit -eq 0 ]] || echo "$download_output" | grep -q "OK, got correct file already"; then
            # Verify it exists now
            local verify_list
            verify_list=$(pveam list local 2>/dev/null || echo "")
            if echo "$verify_list" | grep -qE "vztmpl/${available_template}"; then
                log_success "Template available: $available_template"
                # Update CONTAINER_OS_TEMPLATE to use the template
                local storage_prefix="${template%%:*}"
                CONTAINER_OS_TEMPLATE="${storage_prefix}:vztmpl/${available_template}"
                log_info "Using template: $CONTAINER_OS_TEMPLATE"
                return 0
            else
                # Template might be in cache but not listed yet, or verification format issue
                log_warn "Template download completed, but verification format may differ"
                log_info "Template should be available. Proceeding..."
                local storage_prefix="${template%%:*}"
                CONTAINER_OS_TEMPLATE="${storage_prefix}:vztmpl/${available_template}"
                return 0  # Continue anyway - template is likely available
            fi
        else
            log_error "Failed to download template: $available_template"
            log_info "Please download it manually:"
            log_info "  pveam download local $available_template"
            return 1
        fi
    else
        log_error "Template not found in available templates: $template_base"
        log_info "Available Ubuntu templates:"
        pveam available 2>/dev/null | grep -i ubuntu | head -5 || echo "  (none found)"
        log_info "Please download a template manually:"
        log_info "  pveam download local <template-name>"
        return 1
    fi
}

