#!/bin/bash
# PatchMon Self-Hosting Installation Script
# Automated deployment script for self-hosted PatchMon instances
# Usage: ./self-hosting-install.sh
# Interactive self-hosting installation script

set -e

# Create main installation log file
INSTALL_LOG="/var/log/patchmon-install.log"
echo "[$(date '+%Y-%m-%d %H:%M:%S')] === PatchMon Self-Hosting Installation Started ===" >> "$INSTALL_LOG"
echo "[$(date '+%Y-%m-%d %H:%M:%S')] Script PID: $$" >> "$INSTALL_LOG"
echo "[$(date '+%Y-%m-%d %H:%M:%S')] Running as user: $(whoami)" >> "$INSTALL_LOG"
echo "[$(date '+%Y-%m-%d %H:%M:%S')] Current directory: $(pwd)" >> "$INSTALL_LOG"
echo "[$(date '+%Y-%m-%d %H:%M:%S')] Script arguments: $@" >> "$INSTALL_LOG"
echo "[$(date '+%Y-%m-%d %H:%M:%S')] Script path: $0" >> "$INSTALL_LOG"
echo "[$(date '+%Y-%m-%d %H:%M:%S')] ======================================" >> "$INSTALL_LOG"

# Create immediate debug log for troubleshooting
DEBUG_LOG="/tmp/patchmon_debug_$(date +%Y%m%d_%H%M%S).log"
echo "[$(date '+%Y-%m-%d %H:%M:%S')] === PatchMon Script Started ===" >> "$DEBUG_LOG"
echo "[$(date '+%Y-%m-%d %H:%M:%S')] Script PID: $$" >> "$DEBUG_LOG"
echo "[$(date '+%Y-%m-%d %H:%M:%S')] Running as user: $(whoami)" >> "$DEBUG_LOG"
echo "[$(date '+%Y-%m-%d %H:%M:%S')] Current directory: $(pwd)" >> "$DEBUG_LOG"
echo "[$(date '+%Y-%m-%d %H:%M:%S')] Script arguments: $@" >> "$DEBUG_LOG"
echo "[$(date '+%Y-%m-%d %H:%M:%S')] Script path: $0" >> "$DEBUG_LOG"
echo "[$(date '+%Y-%m-%d %H:%M:%S')] ======================================" >> "$DEBUG_LOG"

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

# Global variables
SCRIPT_VERSION="self-hosting-install.sh v1.2.9-selfhost-2025-10-10-6"
DEFAULT_GITHUB_REPO="https://github.com/PatchMon/PatchMon.git"
FQDN=""
CUSTOM_FQDN=""
EMAIL=""

# Logging function
function log_message() {
    local message="$1"
    local timestamp=$(date '+%Y-%m-%d %H:%M:%S')
    local log_file="/var/log/patchmon-install.log"
    
    echo "[${timestamp}] ${message}" >> "$log_file"
    echo "[${timestamp}] ${message}"
}
DEPLOYMENT_BRANCH="main"
GITHUB_REPO=""
DB_SAFE_DB_DB_USER=""
DB_PASS=""
JWT_SECRET=""
BACKEND_PORT=""
APP_DIR=""
SERVICE_USE_LETSENCRYPT="true"  # Will be set based on user input
SERVER_PROTOCOL_SEL="https"
SERVER_PORT_SEL=""  # Will be set to BACKEND_PORT in init_instance_vars
SETUP_NGINX="true"
UPDATE_MODE="false"
SELECTED_INSTANCE=""
SELECTED_SERVICE_NAME=""

# Functions
print_status() {
    echo -e "${GREEN}✅ $1${NC}"
}

print_info() {
    echo -e "${BLUE}ℹ️  $1${NC}"
}

print_error() {
    echo -e "${RED}❌ $1${NC}"
}

print_warning() {
    echo -e "${YELLOW}⚠️  $1${NC}"
}

print_question() {
    echo -e "${BLUE}❓ $1${NC}"
}

print_success() {
    echo -e "${GREEN}🎉 $1${NC}"
}

# Interactive input functions
read_input() {
    local prompt="$1"
    local var_name="$2"
    local default_value="$3"
    
    if [ -n "$default_value" ]; then
        echo -n -e "${BLUE}$prompt${NC} [${YELLOW}$default_value${NC}]: "
    else
        echo -n -e "${BLUE}$prompt${NC}: "
    fi
    
    read -r input
    if [ -z "$input" ] && [ -n "$default_value" ]; then
        eval "$var_name='$default_value'"
    else
        eval "$var_name='$input'"
    fi
}

read_yes_no() {
    local prompt="$1"
    local var_name="$2"
    local default_value="$3"
    
    while true; do
        if [ -n "$default_value" ]; then
            echo -n -e "${BLUE}$prompt${NC} [${YELLOW}$default_value${NC}]: "
        else
            echo -n -e "${BLUE}$prompt${NC} (y/n): "
        fi
        read -r input
        
        if [ -z "$input" ] && [ -n "$default_value" ]; then
            input="$default_value"
        fi
        
        case $input in
            [Yy]|[Yy][Ee][Ss])
                eval "$var_name='y'"
                break
                ;;
            [Nn]|[Nn][Oo])
                eval "$var_name='n'"
                break
                ;;
            *)
                print_error "Please answer yes (y) or no (n)"
                ;;
        esac
    done
}

print_banner() {
    echo -e "${BLUE}====================================================${NC}"
    echo -e "${BLUE}        PatchMon Self-Hosting Installation${NC}"
    echo -e "${BLUE}Running: $SCRIPT_VERSION${NC}"
    echo -e "${BLUE}====================================================${NC}"
}

# Interactive setup functions
check_timezone() {
    print_info "Checking current timezone..."
    current_tz=$(timedatectl show --property=Timezone --value 2>/dev/null || echo "Unknown")
    
    if [ "$current_tz" != "Unknown" ]; then
        current_datetime=$(date)
        print_info "Current timezone: $current_tz"
        print_info "Current date/time: $current_datetime"
        read_yes_no "Is this timezone and date/time correct?" TIMEZONE_CORRECT "y"
        
        if [ "$TIMEZONE_CORRECT" = "n" ]; then
            print_info "Available timezones:"
            timedatectl list-timezones | head -20
            print_warning "Showing first 20 timezones. Use 'timedatectl list-timezones' to see all."
            read_input "Enter your timezone (e.g., America/New_York, Europe/London)" NEW_TIMEZONE
            
            if [ -n "$NEW_TIMEZONE" ]; then
                print_info "Setting timezone to $NEW_TIMEZONE..."
                timedatectl set-timezone "$NEW_TIMEZONE"
                print_status "Timezone updated to $NEW_TIMEZONE"
                
                # Show updated date/time
                updated_datetime=$(date)
                print_info "Updated date/time: $updated_datetime"
            fi
        fi
    else
        print_warning "Could not detect timezone. Please set it manually if needed."
        current_datetime=$(date)
        print_info "Current date/time: $current_datetime"
    fi
}

check_root() {
    if [[ $EUID -ne 0 ]]; then
        print_error "This script must be run as root"
        print_info "Please run: sudo $0"
        exit 1
    fi
}

# Function to run commands as a specific user with better error handling
run_as_user() {
    local user="$1"
    local command="$2"
    
    if ! command -v sudo >/dev/null 2>&1; then
        print_error "sudo is required but not installed. Please install sudo first."
        exit 1
    fi
    
    if ! id "$user" &>/dev/null; then
        print_error "User '$user' does not exist"
        exit 1
    fi
    
    sudo -u "$user" bash -c "$command"
}

# Detect and use the best available package manager
detect_package_manager() {
    # Prefer apt over apt-get for modern Debian/Ubuntu systems
    if command -v apt >/dev/null 2>&1; then
        PKG_MANAGER="apt"
        PKG_UPDATE="apt update"
        PKG_UPGRADE="apt upgrade -y"
        PKG_INSTALL="apt install -y"
    elif command -v apt-get >/dev/null 2>&1; then
        PKG_MANAGER="apt-get"
        PKG_UPDATE="apt-get update"
        PKG_UPGRADE="apt-get upgrade -y"
        PKG_INSTALL="apt-get install -y"
    else
        print_error "No supported package manager found (apt or apt-get required)"
        print_info "This script requires a Debian/Ubuntu-based system"
        exit 1
    fi
    
    print_info "Using package manager: $PKG_MANAGER"
}

check_prerequisites() {
    print_info "Running and checking prerequisites..."
    
    # Check if running as root
    check_root
    
    # Detect package manager
    detect_package_manager
    
    print_info "Installing updates..."
    $PKG_UPDATE -y
    $PKG_UPGRADE
    
    print_info "Installing prerequisite applications..."
    # Install sudo if not present (needed for user switching)
    if ! command -v sudo >/dev/null 2>&1; then
        print_info "Installing sudo (required for user switching)..."
        $PKG_INSTALL sudo
    fi
    
    $PKG_INSTALL wget curl jq git netcat-openbsd
    
    print_status "Prerequisites installed successfully"
}

select_branch() {
    print_info "Fetching available releases from GitHub repository..."
    
    # Create temporary directory for git operations
    TEMP_DIR="/tmp/patchmon_branches_$$"
    mkdir -p "$TEMP_DIR"
    cd "$TEMP_DIR"
    
    # Try to clone the repository normally
    if git clone "$DEFAULT_GITHUB_REPO" . 2>/dev/null; then
        # Get list of tags sorted by version (semantic versioning)
        # Using git tag with version sorting
        tags=$(git tag -l --sort=-v:refname 2>/dev/null | head -3)
        
        if [ -n "$tags" ]; then
            print_info "Available releases and branches:"
            echo ""
            
            # Display last 3 release tags
            option_count=1
            declare -A options_map
            
            while IFS= read -r tag; do
                if [ -n "$tag" ]; then
                    # Get tag date and commit info
                    tag_date=$(git log -1 --format="%ci" "$tag" 2>/dev/null || echo "Unknown")
                    
                    # Format the date
                    if [ "$tag_date" != "Unknown" ]; then
                        formatted_date=$(date -d "$tag_date" "+%Y-%m-%d %H:%M" 2>/dev/null || echo "$tag_date")
                    else
                        formatted_date="Unknown"
                    fi
                    
                    # Mark the first one as latest
                    if [ $option_count -eq 1 ]; then
                        printf "%2d. %-20s (Latest Release - %s)\n" "$option_count" "$tag" "$formatted_date"
                    else
                        printf "%2d. %-20s (Release - %s)\n" "$option_count" "$tag" "$formatted_date"
                    fi
                    
                    # Store the tag for later selection
                    options_map[$option_count]="$tag"
                    option_count=$((option_count + 1))
                fi
            done <<< "$tags"
            
            # Add main branch as an option
            main_commit=$(git log -1 --format="%ci" "origin/main" 2>/dev/null || echo "Unknown")
            if [ "$main_commit" != "Unknown" ]; then
                formatted_main_date=$(date -d "$main_commit" "+%Y-%m-%d %H:%M" 2>/dev/null || echo "$main_commit")
            else
                formatted_main_date="Unknown"
            fi
            printf "%2d. %-20s (Development Branch - %s)\n" "$option_count" "main" "$formatted_main_date"
            options_map[$option_count]="main"
            
            echo ""
            
            # Default to option 1 (latest release tag)
            default_option=1
            
            while true; do
                read_input "Select version/branch number" SELECTION_NUMBER "$default_option"
                
                if [[ "$SELECTION_NUMBER" =~ ^[0-9]+$ ]]; then
                    selected_option="${options_map[$SELECTION_NUMBER]}"
                    if [ -n "$selected_option" ]; then
                        DEPLOYMENT_BRANCH="$selected_option"
                        
                        # Show confirmation
                        if [ "$selected_option" = "main" ]; then
                            print_status "Selected branch: main (latest development code)"
                            print_info "Last commit: $formatted_main_date"
                        else
                            print_status "Selected release: $selected_option"
                            tag_date=$(git log -1 --format="%ci" "$selected_option" 2>/dev/null || echo "Unknown")
                            if [ "$tag_date" != "Unknown" ]; then
                                formatted_date=$(date -d "$tag_date" "+%Y-%m-%d %H:%M" 2>/dev/null || echo "$tag_date")
                                print_info "Release date: $formatted_date"
                            fi
                        fi
                        break
                    else
                        print_error "Invalid selection number. Please try again."
                    fi
                else
                    print_error "Please enter a valid number."
                fi
            done
        else
            print_warning "No release tags found, using default: main"
            DEPLOYMENT_BRANCH="main"
        fi
    else
        print_warning "Could not connect to GitHub repository"
        print_warning "This might be due to:"
        print_warning "  • Network connectivity issues"
        print_warning "  • Firewall blocking git access"
        print_warning "  • GitHub repository access restrictions"
        print_warning "Using default branch: main"
        DEPLOYMENT_BRANCH="main"
    fi
    
    # Clean up
    cd /
    rm -rf "$TEMP_DIR"
}

interactive_setup() {
    print_banner
    
    print_info "Welcome to PatchMon Self-Hosting Installation!"
    print_info "This script will guide you through the installation process."
    echo ""
    
    # Check prerequisites
    check_prerequisites
    echo ""
    
    # Check timezone
    check_timezone
    echo ""
    
    # Get basic information
    print_question "Let's gather some information about your installation:"
    echo ""
    
    read_input "Enter your domain name or IP address (e.g., patchmon.yourdomain.com or 192.168.1.100)" FQDN "patchmon.internal"
    
    echo ""
    print_info "🔒 SSL/HTTPS Configuration:"
    print_info "   • Public hosting (accessible from internet): Enable SSL for security"
    print_info "   • Local hosting (internal network only): SSL not required"
    echo ""
    read_yes_no "Are you hosting this publicly on the internet and want SSL/HTTPS with Let's Encrypt?" SSL_ENABLED "n"
    
    if [ "$SSL_ENABLED" = "y" ]; then
        read_input "Enter your email address for Let's Encrypt SSL certificate" EMAIL
    else
        EMAIL=""
    fi
    
    
    # Select branch
    echo ""
    select_branch
    echo ""
    
    # Confirm settings
    print_info "Please confirm your settings:"
    echo "  Domain/IP: $FQDN"
    echo "  SSL Enabled: $SSL_ENABLED"
    if [ "$SSL_ENABLED" = "y" ]; then
        echo "  Email: $EMAIL"
    fi
    echo "  Branch: $DEPLOYMENT_BRANCH"
    echo ""
    
    read_yes_no "Proceed with installation?" CONFIRM_INSTALL "y"
    
    if [ "$CONFIRM_INSTALL" = "n" ]; then
        print_info "Installation cancelled by user."
        exit 0
    fi
    
    print_success "Starting installation process..."
    echo ""
}

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

# Generate JWT secret
generate_jwt_secret() {
    openssl rand -base64 64 | tr -d "=+/" | cut -c1-50
}

# Initialize instance variables
init_instance_vars() {
    echo "[$(date '+%Y-%m-%d %H:%M:%S')] init_instance_vars function started" >> "$DEBUG_LOG"
    
    echo "[$(date '+%Y-%m-%d %H:%M:%S')] Creating safe database name from FQDN: $FQDN" >> "$DEBUG_LOG"
    
    # Create safe database name from FQDN
    DB_SAFE_NAME=$(echo "$FQDN" | sed 's/[^a-zA-Z0-9]/_/g' | sed 's/^_*//' | sed 's/_*$//')
    
    # Check if FQDN starts with a digit (likely an IP address)
    if [[ "$FQDN" =~ ^[0-9] ]]; then
        # Generate 2 random letters for IP address prefixing
        RANDOM_PREFIX=$(tr -dc 'a-z' < /dev/urandom | head -c 2)
        DB_SAFE_NAME="${RANDOM_PREFIX}${DB_SAFE_NAME}"
        echo "[$(date '+%Y-%m-%d %H:%M:%S')] IP address detected, prefixed with: $RANDOM_PREFIX" >> "$DEBUG_LOG"
        print_info "IP address detected ($FQDN), using prefix '$RANDOM_PREFIX' for database/service names"
    fi
    
    DB_NAME="${DB_SAFE_NAME}"
    DB_USER="${DB_SAFE_NAME}"
    
    echo "[$(date '+%Y-%m-%d %H:%M:%S')] DB_SAFE_NAME: $DB_SAFE_NAME" >> "$DEBUG_LOG"
    echo "[$(date '+%Y-%m-%d %H:%M:%S')] DB_NAME: $DB_NAME" >> "$DEBUG_LOG"
    echo "[$(date '+%Y-%m-%d %H:%M:%S')] DB_USER: $DB_USER" >> "$DEBUG_LOG"
    
    echo "[$(date '+%Y-%m-%d %H:%M:%S')] Generating password..." >> "$DEBUG_LOG"
    DB_PASS=$(generate_password)
    
    echo "[$(date '+%Y-%m-%d %H:%M:%S')] Generating JWT secret..." >> "$DEBUG_LOG"
    JWT_SECRET=$(generate_jwt_secret)
    
    echo "[$(date '+%Y-%m-%d %H:%M:%S')] Generating random backend port..." >> "$DEBUG_LOG"
    
    # Generate random backend port (3001-3999)
    BACKEND_PORT=$((3001 + RANDOM % 999))
    
    # Set SERVER_PORT_SEL to 443 for HTTPS (external port) or backend port for HTTP
    if [ "$SERVER_PROTOCOL_SEL" = "https" ]; then
        SERVER_PORT_SEL=443
    else
        SERVER_PORT_SEL=$BACKEND_PORT
    fi
    
    echo "[$(date '+%Y-%m-%d %H:%M:%S')] BACKEND_PORT: $BACKEND_PORT" >> "$DEBUG_LOG"
    echo "[$(date '+%Y-%m-%d %H:%M:%S')] SERVER_PORT_SEL: $SERVER_PORT_SEL" >> "$DEBUG_LOG"
    
    echo "[$(date '+%Y-%m-%d %H:%M:%S')] Setting application directory and service name..." >> "$DEBUG_LOG"
    
    # Set application directory and service name
    APP_DIR="/opt/${FQDN}"
    SERVICE_NAME="${FQDN}"
    
    echo "[$(date '+%Y-%m-%d %H:%M:%S')] APP_DIR: $APP_DIR" >> "$DEBUG_LOG"
    echo "[$(date '+%Y-%m-%d %H:%M:%S')] SERVICE_NAME: $SERVICE_NAME" >> "$DEBUG_LOG"
    
    echo "[$(date '+%Y-%m-%d %H:%M:%S')] Creating dedicated user name..." >> "$DEBUG_LOG"
    
    # Create dedicated user name (safe for system users)
    INSTANCE_USER=$(echo "$DB_SAFE_NAME" | cut -c1-32)
    
    echo "[$(date '+%Y-%m-%d %H:%M:%S')] INSTANCE_USER: $INSTANCE_USER" >> "$DEBUG_LOG"
    
    print_info "Initialized variables for $FQDN"
    print_info "Database: $DB_NAME"
    print_info "Backend Port: $BACKEND_PORT"
    print_info "App Directory: $APP_DIR"
    print_info "Instance User: $INSTANCE_USER"
    
    echo "[$(date '+%Y-%m-%d %H:%M:%S')] init_instance_vars function completed successfully" >> "$DEBUG_LOG"
}

# Update system packages
update_system() {
    print_info "Updating system packages..."
    $PKG_UPDATE -y
    $PKG_UPGRADE
}

# Install essential tools
install_essential_tools() {
    print_info "Installing essential tools..."
    $PKG_INSTALL curl netcat-openbsd git jq
}

# Install Node.js (if not already installed)
install_nodejs() {
    # Force PATH refresh to ensure we get the latest Node.js
    export PATH="/usr/bin:/usr/local/bin:$PATH"
    hash -r  # Clear bash command cache
    
    NODE_VERSION=""
    if command -v node >/dev/null 2>&1; then
        NODE_VERSION=$(node --version 2>/dev/null | sed 's/v//')
        print_info "Node.js already installed: v$NODE_VERSION"
        
        # Check if version is 18 or higher
        if [ "$(echo "$NODE_VERSION" | cut -d. -f1)" -ge 18 ]; then
            print_status "Node.js version is sufficient (v$NODE_VERSION)"
            # Clean npm cache to avoid issues
            npm cache clean --force 2>/dev/null || true
            return 0
        else
            print_warning "Node.js version $NODE_VERSION is too old, updating..."
        fi
    fi
    
    print_info "Installing Node.js 20.x..."
    curl -fsSL https://deb.nodesource.com/setup_20.x | bash -
    $PKG_INSTALL nodejs
    
    # Verify installation
    NODE_VERSION=$(node --version | sed 's/v//')
    NPM_VERSION=$(npm --version)
    print_status "Node.js installed: v$NODE_VERSION"
    print_status "npm installed: v$NPM_VERSION"
    
    # Clean npm cache to avoid issues
    npm cache clean --force 2>/dev/null || true
}

# Install PostgreSQL
install_postgresql() {
    print_info "Installing PostgreSQL..."
    
    if systemctl is-active --quiet postgresql; then
        print_status "PostgreSQL already running"
    else
        $PKG_INSTALL postgresql postgresql-contrib
        systemctl start postgresql
        systemctl enable postgresql
        print_status "PostgreSQL installed and started"
    fi
}

# Install nginx
install_nginx() {
    print_info "Installing nginx..."
    
    if systemctl is-active --quiet nginx; then
        print_status "nginx already running"
    else
        $PKG_INSTALL nginx
        systemctl start nginx
        systemctl enable nginx
        print_status "nginx installed and started"
    fi
}

# Install certbot for Let's Encrypt
install_certbot() {
    print_info "Installing certbot for Let's Encrypt..."
    
    if command -v certbot >/dev/null 2>&1; then
        print_status "certbot already installed"
    else
        $PKG_INSTALL certbot python3-certbot-nginx
        print_status "certbot installed"
    fi
}

# Create dedicated user for this instance
create_instance_user() {
    print_info "Creating dedicated user: $INSTANCE_USER"
    
    # Create application directory first (as root)
    mkdir -p "$APP_DIR"
    
    # Check if user already exists
    if id "$INSTANCE_USER" &>/dev/null; then
        print_warning "User $INSTANCE_USER already exists, skipping creation"
        # Ensure directory ownership is correct for existing user
        chown -R "$INSTANCE_USER:$INSTANCE_USER" "$APP_DIR"
        chmod 755 "$APP_DIR"
        return 0
    fi
    
    # Create user with no login shell and no home directory
    useradd --system --no-create-home --shell /bin/false "$INSTANCE_USER"
    
    # Set ownership and permissions
    chown -R "$INSTANCE_USER:$INSTANCE_USER" "$APP_DIR"
    chmod 755 "$APP_DIR"
    
    print_status "Dedicated user $INSTANCE_USER created successfully"
}

# Setup Node.js environment isolation for this instance
setup_nodejs_isolation() {
    print_info "Setting up Node.js environment isolation for $INSTANCE_USER..."
    
    # Create npm directories as root first
    mkdir -p "$APP_DIR/.npm" "$APP_DIR/.npm-global"
    
    # Create .npmrc file with proper configuration
    cat > "$APP_DIR/.npmrc" << EOF
cache=$APP_DIR/.npm
prefix=$APP_DIR/.npm-global
init-module=$APP_DIR/.npm-global/.npm-init.js
tmp=$APP_DIR/.npm/tmp
EOF
    
    # Set ownership to the dedicated user
    chown -R "$INSTANCE_USER:$INSTANCE_USER" "$APP_DIR/.npm" "$APP_DIR/.npm-global" "$APP_DIR/.npmrc"
    
    print_status "Node.js environment isolation configured for $INSTANCE_USER"
}

# Setup database for instance
setup_database() {
    print_info "Setting up database: $DB_NAME"
    
    # Check if sudo is available for user switching
    if command -v sudo >/dev/null 2>&1; then
        # Check if user exists
        user_exists=$(sudo -u postgres psql -tAc "SELECT 1 FROM pg_roles WHERE rolname='$DB_USER'" || echo "0")
        
        if [ "$user_exists" = "1" ]; then
            print_info "Database user $DB_USER already exists, skipping creation"
        else
            print_info "Creating database user $DB_USER"
            sudo -u postgres psql -c "CREATE USER $DB_USER WITH PASSWORD '$DB_PASS';"
        fi
        
        # Check if database exists
        db_exists=$(sudo -u postgres psql -tAc "SELECT 1 FROM pg_database WHERE datname='$DB_NAME'" || echo "0")
        
        if [ "$db_exists" = "1" ]; then
            print_info "Database $DB_NAME already exists, skipping creation"
        else
            print_info "Creating database $DB_NAME"
            sudo -u postgres psql -c "CREATE DATABASE $DB_NAME OWNER $DB_USER;"
        fi
        
        # Always grant privileges (in case they were revoked)
        sudo -u postgres psql -c "GRANT ALL PRIVILEGES ON DATABASE $DB_NAME TO $DB_USER;"
    else
        # Alternative method for systems without sudo (run as postgres user directly)
        print_warning "sudo not available, using alternative method for PostgreSQL setup"
        
        # Check if user exists
        user_exists=$(su - postgres -c "psql -tAc \"SELECT 1 FROM pg_roles WHERE rolname='$DB_USER'\"" || echo "0")
        
        if [ "$user_exists" = "1" ]; then
            print_info "Database user $DB_USER already exists, skipping creation"
        else
            print_info "Creating database user $DB_USER"
            su - postgres -c "psql -c \"CREATE USER $DB_USER WITH PASSWORD '$DB_PASS';\""
        fi
        
        # Check if database exists
        db_exists=$(su - postgres -c "psql -tAc \"SELECT 1 FROM pg_database WHERE datname='$DB_NAME'\"" || echo "0")
        
        if [ "$db_exists" = "1" ]; then
            print_info "Database $DB_NAME already exists, skipping creation"
        else
            print_info "Creating database $DB_NAME"
            su - postgres -c "psql -c \"CREATE DATABASE $DB_NAME OWNER $DB_USER;\""
        fi
        
        # Always grant privileges (in case they were revoked)
        su - postgres -c "psql -c \"GRANT ALL PRIVILEGES ON DATABASE $DB_NAME TO $DB_USER;\""
    fi
    
    print_status "Database setup complete for $DB_NAME"
}

# Clone application repository
clone_application() {
    print_info "Cloning PatchMon application..."
    
    if [ -d "$APP_DIR" ]; then
        print_warning "Directory $APP_DIR already exists, removing..."
        rm -rf "$APP_DIR"
    fi
    
    git clone -b "$DEPLOYMENT_BRANCH" "$GITHUB_REPO" "$APP_DIR"
    
    # Set ownership to the dedicated user
    chown -R "$INSTANCE_USER:$INSTANCE_USER" "$APP_DIR"
    
    cd "$APP_DIR"
    
    print_status "Application cloned to $APP_DIR with ownership set to $INSTANCE_USER"
}

# Setup Node.js environment
setup_node_environment() {
    print_info "Setting up Node.js environment..."
    
    cd "$APP_DIR"
    
    # Set Node.js environment
    export NODE_ENV=production
    export PATH="/usr/bin:/usr/local/bin:$PATH"
    
    print_status "Node.js environment configured"
}

# Install dependencies
install_dependencies() {
    print_info "Installing dependencies as user $INSTANCE_USER..."
    
    cd "$APP_DIR"
    
    # Clean up any existing node_modules to avoid conflicts
    rm -rf node_modules
    
    # Create tmp directory for npm
    mkdir -p "$APP_DIR/.npm/tmp"
    
    # Fix npm cache ownership issues (common problem)
    chown -R "$INSTANCE_USER:$INSTANCE_USER" "$APP_DIR/.npm"
    
    # Clean npm cache to avoid permission issues
    run_as_user "$INSTANCE_USER" "cd $APP_DIR && npm cache clean --force" 2>/dev/null || true
    
    # Install root dependencies as the dedicated user
    print_info "Installing root dependencies..."
    if ! run_as_user "$INSTANCE_USER" "
        cd $APP_DIR
        export NPM_CONFIG_CACHE=$APP_DIR/.npm
        export NPM_CONFIG_PREFIX=$APP_DIR/.npm-global
        export NPM_CONFIG_TMP=$APP_DIR/.npm/tmp
        npm install --omit=dev --no-audit --no-fund --no-save --ignore-scripts
    "; then
        print_error "Failed to install root dependencies"
        return 1
    fi
    
    # Install backend dependencies as the dedicated user
    print_info "Installing backend dependencies..."
    cd backend
    rm -rf node_modules
    if ! run_as_user "$INSTANCE_USER" "
        cd $APP_DIR/backend
        export NPM_CONFIG_CACHE=$APP_DIR/.npm
        export NPM_CONFIG_PREFIX=$APP_DIR/.npm-global
        export NPM_CONFIG_TMP=$APP_DIR/.npm/tmp
        npm install --omit=dev --no-audit --no-fund --no-save --ignore-scripts
    "; then
        print_error "Failed to install backend dependencies"
        return 1
    fi
    cd ..
    
    # Install frontend dependencies as the dedicated user (including dev dependencies for build)
    print_info "Installing frontend dependencies..."
    cd frontend
    rm -rf node_modules
    if ! run_as_user "$INSTANCE_USER" "
        cd $APP_DIR/frontend
        export NPM_CONFIG_CACHE=$APP_DIR/.npm
        export NPM_CONFIG_PREFIX=$APP_DIR/.npm-global
        export NPM_CONFIG_TMP=$APP_DIR/.npm/tmp
        npm install --no-audit --no-fund --no-save --ignore-scripts
    "; then
        print_error "Failed to install frontend dependencies"
        return 1
    fi
    
    # Build frontend
    print_info "Building frontend..."
    if ! run_as_user "$INSTANCE_USER" "
        cd $APP_DIR/frontend
        export NPM_CONFIG_CACHE=$APP_DIR/.npm
        export NPM_CONFIG_PREFIX=$APP_DIR/.npm-global
        export NPM_CONFIG_TMP=$APP_DIR/.npm/tmp
        npm run build
    "; then
        print_error "Failed to build frontend"
        return 1
    fi
    cd ..
    
    # Ensure ownership is maintained
    chown -R "$INSTANCE_USER:$INSTANCE_USER" "$APP_DIR"
    
    print_status "Dependencies installed and frontend built as $INSTANCE_USER"
}

# Create environment files
create_env_files() {
    print_info "Creating environment files..."
    
    cd "$APP_DIR"
    
    # Backend .env
    cat > backend/.env << EOF
# Database Configuration
DATABASE_URL="postgresql://$DB_USER:$DB_PASS@localhost:5432/$DB_NAME"
PM_DB_CONN_MAX_ATTEMPTS=30
PM_DB_CONN_WAIT_INTERVAL=2

# JWT Configuration
JWT_SECRET="$JWT_SECRET"
JWT_EXPIRES_IN=1h
JWT_REFRESH_EXPIRES_IN=7d

# Server Configuration
PORT=$BACKEND_PORT
NODE_ENV=production

# API Configuration
API_VERSION=v1

# CORS Configuration
CORS_ORIGIN="$SERVER_PROTOCOL_SEL://$FQDN"

# Session Configuration
SESSION_INACTIVITY_TIMEOUT_MINUTES=30

# User Configuration
DEFAULT_USER_ROLE=user

# Rate Limiting (times in milliseconds)
RATE_LIMIT_WINDOW_MS=900000
RATE_LIMIT_MAX=5000
AUTH_RATE_LIMIT_WINDOW_MS=600000
AUTH_RATE_LIMIT_MAX=500
AGENT_RATE_LIMIT_WINDOW_MS=60000
AGENT_RATE_LIMIT_MAX=1000

# Logging
LOG_LEVEL=info
ENABLE_LOGGING=true
EOF

    # Frontend .env
    cat > frontend/.env << EOF
VITE_API_URL=$SERVER_PROTOCOL_SEL://$FQDN/api/v1
VITE_APP_NAME=PatchMon
VITE_APP_VERSION=1.2.9
EOF

    print_status "Environment files created"
}

# Run database migrations
run_migrations() {
    print_info "Running database migrations as user $INSTANCE_USER..."
    
    cd "$APP_DIR/backend"
    # Suppress Prisma CLI output (still logged to install log via tee)
    run_as_user "$INSTANCE_USER" "cd $APP_DIR/backend && npx prisma migrate deploy" >/dev/null 2>&1 || true
    run_as_user "$INSTANCE_USER" "cd $APP_DIR/backend && npx prisma generate" >/dev/null 2>&1 || true
    
    print_status "Database migrations completed as $INSTANCE_USER"
}

# Admin account creation removed - handled by application's first-time setup

# Create systemd service
create_systemd_service() {
    print_info "Creating systemd service for user $INSTANCE_USER..."
    
    cat > "/etc/systemd/system/$SERVICE_NAME.service" << EOF
[Unit]
Description=PatchMon Service for $FQDN
After=network.target postgresql.service

[Service]
Type=simple
User=$INSTANCE_USER
Group=$INSTANCE_USER
WorkingDirectory=$APP_DIR/backend
ExecStart=/usr/bin/node src/server.js
Restart=always
RestartSec=10
Environment=NODE_ENV=production
Environment=PATH=/usr/bin:/usr/local/bin
NoNewPrivileges=true
PrivateTmp=true
ProtectSystem=strict
ProtectHome=true
ReadWritePaths=$APP_DIR

[Install]
WantedBy=multi-user.target
EOF

    systemctl daemon-reload
    systemctl enable "$SERVICE_NAME"
    
    print_status "Systemd service created: $SERVICE_NAME (running as $INSTANCE_USER)"
}

# Setup nginx configuration
setup_nginx() {
    print_info "Setting up nginx configuration..."
    log_message "Setting up nginx configuration for $FQDN"
    
    if [ "$USE_LETSENCRYPT" = "true" ]; then
        # HTTP-only config first for Certbot challenge
        cat > "/etc/nginx/sites-available/$FQDN" << EOF
server {
    listen 80;
    server_name $FQDN;
    
    location /.well-known/acme-challenge/ {
        root /var/www/html;
    }
    
    location / {
        return 301 https://\$server_name\$request_uri;
    }
}
EOF
    else
        # HTTP-only configuration for local hosting
        cat > "/etc/nginx/sites-available/$FQDN" << EOF
server {
    listen 80;
    server_name $FQDN;
    
    # Frontend
    location / {
        root $APP_DIR/frontend/dist;
        try_files \$uri \$uri/ /index.html;
        
        # Security headers
        add_header X-Frame-Options DENY;
        add_header X-Content-Type-Options nosniff;
        add_header X-XSS-Protection "1; mode=block";
    }
    
    # API routes
    location /api/ {
        proxy_pass http://127.0.0.1:$BACKEND_PORT;
        proxy_http_version 1.1;
        proxy_set_header Upgrade \$http_upgrade;
        proxy_set_header Connection 'upgrade';
        proxy_set_header Host \$host;
        proxy_set_header X-Real-IP \$remote_addr;
        proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto \$scheme;
        proxy_cache_bypass \$http_upgrade;
        proxy_read_timeout 300s;
        proxy_connect_timeout 75s;
    }
    
    # Health check
    location /health {
        proxy_pass http://127.0.0.1:$BACKEND_PORT/health;
        access_log off;
    }
}
EOF
    fi

    # Enable site
    ln -sf "/etc/nginx/sites-available/$FQDN" "/etc/nginx/sites-enabled/"
    
    # Remove default site if it exists
    rm -f /etc/nginx/sites-enabled/default
    
    # Test nginx configuration
    nginx -t
    
    # Reload nginx
    systemctl reload nginx
    
    print_status "nginx configuration created for $FQDN"
}

# Setup Let's Encrypt SSL
setup_letsencrypt() {
    print_info "Setting up Let's Encrypt SSL certificate..."
    
    # Check if a valid certificate already exists
    if certbot certificates 2>/dev/null | grep -q "$FQDN" && certbot certificates 2>/dev/null | grep -A 10 "$FQDN" | grep -q "VALID"; then
        print_status "Valid SSL certificate already exists for $FQDN, skipping certificate generation"
        
        # Update Nginx config with existing HTTPS configuration
        cat > "/etc/nginx/sites-available/$FQDN" << EOF
server {
    listen 80;
    server_name $FQDN;
    return 301 https://\$server_name\$request_uri;
}

server {
    listen 443 ssl http2;
    server_name $FQDN;
    
    ssl_certificate /etc/letsencrypt/live/$FQDN/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/$FQDN/privkey.pem;
    include /etc/letsencrypt/options-ssl-nginx.conf;
    ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;
    
    # Security headers
    add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
    add_header X-Frame-Options DENY;
    add_header X-Content-Type-Options nosniff;
    add_header X-XSS-Protection "1; mode=block";
    
    # Frontend
    location / {
        root $APP_DIR/frontend/dist;
        try_files \$uri \$uri/ /index.html;
        
        # Security headers
        add_header X-Frame-Options DENY;
        add_header X-Content-Type-Options nosniff;
        add_header X-XSS-Protection "1; mode=block";
    }
    
    # API proxy
    location /api/ {
        proxy_pass http://127.0.0.1:$BACKEND_PORT;
        proxy_http_version 1.1;
        proxy_set_header Upgrade \$http_upgrade;
        proxy_set_header Connection 'upgrade';
        proxy_set_header Host \$host;
        proxy_set_header X-Real-IP \$remote_addr;
        proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto \$scheme;
        proxy_cache_bypass \$http_upgrade;
    }
}
EOF
        
        # Enable the site
        ln -sf "/etc/nginx/sites-available/$FQDN" "/etc/nginx/sites-enabled/"
        
        # Test nginx configuration
        if nginx -t; then
            print_status "Nginx configuration updated for existing SSL certificate"
            systemctl reload nginx
        else
            print_error "Nginx configuration test failed"
            return 1
        fi
        
        return 0
    fi
    
    print_info "No valid certificate found, generating new SSL certificate..."
    
    # Wait a moment for nginx to be ready
    sleep 5
    
    # Obtain SSL certificate
    log_message "Obtaining SSL certificate for $FQDN using Let's Encrypt"
    certbot --nginx -d "$FQDN" --non-interactive --agree-tos --email "$EMAIL" --redirect
    log_message "SSL certificate obtained successfully"
    
    # Update Nginx config with full HTTPS configuration
    cat > "/etc/nginx/sites-available/$FQDN" << EOF
server {
    listen 80;
    server_name $FQDN;
    return 301 https://\$server_name\$request_uri;
}

server {
    listen 443 ssl http2;
    server_name $FQDN;
    
    ssl_certificate /etc/letsencrypt/live/$FQDN/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/$FQDN/privkey.pem;
    include /etc/letsencrypt/options-ssl-nginx.conf;
    ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;
    
    # Frontend
    location / {
        root $APP_DIR/frontend/dist;
        try_files \$uri \$uri/ /index.html;
        
        # Security headers
        add_header X-Frame-Options DENY;
        add_header X-Content-Type-Options nosniff;
        add_header X-XSS-Protection "1; mode=block";
        add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
    }
    
    # API routes
    location /api/ {
        proxy_pass http://127.0.0.1:$BACKEND_PORT;
        proxy_http_version 1.1;
        proxy_set_header Upgrade \$http_upgrade;
        proxy_set_header Connection 'upgrade';
        proxy_set_header Host \$host;
        proxy_set_header X-Real-IP \$remote_addr;
        proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto \$scheme;
        proxy_cache_bypass \$http_upgrade;
        proxy_read_timeout 300s;
        proxy_connect_timeout 75s;
    }
    
    # Health check
    location /health {
        proxy_pass http://127.0.0.1:$BACKEND_PORT/health;
        access_log off;
    }
}
EOF
    
    nginx -t
    nginx -s reload
    
    # Setup auto-renewal
    echo "0 12 * * * /usr/bin/certbot renew --quiet" | crontab -
    
    print_status "SSL certificate obtained and auto-renewal configured"
}

# Start services
start_services() {
    print_info "Starting services..."
    
    # Start PatchMon service
    systemctl start "$SERVICE_NAME"
    
    # Wait for service to start
    sleep 10
    
    # Check if service is running
    if systemctl is-active --quiet "$SERVICE_NAME"; then
        print_status "PatchMon service started successfully"
    else
        print_error "Failed to start PatchMon service"
        systemctl status "$SERVICE_NAME"
        return 1
    fi
}

# Populate server settings in database
populate_server_settings() {
    print_info "Populating server settings in database..."
    
    cd "$APP_DIR/backend"
    
    # Create settings update script
    cat > update_settings.js << EOF
const { PrismaClient } = require('@prisma/client');
const prisma = new PrismaClient();

async function updateSettings() {
  try {
    // Check if settings record exists, create or update
    const existingSettings = await prisma.settings.findFirst();
    
    const settingsData = {
      server_url: '$SERVER_PROTOCOL_SEL://$FQDN',
      server_protocol: '$SERVER_PROTOCOL_SEL',
      server_host: '$FQDN',
      server_port: $SERVER_PORT_SEL,
      update_interval: 60,
      auto_update: true
    };
    
    if (existingSettings) {
      // Update existing settings
      await prisma.settings.update({
        where: { id: existingSettings.id },
        data: settingsData
      });
    } else {
      // Create new settings record
      await prisma.settings.create({
        data: settingsData
      });
    }
    
    console.log('✅ Database settings updated successfully');
  } catch (error) {
    console.error('❌ Error updating settings:', error.message);
    process.exit(1);
  } finally {
    await prisma.\$disconnect();
  }
}

updateSettings();
EOF

    # Run the settings update script as the dedicated user
    run_as_user "$INSTANCE_USER" "cd $APP_DIR/backend && node update_settings.js"
    
    # Clean up temporary script
    rm -f update_settings.js
    
    print_status "Server settings populated successfully"
}

# Create agent version
create_agent_version() {
    echo -e "${BLUE}🤖 Creating agent version...${NC}"
    log_message "Creating agent version in database..."
    cd $APP_DIR/backend
    
    # Priority 1: Get version from agent script (most accurate for agent versions)
    local current_version="N/A"
    if [ -f "$APP_DIR/agents/patchmon-agent.sh" ]; then
        current_version=$(grep '^AGENT_VERSION=' "$APP_DIR/agents/patchmon-agent.sh" | cut -d'"' -f2 2>/dev/null || echo "N/A")
        if [ "$current_version" != "N/A" ] && [ -n "$current_version" ]; then
            print_info "Detected agent version from script: $current_version"
        fi
    fi
    
    # Priority 2: Use fallback version if not found
    if [ "$current_version" = "N/A" ] || [ -z "$current_version" ]; then
        current_version="1.2.9"
        print_warning "Could not determine version, using fallback: $current_version"
    fi
    
    print_info "Creating/updating agent version: $current_version"
    print_info "This will ensure the latest agent script is available in the database"
    
    # Test connection before creating agent version
    if ! PGPASSWORD="$DB_PASS" psql -h localhost -U "$DB_USER" -d "$DB_NAME" -c "SELECT 1;" >/dev/null 2>&1; then
        print_error "Cannot connect to database before creating agent version"
        exit 1
    fi
    
    # Copy agent script to backend directory
    if [ -f "$APP_DIR/agents/patchmon-agent.sh" ]; then
        cp "$APP_DIR/agents/patchmon-agent.sh" "$APP_DIR/backend/"
        
        print_status "Agent version management removed - using file-based approach"
# Ensure we close the conditional and the function properly
    fi

    return 0
}
# Create deployment summary
create_deployment_summary() {
    print_info "Writing deployment summary into deployment-info.txt..."
    
    # Reuse the unified deployment info file
    SUMMARY_FILE="$APP_DIR/deployment-info.txt"
    
    cat >> "$SUMMARY_FILE" << EOF

----------------------------------------------------
        Deployment Summary (Appended)
----------------------------------------------------

Deployment Information:
- Email: $EMAIL
- Branch: $DEPLOYMENT_BRANCH
- Deployed: $(date)
- Deployment Duration: $(($(date +%s) - $DEPLOYMENT_START_TIME)) seconds

Service Status:
- PatchMon Service: $(systemctl is-active $SERVICE_NAME)
- Nginx Service: $(systemctl is-active nginx)
- PostgreSQL Service: $(systemctl is-active postgresql)
- SSL Certificate: $(if [ "$USE_LETSENCRYPT" = "true" ]; then echo "Enabled"; else echo "Disabled"; fi)

Diagnostic Commands:
- Service Status: systemctl status $SERVICE_NAME
- Service Logs: journalctl -u $SERVICE_NAME -f
- Nginx Status: systemctl status nginx
- Nginx Logs: journalctl -u nginx -f
- Database Status: systemctl status postgresql
- SSL Certificate: certbot certificates
- Disk Usage: df -h $APP_DIR
- Process Status: ps aux | grep $SERVICE_NAME

Troubleshooting:
- Check deployment log: cat $APP_DIR/patchmon-install.log
- Check service logs: journalctl -u $SERVICE_NAME --since "1 hour ago"
- Check nginx config: nginx -t
- Check database connection: sudo -u $DB_USER psql -d $DB_NAME -c "SELECT 1;"
- Check port binding: netstat -tlnp | grep $BACKEND_PORT

====================================================
EOF

    # Ensure permissions
    chmod 644 "$SUMMARY_FILE"
    chown "$INSTANCE_USER:$INSTANCE_USER" "$SUMMARY_FILE"
    
    # Copy the entire installation log into the instance folder
    if [ -f "$INSTALL_LOG" ]; then
        cp "$INSTALL_LOG" "$APP_DIR/patchmon-install.log" || true
        chown "$INSTANCE_USER:$INSTANCE_USER" "$APP_DIR/patchmon-install.log" || true
        chmod 644 "$APP_DIR/patchmon-install.log" || true
    fi
    
    print_status "Unified deployment info saved to: $SUMMARY_FILE"
}

# Email notification function removed for self-hosting deployment

# Save deployment information to file
save_deployment_info() {
    print_info "Saving deployment information to file..."
    
    # Create deployment info file
    INFO_FILE="$APP_DIR/deployment-info.txt"
    
    cat > "$INFO_FILE" << EOF
====================================================
        PatchMon Deployment Information
====================================================

Instance Details:
- FQDN: $FQDN
- URL: $SERVER_PROTOCOL_SEL://$FQDN
- Deployed: $(date)
- Deployment Type: $(if [ "$USE_LETSENCRYPT" = "true" ]; then echo "Public with SSL"; else echo "Local/Internal"; fi)
- SSL Enabled: $USE_LETSENCRYPT
- Service Name: $SERVICE_NAME

Directories:
- App Directory: $APP_DIR
- Backend: $APP_DIR/backend
- Frontend (built): $APP_DIR/frontend/dist
- Node.js isolation dir: $APP_DIR/.npm

Database Information:
- Name: $DB_NAME
- User: $DB_USER
- Password: $DB_PASS
- Host: localhost
- Port: 5432

Networking:
- Backend Port: $BACKEND_PORT
- Nginx Config: /etc/nginx/sites-available/$FQDN

Logs & Files:
- Deployment Log: $LOG_FILE
- Systemd Service: /etc/systemd/system/$SERVICE_NAME.service

Common Commands:
- Restart backend service: sudo systemctl restart $SERVICE_NAME
- Check backend status:   systemctl status $SERVICE_NAME
- Tail backend logs:      journalctl -u $SERVICE_NAME -f
- Test nginx config:      nginx -t && systemctl reload nginx
- Check DB connection:    sudo -u $DB_USER psql -d $DB_NAME -c "SELECT 1;"

First-Time Setup:
- Visit the web interface: $SERVER_PROTOCOL_SEL://$FQDN
- Create the admin account through the web UI (no pre-created credentials)

Notes:
- Default role permissions (admin/user) are created automatically on backend startup
- Keep this file for future reference of your environment

====================================================
EOF

    # Set permissions (readable by root and instance user)
    chmod 644 "$INFO_FILE"
    chown "$INSTANCE_USER:$INSTANCE_USER" "$INFO_FILE"
    
    print_status "Deployment information saved to: $INFO_FILE"
}

# Restart PatchMon service
restart_patchmon() {
    print_info "Restarting PatchMon service..."
    
    # Restart PatchMon service
    systemctl restart "$SERVICE_NAME"
    
    # Wait for service to restart
    sleep 5
    
    # Check if service is running
    if systemctl is-active --quiet "$SERVICE_NAME"; then
        print_status "PatchMon service restarted successfully"
    else
        print_error "Failed to restart PatchMon service"
        systemctl status "$SERVICE_NAME"
        return 1
    fi
}

# Setup logging for deployment
setup_deployment_logging() {
    echo "[$(date '+%Y-%m-%d %H:%M:%S')] setup_deployment_logging function started" >> "$DEBUG_LOG"
    
    print_info "Setting up deployment logging..."
    
    echo "[$(date '+%Y-%m-%d %H:%M:%S')] APP_DIR variable: $APP_DIR" >> "$DEBUG_LOG"
    
    # Use the main installation log file
    LOG_FILE="$INSTALL_LOG"
    
    echo "[$(date '+%Y-%m-%d %H:%M:%S')] Using main log file: $LOG_FILE" >> "$DEBUG_LOG"
    
    print_info "Deployment log: $LOG_FILE"
    
    # Function to log with timestamp
    log_output() {
        echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1" >> "$LOG_FILE"
    }
    
    # Redirect all output to both terminal and log file
    exec > >(tee -a "$LOG_FILE")
    exec 2>&1
    
    log_output "=== PatchMon Deployment Started ==="
    log_output "Script started at: $(date)"
    log_output "Script PID: $$"
    log_output "Running as user: $(whoami)"
    log_output "Current directory: $(pwd)"
    log_output "Script arguments: $@"
    log_output "FQDN: $FQDN"
    log_output "Email: $EMAIL"
    log_output "Branch: $DEPLOYMENT_BRANCH"
    log_output "SSL Enabled: $USE_LETSENCRYPT"
    log_output "====================================="
}

# Main deployment function
deploy_instance() {
    echo "[$(date '+%Y-%m-%d %H:%M:%S')] deploy_instance function started" >> "$DEBUG_LOG"
    
    log_message "=== SELF-HOSTING-INSTALL.SH DEPLOYMENT STARTED ==="
    log_message "Script version: $SCRIPT_VERSION"
    log_message "FQDN: $FQDN"
    log_message "Email: $EMAIL"
    log_message "SSL Enabled: $USE_LETSENCRYPT"
    
    print_banner
    
    echo "[$(date '+%Y-%m-%d %H:%M:%S')] Skipping early logging setup - will do after variables initialized" >> "$DEBUG_LOG"
    
    # Record deployment start time
    DEPLOYMENT_START_TIME=$(date +%s)
    
    echo "[$(date '+%Y-%m-%d %H:%M:%S')] About to validate parameters" >> "$DEBUG_LOG"
    
    # Parameters are already validated in interactive_setup
    print_info "All parameters validated successfully"
    
    echo "[$(date '+%Y-%m-%d %H:%M:%S')] Parameter validation passed" >> "$DEBUG_LOG"
    
    echo "[$(date '+%Y-%m-%d %H:%M:%S')] Checking if instance already exists at /opt/$FQDN" >> "$DEBUG_LOG"
    
    # Check if instance already exists
    if [ -d "/opt/$FQDN" ]; then
        print_error "Instance for $FQDN already exists at /opt/$FQDN"
        echo "[$(date '+%Y-%m-%d %H:%M:%S')] ERROR: Instance already exists" >> "$DEBUG_LOG"
        exit 1
    fi
    
    echo "[$(date '+%Y-%m-%d %H:%M:%S')] Instance check passed - no existing instance found" >> "$DEBUG_LOG"
    
    print_info "🚀 Deploying PatchMon instance for $FQDN"
    print_info "📧 Email: $EMAIL"
    print_info "🌿 Branch: $DEPLOYMENT_BRANCH"
    print_info "🔒 SSL: $USE_LETSENCRYPT"
    if [ "$USE_LETSENCRYPT" = "true" ]; then
        print_info "📧 SSL Email: $EMAIL"
    fi
    
    echo "[$(date '+%Y-%m-%d %H:%M:%S')] About to call init_instance_vars function" >> "$DEBUG_LOG"
    
    # Initialize variables
    init_instance_vars
    
    echo "[$(date '+%Y-%m-%d %H:%M:%S')] init_instance_vars function completed" >> "$DEBUG_LOG"
    echo "[$(date '+%Y-%m-%d %H:%M:%S')] Variables initialized, APP_DIR: $APP_DIR" >> "$DEBUG_LOG"
    
    # Setup logging (after variables are initialized)
    setup_deployment_logging
    
    echo "[$(date '+%Y-%m-%d %H:%M:%S')] Deployment logging setup completed" >> "$DEBUG_LOG"
    
    # Display generated credentials
    echo -e "${BLUE}🔐 Auto-generated credentials:${NC}"
    echo -e "${YELLOW}Database Name: $DB_NAME${NC}"
    echo -e "${YELLOW}Database User: $DB_USER${NC}"
    echo -e "${YELLOW}Database Password: $DB_PASS${NC}"
    echo -e "${YELLOW}JWT Secret: $JWT_SECRET${NC}"
    echo -e "${YELLOW}Backend Port: $BACKEND_PORT${NC}"
    echo -e "${YELLOW}Instance User: $INSTANCE_USER${NC}"
    echo -e "${YELLOW}Node.js Isolation: $APP_DIR/.npm${NC}"
    echo ""
    
    # System setup (prerequisites already installed in interactive_setup)
    install_nodejs
    install_postgresql
    install_nginx
    
    # Only install certbot if SSL is enabled
    if [ "$USE_LETSENCRYPT" = "true" ]; then
        install_certbot
    fi
    
    # Instance-specific setup
    create_instance_user
    setup_nodejs_isolation
    setup_database
    clone_application
    setup_node_environment
    install_dependencies
    create_env_files
    run_migrations
    # Admin account creation removed - handled by application's first-time setup
    
    # Service and web server setup
    create_systemd_service
    setup_nginx
    
    # SSL setup (if enabled)
    if [ "$USE_LETSENCRYPT" = "true" ]; then
        setup_letsencrypt
    else
        print_info "SSL disabled - skipping SSL certificate setup"
    fi
    
    # Start services
    start_services
    
    # Populate server settings in database
    populate_server_settings
    
    # Create agent version in database
    create_agent_version
    
    # Restart PatchMon service to ensure it's running properly
    restart_patchmon
    
    # Save deployment information to file
    save_deployment_info
    
    # Create deployment summary
    create_deployment_summary
    
    # Email notifications removed for self-hosting deployment
    
    # Final status
    log_message "=== DEPLOYMENT COMPLETED SUCCESSFULLY ==="
    log_message "Instance URL: $SERVER_PROTOCOL_SEL://$FQDN"
    log_message "Service name: $SERVICE_NAME"
    log_message "Backend port: $BACKEND_PORT"
    log_message "SSL enabled: $USE_LETSENCRYPT"
    
    print_status "🎉 PatchMon instance deployed successfully!"
    echo ""
    print_info "Next steps:"
    echo "  • Visit your URL: $SERVER_PROTOCOL_SEL://$FQDN (ensure DNS is configured)"
    echo "  • Useful deployment information is stored in: $APP_DIR/deployment-info.txt"
    echo ""
    
    # Suppress JSON echo to terminal; details already logged and saved to summary/credentials files
    :
}

# Detect existing PatchMon installations
detect_installations() {
    local installations=()
    
    # Find all directories in /opt that contain PatchMon installations
    if [ -d "/opt" ]; then
        for dir in /opt/*/; do
            local dirname=$(basename "$dir")
            # Skip backup directories
            if [[ "$dirname" =~ \.backup\. ]]; then
                continue
            fi
            # Check if it's a PatchMon installation
            if [ -f "$dir/backend/package.json" ] && grep -q "patchmon" "$dir/backend/package.json" 2>/dev/null; then
                installations+=("$dirname")
            fi
        done
    fi
    
    echo "${installations[@]}"
}

# Select installation to update
select_installation_to_update() {
    local installations=($(detect_installations))
    
    if [ ${#installations[@]} -eq 0 ]; then
        print_error "No existing PatchMon installations found in /opt"
        exit 1
    fi
    
    print_info "Found ${#installations[@]} existing installation(s):"
    echo ""
    
    local i=1
    declare -A install_map
    for install in "${installations[@]}"; do
        # Get current version if possible
        local version="unknown"
        if [ -f "/opt/$install/backend/package.json" ]; then
            version=$(grep '"version"' "/opt/$install/backend/package.json" | head -1 | sed 's/.*"version": "\([^"]*\)".*/\1/')
        fi
        
        # Get service status - try multiple naming conventions
        # Convention 1: Just the install name (e.g., patchmon.internal)
        local service_name="$install"
        # Convention 2: patchmon. prefix (e.g., patchmon.patchmon.internal)
        local alt_service_name1="patchmon.$install"
        # Convention 3: patchmon- prefix with underscores (e.g., patchmon-patchmon_internal)
        local alt_service_name2="patchmon-$(echo "$install" | tr '.' '_')"
        local status="unknown"
        
        # Try convention 1 first (most common)
        if systemctl is-active --quiet "$service_name" 2>/dev/null; then
            status="running"
        elif systemctl is-enabled --quiet "$service_name" 2>/dev/null; then
            status="stopped"
        # Try convention 2
        elif systemctl is-active --quiet "$alt_service_name1" 2>/dev/null; then
            status="running"
            service_name="$alt_service_name1"
        elif systemctl is-enabled --quiet "$alt_service_name1" 2>/dev/null; then
            status="stopped"
            service_name="$alt_service_name1"
        # Try convention 3
        elif systemctl is-active --quiet "$alt_service_name2" 2>/dev/null; then
            status="running"
            service_name="$alt_service_name2"
        elif systemctl is-enabled --quiet "$alt_service_name2" 2>/dev/null; then
            status="stopped"
            service_name="$alt_service_name2"
        fi
        
        printf "%2d. %-30s (v%-10s - %s)\n" "$i" "$install" "$version" "$status"
        install_map[$i]="$install"
        # Store the service name for later use
        declare -g "service_map_$i=$service_name"
        i=$((i + 1))
    done
    
    echo ""
    
    while true; do
        read_input "Select installation number to update" SELECTION "1"
        
        if [[ "$SELECTION" =~ ^[0-9]+$ ]] && [ -n "${install_map[$SELECTION]}" ]; then
            SELECTED_INSTANCE="${install_map[$SELECTION]}"
            # Get the stored service name
            local varname="service_map_$SELECTION"
            SELECTED_SERVICE_NAME="${!varname}"
            print_status "Selected: $SELECTED_INSTANCE"
            print_info "Service: $SELECTED_SERVICE_NAME"
            return 0
        else
            print_error "Invalid selection. Please enter a number from 1 to ${#installations[@]}"
        fi
    done
}

# Update existing installation
update_installation() {
    local instance_dir="/opt/$SELECTED_INSTANCE"
    local service_name="$SELECTED_SERVICE_NAME"
    
    print_info "Updating PatchMon installation: $SELECTED_INSTANCE"
    print_info "Installation directory: $instance_dir"
    print_info "Service name: $service_name"
    
    # Verify it's a git repository
    if [ ! -d "$instance_dir/.git" ]; then
        print_error "Installation directory is not a git repository"
        print_error "Cannot perform git-based update"
        exit 1
    fi
    
    # Add git safe.directory to avoid ownership issues when running as root
    print_info "Configuring git safe.directory..."
    git config --global --add safe.directory "$instance_dir" 2>/dev/null || true
    
    # Load existing .env to get database credentials
    if [ -f "$instance_dir/backend/.env" ]; then
        source "$instance_dir/backend/.env"
        print_status "Loaded existing configuration"
        
        # Parse DATABASE_URL to extract credentials
        # Format: postgresql://user:password@host:port/database
        if [ -n "$DATABASE_URL" ]; then
            # Extract components using regex
            DB_USER=$(echo "$DATABASE_URL" | sed -n 's|postgresql://\([^:]*\):.*|\1|p')
            DB_PASS=$(echo "$DATABASE_URL" | sed -n 's|postgresql://[^:]*:\([^@]*\)@.*|\1|p')
            DB_HOST=$(echo "$DATABASE_URL" | sed -n 's|.*@\([^:]*\):.*|\1|p')
            DB_PORT=$(echo "$DATABASE_URL" | sed -n 's|.*:\([0-9]*\)/.*|\1|p')
            DB_NAME=$(echo "$DATABASE_URL" | sed -n 's|.*/\([^?]*\).*|\1|p')
            
            print_info "Database: $DB_NAME (user: $DB_USER)"
        else
            print_error "DATABASE_URL not found in .env file"
            exit 1
        fi
    else
        print_error "Cannot find .env file at $instance_dir/backend/.env"
        exit 1
    fi
    
    # Select branch/version to update to
    select_branch
    
    print_info "Updating to: $DEPLOYMENT_BRANCH"
    echo ""
    
    read_yes_no "Proceed with update? This will pull new code and restart services" CONFIRM_UPDATE "y"
    
    if [ "$CONFIRM_UPDATE" != "y" ]; then
        print_warning "Update cancelled by user"
        exit 0
    fi
    
    # Stop the service
    print_info "Stopping service: $service_name"
    systemctl stop "$service_name" || true
    
    # Create backup directory
    local timestamp=$(date +%Y%m%d_%H%M%S)
    local backup_dir="$instance_dir.backup.$timestamp"
    local db_backup_file="$backup_dir/database_backup_$timestamp.sql"
    
    print_info "Creating backup directory: $backup_dir"
    mkdir -p "$backup_dir"
    
    # Backup database
    print_info "Backing up database: $DB_NAME"
    if PGPASSWORD="$DB_PASS" pg_dump -h "$DB_HOST" -U "$DB_USER" -d "$DB_NAME" -F c -f "$db_backup_file" 2>/dev/null; then
        print_status "Database backup created: $db_backup_file"
    else
        print_warning "Database backup failed, but continuing with code backup"
    fi
    
    # Backup code
    print_info "Backing up code files..."
    cp -r "$instance_dir" "$backup_dir/code"
    print_status "Code backup created"
    
    # Update code
    print_info "Pulling latest code from branch: $DEPLOYMENT_BRANCH"
    cd "$instance_dir"
    
    # Fetch latest changes
    git fetch origin
    
    # Checkout the selected branch/tag
    git checkout "$DEPLOYMENT_BRANCH"
    git pull origin "$DEPLOYMENT_BRANCH" || git pull # For tags, just pull
    
    print_status "Code updated successfully"
    
    # Update dependencies
    print_info "Updating backend dependencies..."
    cd "$instance_dir/backend"
    npm install --production --ignore-scripts
    
    print_info "Updating frontend dependencies..."
    cd "$instance_dir/frontend"
    npm install --ignore-scripts
    
    # Build frontend
    print_info "Building frontend..."
    npm run build
    
    # Run database migrations and generate Prisma client
    print_info "Running database migrations..."
    cd "$instance_dir/backend"
    npx prisma generate
    npx prisma migrate deploy
    
    # Start the service
    print_info "Starting service: $service_name"
    systemctl start "$service_name"
    
    # Wait a moment and check status
    sleep 3
    
    if systemctl is-active --quiet "$service_name"; then
        print_success "✅ Update completed successfully!"
        print_status "Service $service_name is running"
        
        # Get new version
        local new_version=$(grep '"version"' "$instance_dir/backend/package.json" | head -1 | sed 's/.*"version": "\([^"]*\)".*/\1/')
        print_info "Updated to version: $new_version"
        echo ""
        print_info "Backup Information:"
        print_info "  Code backup: $backup_dir/code"
        print_info "  Database backup: $db_backup_file"
        echo ""
        print_info "To restore database if needed:"
        print_info "  PGPASSWORD=\"$DB_PASS\" pg_restore -h \"$DB_HOST\" -U \"$DB_USER\" -d \"$DB_NAME\" -c \"$db_backup_file\""
        echo ""
    else
        print_error "Service failed to start after update"
        echo ""
        print_warning "ROLLBACK INSTRUCTIONS:"
        print_info "1. Restore code:"
        print_info "   sudo rm -rf $instance_dir"
        print_info "   sudo mv $backup_dir/code $instance_dir"
        echo ""
        print_info "2. Restore database:"
        print_info "   PGPASSWORD=\"$DB_PASS\" pg_restore -h \"$DB_HOST\" -U \"$DB_USER\" -d \"$DB_NAME\" -c \"$db_backup_file\""
        echo ""
        print_info "3. Restart service:"
        print_info "   sudo systemctl start $service_name"
        echo ""
        print_info "Check logs: journalctl -u $service_name -f"
        exit 1
    fi
}

# Main script execution
main() {
    # Parse command-line arguments
    if [ "$1" = "--update" ]; then
        UPDATE_MODE="true"
    fi
    
    # Log script entry
    echo "[$(date '+%Y-%m-%d %H:%M:%S')] Script started - Update mode: $UPDATE_MODE" >> "$DEBUG_LOG"
    
    # Handle update mode
    if [ "$UPDATE_MODE" = "true" ]; then
        print_banner
        print_info "🔄 PatchMon Update Mode"
        echo ""
        
        # Select installation to update
        select_installation_to_update
        
        # Perform update
        update_installation
        
        exit 0
    fi
    
    # Normal installation mode
    # Run interactive setup
    interactive_setup
    
    # Set GitHub repo (always use public repo for self-hosted deployments)
    GITHUB_REPO="$DEFAULT_GITHUB_REPO"
    
    # Validate SSL setting
    if [ "$SSL_ENABLED" = "y" ] || [ "$SSL_ENABLED" = "yes" ]; then
        USE_LETSENCRYPT="true"
        SERVER_PROTOCOL_SEL="https"
        print_info "SSL enabled - will use Let's Encrypt for HTTPS"
        
        # Validate email for SSL
        if [ -z "$EMAIL" ]; then
            print_error "Email is required when SSL is enabled for Let's Encrypt"
            exit 1
        fi
    else
        USE_LETSENCRYPT="false"
        SERVER_PROTOCOL_SEL="http"
        print_info "SSL disabled - will use HTTP only"
    fi
    
    # Log before calling deploy_instance
    echo "[$(date '+%Y-%m-%d %H:%M:%S')] About to call deploy_instance function" >> "$DEBUG_LOG"
    
    # Run deployment
    deploy_instance
    
    # Log after deploy_instance completes
    echo "[$(date '+%Y-%m-%d %H:%M:%S')] deploy_instance function completed" >> "$DEBUG_LOG"
}

# Show usage/help
show_usage() {
    echo "PatchMon Self-Hosting Installation & Update Script"
    echo "Version: $SCRIPT_VERSION"
    echo ""
    echo "Usage:"
    echo "  $0              # Interactive installation (default)"
    echo "  $0 --update     # Update existing installation"
    echo "  $0 --help       # Show this help message"
    echo ""
    echo "Examples:"
    echo "  # New installation:"
    echo "  sudo bash $0"
    echo ""
    echo "  # Update existing installation:"
    echo "  sudo bash $0 --update"
    echo ""
}

# Check for help flag
if [ "$1" = "--help" ] || [ "$1" = "-h" ]; then
    show_usage
    exit 0
fi

# Run main function
main "$@"
