#!/bin/bash
set -euo pipefail
# ==============================================================================
# Helper Functions: Logger & Health Checks
# ==============================================================================

# ANSI Color Codes
readonly COLOR_RESET='\033[0m'
readonly COLOR_BLUE='\033[34m'
readonly COLOR_GREEN='\033[32m'
readonly COLOR_YELLOW='\033[33m'
readonly COLOR_RED='\033[31m'
readonly COLOR_CYAN='\033[36m'
readonly COLOR_MAGENTA='\033[35m'

log_info() { echo -e "${COLOR_BLUE}[INFO]${COLOR_RESET} $*"; }
log_success() { echo -e "${COLOR_GREEN}[SUCCESS]${COLOR_RESET} $*"; }
log_warning() { echo -e "${COLOR_YELLOW}[WARNING]${COLOR_RESET} $*"; }
log_error() { echo -e "${COLOR_RED}[ERROR]${COLOR_RESET} $*" >&2; }
log_step() { echo -e "${COLOR_CYAN}[STEP]${COLOR_RESET} $*"; }
log_debug() {
    if [[ "${DEBUG:-0}" == "1" ]]; then
        echo -e "${COLOR_MAGENTA}[DEBUG]${COLOR_RESET} $*"
    fi
}
log_separator() { echo "================================================================"; }
log_header() {
    echo ""
    log_separator
    echo -e "       ${COLOR_CYAN}$*${COLOR_RESET}"
    log_separator
    echo ""
}

confirm() {
    local prompt="$1"
    local response
    while true; do
        echo -ne "${COLOR_MAGENTA}[PROMPT]${COLOR_RESET} $prompt (y/n): "
        read -r response
        case "$response" in
            [yY][eE][sS] | [yY]) return 0 ;;
            [nN][oO] | [nN]) return 1 ;;
            *) log_warning "Please answer 'y' or 'n'" ;;
        esac
    done
}

prompt_input() {
    local prompt="$1"
    local default="$2"
    local response

    if [[ -n "$default" ]]; then
        echo -ne "${COLOR_MAGENTA}[PROMPT]${COLOR_RESET} $prompt (default: $default): "
    else
        echo -ne "${COLOR_MAGENTA}[PROMPT]${COLOR_RESET} $prompt: "
    fi

    read -r response

    if [[ -z "$response" ]] && [[ -n "$default" ]]; then
        echo "$default"
    else
        echo "$response"
    fi
}

die() {
    log_error "$*"
    exit 1
}

warn_and_continue() {
    log_warning "$*"
}

check_port() {
    local port=$1
    local timeout=${2:-5}
    if nc -z -w "$timeout" localhost "$port" 2>/dev/null; then
        return 0
    else
        return 1
    fi
}

check_http() {
    local url=$1
    local timeout=${2:-5}
    if curl -f --silent --max-time "$timeout" "$url" >/dev/null 2>&1; then
        return 0
    else
        return 1
    fi
}

check_service() {
    local service_name=$1
    if [[ -n "${INSTALL_USER:-}" ]]; then
        if sudo -H -u "$INSTALL_USER" bash -c "cd '/home/$INSTALL_USER' && pm2 status '$service_name'" 2>/dev/null | grep -q "online"; then
            return 0
        fi
    fi
    if command -v pm2 &>/dev/null; then
        pm2 status "$service_name" 2>/dev/null | grep -q "online"
        return $?
    fi
    return 1
}

check_nginx() {
    if systemctl is-active --quiet nginx 2>/dev/null; then
        return 0
    elif pgrep nginx >/dev/null 2>&1; then
        return 0
    else
        return 1
    fi
}

check_database() {
    local db_path=$1
    if [[ ! -f "$db_path" ]]; then
        log_error "Database file not found: $db_path"
        return 1
    fi
    if sqlite3 "$db_path" "SELECT 1;" >/dev/null 2>&1; then
        return 0
    else
        return 1
    fi
}

run_health_checks() {
    local api_port=${1:-4500}
    local web_port=${2:-80}
    local db_path=${3:-}
    local check_count=0
    local passed_count=0

    log_header "Running Health Checks"

    check_count=$((check_count + 1))
    if check_service "smart-home-api"; then
        log_success "PM2 Process 'smart-home-api': online"
        passed_count=$((passed_count + 1))
    else
        log_error "PM2 Process 'smart-home-api': not running"
    fi

    check_count=$((check_count + 1))
    if check_http "http://localhost:${api_port}/health" 10; then
        log_success "API Health Endpoint (http://localhost:${api_port}/health): OK"
        passed_count=$((passed_count + 1))
    else
        log_error "API Health Endpoint (http://localhost:${api_port}/health): FAILED"
    fi

    check_count=$((check_count + 1))
    if check_port "$api_port" 5; then
        log_success "API Port ${api_port}: listening"
        passed_count=$((passed_count + 1))
    else
        log_error "API Port ${api_port}: not listening"
    fi

    check_count=$((check_count + 1))
    if check_port "$web_port" 5; then
        log_success "Web Portal Port ${web_port}: listening"
        passed_count=$((passed_count + 1))
    else
        log_error "Web Portal Port ${web_port}: not listening"
    fi

    check_count=$((check_count + 1))
    if check_nginx; then
        log_success "Nginx: running"
        passed_count=$((passed_count + 1))
    else
        log_error "Nginx: not running"
    fi

    if [[ -n "$db_path" ]]; then
        check_count=$((check_count + 1))
        if check_database "$db_path"; then
            log_success "Database: accessible"
            passed_count=$((passed_count + 1))
        else
            log_error "Database: not accessible"
        fi
    fi

    echo ""
    if [[ $passed_count -eq $check_count ]]; then
        log_success "All health checks passed: $passed_count/$check_count"
        return 0
    else
        log_warning "Some health checks failed: $passed_count/$check_count"
        return 1
    fi
}

display_service_summary() {
    local install_dir=$1
    local web_port=${2:-80}
    local api_port=${3:-4500}
    local local_ip=""

    if command -v hostname &>/dev/null; then
        local_ip=$(hostname -I | awk '{print $1}')
    fi
    if [[ -z "$local_ip" ]]; then
        local_ip="192.168.x.x"
    fi

    log_header "Smart Home System Installation Complete"
    echo "Access Your System:"
    echo ""
    echo "  Web Portal:  http://${local_ip}/"
    echo "  API Docs:    http://${local_ip}/api/docs"
    echo "  API Health:  http://${local_ip}/health"
    echo ""
    echo "PM2 Management:"
    echo "  Monitor:     pm2 monit"
    echo "  View Logs:   pm2 logs smart-home-api"
    echo "  Restart:     pm2 restart smart-home-api"
    echo "  Stop:        pm2 stop smart-home-api"
    echo "  Start:       pm2 start smart-home-api"
    echo ""
    echo "Installation Directory:"
    echo "  $install_dir"
    echo ""
    log_separator
}
generate_nginx_config() {
    local api_port=${1:-4500}
    local web_root=${2:-}
    local output_file=${3:-}

    if [[ -z "$web_root" ]] || [[ -z "$output_file" ]]; then
        log_error "generate_nginx_config requires web_root and output_file"
        return 1
    fi

    cat > "$output_file" << 'EOF'
# ============================================================
# Smart Home - Frontend
# ============================================================
server {
    listen 80;
    listen [::]:80;

    server_name _;

    root WEB_ROOT_PLACEHOLDER;

    client_max_body_size 100M;

    access_log /var/log/nginx/smart-home-frontend-access.log combined;
    error_log  /var/log/nginx/smart-home-frontend-error.log warn;

    # --------------------------------------------------------
    # Security headers
    # --------------------------------------------------------
    add_header X-Frame-Options "SAMEORIGIN" always;
    add_header X-Content-Type-Options "nosniff" always;
    add_header X-XSS-Protection "1; mode=block" always;
    add_header Referrer-Policy "no-referrer-when-downgrade" always;

    # --------------------------------------------------------
    # React SPA
    # --------------------------------------------------------
    location / {
        try_files $uri $uri/ /index.html;
    }

    # --------------------------------------------------------
    # Static assets
    # --------------------------------------------------------
    location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf|eot)$ {
        expires 365d;
        add_header Cache-Control "public, immutable";
    }

    # --------------------------------------------------------
    # React entry point should not be cached
    # --------------------------------------------------------
    location = /index.html {
        add_header Cache-Control "no-cache, must-revalidate";
    }

    # --------------------------------------------------------
    # Block hidden files
    # --------------------------------------------------------
    location ~ /\. {
        deny all;
        access_log off;
        log_not_found off;
    }

    # --------------------------------------------------------
    # Block backup files
    # --------------------------------------------------------
    location ~ ~$ {
        deny all;
        access_log off;
        log_not_found off;
    }
}


# ============================================================
# Smart Home - Backend API
# ============================================================
server {
    listen 4500;
    listen [::]:4500;

    server_name _;

    client_max_body_size 100M;

    access_log /var/log/nginx/smart-home-api-access.log combined;
    error_log  /var/log/nginx/smart-home-api-error.log warn;

    # --------------------------------------------------------
    # Health check
    # --------------------------------------------------------
    location = /health {
        proxy_pass http://127.0.0.1:3000/health;

        proxy_http_version 1.1;

        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_set_header X-Forwarded-Host $host;
        proxy_set_header X-Forwarded-Port $server_port;

        proxy_connect_timeout 10s;
        proxy_send_timeout 60s;
        proxy_read_timeout 60s;

        access_log off;
    }

    # --------------------------------------------------------
    # Backend API
    #
    # IMPORTANT:
    # proxy_pass has NO trailing slash.
    #
    # /api/users -> http://127.0.0.1:3000/api/users
    # --------------------------------------------------------
    location / {
        proxy_pass http://127.0.0.1:3000;

        proxy_http_version 1.1;

        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_set_header X-Forwarded-Host $host;
        proxy_set_header X-Forwarded-Port $server_port;

        # WebSocket support
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";

        proxy_connect_timeout 60s;
        proxy_send_timeout 60s;
        proxy_read_timeout 60s;

        # CRITICAL: Disable buffering for WebSocket!
        # Buffering breaks bidirectional streaming required for real-time updates
        proxy_buffering off;
        proxy_request_buffering off;
    }

    # --------------------------------------------------------
    # Block hidden files
    # --------------------------------------------------------
    location ~ /\. {
        deny all;
        access_log off;
        log_not_found off;
    }

    # --------------------------------------------------------
    # Block backup files
    # --------------------------------------------------------
    location ~ ~$ {
        deny all;
        access_log off;
        log_not_found off;
    }
}
EOF

    sed -i "s|WEB_ROOT_PLACEHOLDER|${web_root}|g" "$output_file"
    sed -i "s|API_PORT_PLACEHOLDER|${api_port}|g" "$output_file"

    if [[ -f "$output_file" ]]; then
        log_success "Nginx configuration generated: $output_file"
        return 0
    else
        log_error "Failed to generate nginx configuration"
        return 1
    fi
}

validate_nginx_config() {
    if ! command -v nginx &>/dev/null && [[ ! -x /usr/sbin/nginx ]]; then
        log_error "nginx not found"
        return 1
    fi

    local nginx_cmd="nginx"
    [[ ! -x /usr/sbin/nginx ]] || nginx_cmd="/usr/sbin/nginx"

    if $nginx_cmd -t 2>&1 | grep -q "successful"; then
        log_success "Nginx configuration is valid"
        return 0
    else
        log_error "Nginx configuration is invalid"
        $nginx_cmd -t
        return 1
    fi
}

install_nginx_config() {
    local config_file=$1
    local nginx_available="/etc/nginx/sites-available/smart-home"
    local nginx_enabled="/etc/nginx/sites-enabled/smart-home"

    if [[ ! -f "$config_file" ]]; then
        log_error "Configuration file not found: $config_file"
        return 1
    fi

    log_step "Installing Nginx configuration..."

    if ! sudo cp "$config_file" "$nginx_available"; then
        log_error "Failed to copy nginx configuration"
        return 1
    fi

    if [[ ! -L "$nginx_enabled" ]]; then
        if ! sudo ln -s "$nginx_available" "$nginx_enabled"; then
            log_error "Failed to enable nginx site"
            return 1
        fi
    fi

    if ! validate_nginx_config; then
        sudo rm -f "$nginx_enabled"
        sudo rm -f "$nginx_available"
        return 1
    fi

    # Disable default site if present
    sudo rm -f /etc/nginx/sites-enabled/default

    # Ensure Nginx is running and config is applied
    if pgrep nginx >/dev/null 2>&1 || sudo systemctl is-active --quiet nginx 2>/dev/null; then
        if sudo systemctl reload nginx 2>/dev/null || sudo service nginx reload 2>/dev/null || sudo /usr/sbin/nginx -s reload 2>/dev/null; then
            log_success "Nginx reloaded successfully"
            return 0
        fi
    fi

    if sudo systemctl restart nginx 2>/dev/null || sudo service nginx restart 2>/dev/null || sudo systemctl start nginx 2>/dev/null || sudo service nginx start 2>/dev/null || sudo /usr/sbin/nginx 2>/dev/null; then
        log_success "Nginx started successfully"
        return 0
    else
        log_error "Failed to start/reload nginx"
        return 1
    fi
}
ADMIN_USER="lalotech"
ADMIN_PASSWORD=""
INSTALL_USER="smarthome"
INSTALL_DIR=""
MANIFEST_URL="https://releases.lalotech.net/smart-home/manifest.json"
TARGET_VERSION=""
API_PORT=4500
WEB_PORT=80
SKIP_BACKUP=false
SKIP_NGINX=false
SKIP_FIREWALL=false
SKIP_API_DEPS=false
NON_INTERACTIVE=false
MANIFEST_VERSION=""
MANIFEST_BACKEND_URL=""
MANIFEST_BACKEND_CHECKSUM=""
MANIFEST_FRONTEND_URL=""
MANIFEST_FRONTEND_CHECKSUM=""
BACKEND_ZIP_PATH="/tmp/smart-home-api-$$.zip"
FRONTEND_ZIP_PATH="/tmp/smart-home-web-$$.zip"

parse_arguments() {
    while [[ $# -gt 0 ]]; do
        case "$1" in
            --user) INSTALL_USER="$2"; shift 2 ;;
            --user=*) INSTALL_USER="${1#*=}"; shift ;;
            --admin-password) ADMIN_PASSWORD="$2"; shift 2 ;;
            --install-dir) INSTALL_DIR="$2"; shift 2 ;;
            --install-dir=*) INSTALL_DIR="${1#*=}"; shift ;;
            --manifest-url) MANIFEST_URL="$2"; shift 2 ;;
            --manifest-url=*) MANIFEST_URL="${1#*=}"; shift ;;
            --server-url)
                local base="$2"
                [[ "$base" == */ ]] && base="${base%/}"
                if [[ "$base" == *.json ]]; then
                    MANIFEST_URL="$base"
                else
                    MANIFEST_URL="${base}/manifest.json"
                fi
                shift 2
                ;;
            --server-url=*)
                local base="${1#*=}"
                [[ "$base" == */ ]] && base="${base%/}"
                if [[ "$base" == *.json ]]; then
                    MANIFEST_URL="$base"
                else
                    MANIFEST_URL="${base}/manifest.json"
                fi
                shift
                ;;
            --version) TARGET_VERSION="$2"; shift 2 ;;
            --version=*) TARGET_VERSION="${1#*=}"; shift ;;
            --api-port) API_PORT="$2"; shift 2 ;;
            --api-port=*) API_PORT="${1#*=}"; shift ;;
            --web-port) WEB_PORT="$2"; shift 2 ;;
            --web-port=*) WEB_PORT="${1#*=}"; shift ;;
            --skip-nginx) SKIP_NGINX=true; shift ;;
            --skip-firewall) SKIP_FIREWALL=true; shift ;;
            --skip-backup) SKIP_BACKUP=true; shift ;;
            --skip-api-deps) SKIP_API_DEPS=true; shift ;;
            --non-interactive) NON_INTERACTIVE=true; shift ;;
            --help|-h)
                printf "Usage: %s [OPTIONS]\n\n" "$0"
                printf "Options:\n"
                printf "  --manifest-url <url>  URL to manifest.json (default: %s)\n" "$MANIFEST_URL"
                printf "  --server-url <url>    Base server URL (auto-appends /manifest.json)\n"
                printf "  --user <name>         System user (default: %s)\n" "$INSTALL_USER"
                printf "  --install-dir <path>  Installation directory (default: /home/<user>/smarthome)\n"
                printf "  --version <version>   Target release version\n"
                printf "  --api-port <port>     Backend API port (default: %s)\n" "$API_PORT"
                printf "  --web-port <port>     Frontend Web port (default: %s)\n" "$WEB_PORT"
                printf "  --skip-nginx          Skip Nginx configuration\n"
                printf "  --skip-firewall       Skip UFW firewall configuration\n"
                printf "  --skip-backup         Skip backup during updates\n"
                printf "  --skip-api-deps       Skip npm install for backend dependencies\n"
                printf "  --non-interactive     Run non-interactively without prompts\n"
                printf "  --help, -h            Show this help\n"
                exit 0
                ;;
            *) log_error "Unknown option: $1"; exit 1 ;;
        esac
    done
    [[ -z "$MANIFEST_URL" ]] && die "Missing manifest URL (--manifest-url or --server-url required)"
    [[ -z "$ADMIN_PASSWORD" ]] && die "Missing admin password (--admin-password required)"
    [[ -z "$INSTALL_DIR" ]] && INSTALL_DIR="/home/${INSTALL_USER}/smarthome"
}
load_manifest() {
 local m="/tmp/manifest-$$.json"
 log_step "Loading manifest from $MANIFEST_URL..."
 curl -fsSL "$MANIFEST_URL" -o "$m" || die "Failed to download"
 [[ -f "$m" ]] && parse_manifest "$m"
 rm -f "$m"
 log_success "Manifest loaded: version ${MANIFEST_VERSION}"
}
parse_manifest() {
 if command -v jq &>/dev/null; then
  MANIFEST_VERSION=$(jq -r '.version' "$1")
  MANIFEST_BACKEND_URL=$(jq -r '.backend.url' "$1")
  MANIFEST_BACKEND_CHECKSUM=$(jq -r '.backend.checksum' "$1")
  MANIFEST_FRONTEND_URL=$(jq -r '.frontend.url' "$1")
  MANIFEST_FRONTEND_CHECKSUM=$(jq -r '.frontend.checksum' "$1")
  MANIFEST_ROLLBACK_URL=$(jq -r '.rollback.url // .installer.rollbackUrl // empty' "$1" 2>/dev/null || echo "")
  MANIFEST_ROLLBACK_CHECKSUM=$(jq -r '.rollback.checksum // .installer.rollbackChecksum // empty' "$1" 2>/dev/null || echo "")
 else
  MANIFEST_VERSION=$(grep '"version"' "$1" | head -1 | grep -o '[0-9]\+\.[0-9]\+\.[0-9]\+' || echo "1.0.0")
  MANIFEST_BACKEND_URL=$(grep -o 'https\?://[^"]*' "$1" | head -1)
  MANIFEST_BACKEND_CHECKSUM=$(grep -o 'sha256:[a-f0-9]\+' "$1" | head -1 || echo "")
  MANIFEST_FRONTEND_URL=$(grep -o 'https\?://[^"]*' "$1" | tail -1)
  MANIFEST_FRONTEND_CHECKSUM=$(grep -o 'sha256:[a-f0-9]\+' "$1" | tail -1 || echo "")
  MANIFEST_ROLLBACK_URL=$(grep -A 2 '"rollback"' "$1" | grep -o 'https\?://[^"]*' | head -1 || echo "")
  MANIFEST_ROLLBACK_CHECKSUM=$(grep -A 2 '"rollback"' "$1" | grep -o 'sha256:[a-f0-9]\+' | head -1 || echo "")
 fi
}
detect_installation() { [[ -d "$INSTALL_DIR/smart-home-api" ]]; }
read_current_version() {
 local p="$INSTALL_DIR/smart-home-api/package.json"
 [[ ! -f "$p" ]] && echo "unknown" && return
 if command -v jq &>/dev/null; then
  jq -r '.version' "$p" 2>/dev/null || echo "unknown"
 else
  grep '"version"' "$p" | grep -o '[0-9.]*' | head -1
 fi
}
display_configuration() {
 log_header "Installation Configuration"
 if [[ $1 -eq 1 ]]; then
  echo "Type: UPDATE ($(read_current_version) → ${MANIFEST_VERSION})"
 else
  echo "Type: FRESH INSTALL"
 fi
 echo "User: $INSTALL_USER | Dir: $INSTALL_DIR | API:$API_PORT | Web:$WEB_PORT"
 echo "skip-api-deps: $SKIP_API_DEPS | skip-nginx: $SKIP_NGINX | skip-firewall: $SKIP_FIREWALL | skip-backup: $SKIP_BACKUP"
 log_separator
}
check_system_requirements() {
 log_step "Checking system requirements..."
 [[ $EUID -ne 0 ]] && die "This script must be run as root or with sudo"
 log_success "System requirements: OK"
}
install_system_dependencies() {
 log_step "Installing system dependencies..."
 sudo apt-get update -qq || true
 local packages=("curl" "wget" "unzip" "sqlite3" "build-essential" "python3" "netcat-openbsd" "jq" "openssh-server")
 sudo DEBIAN_FRONTEND=noninteractive apt-get install -y -qq "${packages[@]}" || die "Failed"
 if [[ "$SKIP_NGINX" != true ]]; then
  if ! command -v nginx &>/dev/null; then
   log_info "Installing nginx..."
   sudo DEBIAN_FRONTEND=noninteractive apt-get install -y -qq nginx || die "Failed to install nginx"
   sudo systemctl enable nginx 2>/dev/null || true
  fi
 fi
 if [[ "$SKIP_FIREWALL" != true ]]; then
  if ! command -v ufw &>/dev/null; then
   log_info "Installing ufw..."
   sudo apt-get install -y -qq ufw || die "Failed to install ufw"
   sudo systemctl enable ufw 2>/dev/null || true
  fi
 fi
 log_success "Dependencies installed"
}
install_nodejs() {
 log_step "Installing Node.js v22..."
 command -v node &>/dev/null && log_info "Already installed" && return 0
 curl -fsSL https://deb.nodesource.com/setup_22.x | sudo bash - || die "Failed"
 sudo DEBIAN_FRONTEND=noninteractive apt-get install -y -qq nodejs || die "Failed"
 log_success "Node.js installed"
}
install_pm2() {
 log_step "Installing PM2..."
 command -v pm2 &>/dev/null && log_info "Already installed" && return 0
 sudo npm install -g pm2 >/dev/null 2>&1 || die "Failed"
 log_success "PM2 installed"
}
create_admin_user() {
 log_step "Setting up admin user..."
 id "$ADMIN_USER" &>/dev/null && log_info "User exists" && return 0
 sudo useradd -m -s /bin/bash -G sudo "$ADMIN_USER" || die "Failed to create user"
 echo "$ADMIN_USER:$ADMIN_PASSWORD" | sudo chpasswd || die "Failed to set password"
 sudo chmod 700 "/home/$ADMIN_USER"
 log_success "Admin user created"
}
create_user() {
 log_step "Setting up user..."
 if id "$INSTALL_USER" &>/dev/null; then
  log_info "User exists"
 else
  sudo useradd -m -s /bin/bash "$INSTALL_USER" || die "Failed"
  log_success "User created"
 fi
 sudo chown "$INSTALL_USER:$INSTALL_USER" "/home/$INSTALL_USER"
}
setup_directories() {
 log_step "Setting up directories..."
 for d in "$INSTALL_DIR" "$INSTALL_DIR/smart-home-api" "$INSTALL_DIR/smart-home-web" "$INSTALL_DIR/backups" "$INSTALL_DIR/logs"; do
  sudo mkdir -p "$d"
 done
 sudo chown -R "$INSTALL_USER:$INSTALL_USER" "$INSTALL_DIR"
 log_success "Directories created"
}
download_with_checksum() {
    local url="$1" output="$2" checksum="${3:-}"
    log_step "Downloading: $(basename "$output") from $url..."
    if command -v curl &>/dev/null; then
        curl -fsSL "$url" -o "$output" || die "Failed to download $url"
    elif command -v wget &>/dev/null; then
        wget -q "$url" -O "$output" || die "Failed to download $url"
    else
        die "Neither curl nor wget found to download $url"
    fi

    if [[ -n "$checksum" ]] && [[ "$checksum" != "null" ]]; then
        local actual_cs=""
        if command -v sha256sum &>/dev/null; then
            actual_cs=$(sha256sum "$output" | awk '{print $1}')
        else
            actual_cs=$(shasum -a 256 "$output" | awk '{print $1}')
        fi
        local expected_hash="${checksum#sha256:}"
        if [[ "$actual_cs" != "$expected_hash" ]]; then
            die "Checksum mismatch for $(basename "$output"): expected $expected_hash, got $actual_cs"
        fi
        log_success "Checksum verified for $(basename "$output")"
    else
        log_success "Downloaded $(basename "$output")"
    fi
}

download_packages() {
    log_header "Downloading Packages"
    download_with_checksum "${MANIFEST_BACKEND_URL}" "$BACKEND_ZIP_PATH" "${MANIFEST_BACKEND_CHECKSUM}"
    download_with_checksum "${MANIFEST_FRONTEND_URL}" "$FRONTEND_ZIP_PATH" "${MANIFEST_FRONTEND_CHECKSUM}"
    if [[ -n "${MANIFEST_ROLLBACK_URL:-}" ]]; then
        local rb_target="$INSTALL_DIR/rollback.sh"
        log_info "Downloading local rollback utility to $rb_target..."
        download_with_checksum "${MANIFEST_ROLLBACK_URL}" "$rb_target" "${MANIFEST_ROLLBACK_CHECKSUM:-}"
        chmod 755 "$rb_target" 2>/dev/null || true
        chown "$INSTALL_USER:$INSTALL_USER" "$rb_target" 2>/dev/null || true
    fi
}
backup_existing_installation() {
 local v=$1
 [[ "$SKIP_BACKUP" == true ]] && return 0
 log_step "Creating backup..."
 local bdir="$INSTALL_DIR/backups/v${v}"
 [[ -d "$bdir" ]] && return 0
 sudo mkdir -p "$bdir"
 [[ -d "$INSTALL_DIR/smart-home-api" ]] && sudo cp -r "$INSTALL_DIR/smart-home-api" "$bdir/api"
 [[ -d "$INSTALL_DIR/smart-home-web" ]] && sudo cp -r "$INSTALL_DIR/smart-home-web" "$bdir/web"
 sudo chown -R "$INSTALL_USER:$INSTALL_USER" "$bdir"
 log_success "Backup created"
}
install_backend() {
 local bz=$1 is_upd=${2:-0}
 log_step "Installing backend from $bz..."
 [[ $is_upd -eq 1 ]] && sudo -u "$INSTALL_USER" pm2 stop smart-home-api 2>/dev/null || true
 sleep 2
 local t="/tmp/be-extract-$$"
 mkdir -p "$t"
 log_info "Extracting $bz to $t..."
 unzip -o "$bz" -d "$t" || die "Failed to unzip backend archive $bz"
 local bs; [[ -d "$t/build" ]] && bs="$t" || bs="$t/smart-home-api"
 local pd=false
 local data_backup=""
 if [[ $is_upd -eq 1 ]] && [[ -d "$INSTALL_DIR/smart-home-api/data" ]]; then
  data_backup="/tmp/data-backup-$$"
  log_info "Backing up data directory..."
  sudo mkdir -p "$data_backup"
  sudo cp -r "$INSTALL_DIR/smart-home-api/data" "$data_backup/data" || log_warning "Failed to backup data"
  [[ -d "$data_backup/data" ]] && pd=true
 fi

 if [[ $is_upd -eq 1 ]]; then
  log_info "Updating backend (replacing entire directory)..."
  local nm_backup=""
  if [[ "$SKIP_API_DEPS" == true ]] && [[ -d "$INSTALL_DIR/smart-home-api/node_modules" ]]; then
   nm_backup="/tmp/nm-backup-$$"
   log_info "Preserving node_modules (--skip-api-deps set)..."
   sudo cp -r "$INSTALL_DIR/smart-home-api/node_modules" "$nm_backup" || log_warning "Failed to backup node_modules"
  fi
  sudo rm -rf "$INSTALL_DIR/smart-home-api"
  sudo cp -r "$bs" "$INSTALL_DIR/smart-home-api"
  if [[ -n "$nm_backup" ]] && [[ -d "$nm_backup" ]]; then
   log_info "Restoring preserved node_modules..."
   sudo rm -rf "$INSTALL_DIR/smart-home-api/node_modules"
   sudo cp -r "$nm_backup" "$INSTALL_DIR/smart-home-api/node_modules"
   sudo chown -R "$INSTALL_USER:$INSTALL_USER" "$INSTALL_DIR/smart-home-api/node_modules"
   rm -rf "$nm_backup"
   log_success "node_modules restored"
  fi
 else
  log_info "Fresh install of backend..."
  sudo rm -rf "$INSTALL_DIR/smart-home-api"
  sudo cp -r "$bs" "$INSTALL_DIR/smart-home-api"
 fi

 if [[ "$pd" == true ]]; then
  log_info "Restoring data directory from backup..."
  sudo rm -rf "$INSTALL_DIR/smart-home-api/data"
  sudo mkdir -p "$INSTALL_DIR/smart-home-api"
  if sudo cp -r "$data_backup/data" "$INSTALL_DIR/smart-home-api/data"; then
   sudo chown -R "$INSTALL_USER:$INSTALL_USER" "$INSTALL_DIR/smart-home-api/data"
   log_success "Data restored successfully"
  else
   log_error "Failed to restore data directory"
  fi
  rm -rf "$data_backup"
 fi

 log_info "Setting permissions on installation..."
 sudo chown -R "$INSTALL_USER:$INSTALL_USER" "$INSTALL_DIR/smart-home-api"
 sudo chmod -R 755 "$INSTALL_DIR/smart-home-api"
 sudo find "$INSTALL_DIR/smart-home-api" -type f -exec chmod 644 {} +
 
 if [[ "$SKIP_API_DEPS" == true ]]; then
  log_info "Skipping npm install (--skip-api-deps set)"
 else
  log_info "Installing npm dependencies..."
  sudo -H -u "$INSTALL_USER" bash -c "cd $INSTALL_DIR/smart-home-api && npm install --omit=dev" || die "Failed to run npm install"
 fi
 log_success "Backend installed" && rm -rf "$t"
}
generate_env_file() {
 log_step "Generating .env..."
 local ef="$INSTALL_DIR/smart-home-api/.env"
 cat > "$ef" << ENVEOF
PORT=3000
NODE_ENV=production
DB_PATH=./data/smart-home.db
LOG_LEVEL=info
CORS_ORIGIN=*
# TODO: Configure log forwarding to external monitoring server
# LOG_FORWARDING_ENABLED=false
# LOG_FORWARDING_URL=
ENVEOF
 sudo chown "$INSTALL_USER:$INSTALL_USER" "$ef" && sudo chmod 600 "$ef"
 log_success "Environment file created"
}
run_database_migrations() {
 log_step "Running migrations..."
 local ad="$INSTALL_DIR/smart-home-api"
 local db="$ad/data/smart-home.db"
 [[ ! -d "$ad/data" ]] && sudo mkdir -p "$ad/data" && sudo chown "$INSTALL_USER:$INSTALL_USER" "$ad/data"
 [[ ! -f "$db" ]] && sudo -H -u "$INSTALL_USER" sqlite3 "$db" "SELECT 1;" >/dev/null 2>&1
 sudo -H -u "$INSTALL_USER" sqlite3 "$db" "CREATE TABLE IF NOT EXISTS _migrations (id INTEGER PRIMARY KEY, version TEXT UNIQUE, applied_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP);"
 [[ ! -d "$ad/sql" ]] && log_warning "No SQL directory" && return 0
 local applied=$(sudo -H -u "$INSTALL_USER" sqlite3 "$db" "SELECT GROUP_CONCAT(version) FROM _migrations;" 2>/dev/null || echo "")
 local cnt=0
 for sf in $(find "$ad/sql" -name '*.sql' -type f | sort); do
  local v=$(basename "$sf" .sql)
  if [[ -z "$applied" ]] || ! echo "$applied" | grep -q "$v"; then
   log_info "Applying: $v"
   sudo -H -u "$INSTALL_USER" sqlite3 "$db" < "$sf" || die "Failed"
   sudo -H -u "$INSTALL_USER" sqlite3 "$db" "INSERT INTO _migrations (version) VALUES ('$v');"
   cnt=$((cnt + 1))
  fi
 done
 sudo chmod 600 "$db"
 [[ $cnt -gt 0 ]] && log_success "Applied $cnt migrations" || log_info "All migrations applied"
}
install_frontend() {
 local fz=$1
 log_step "Installing frontend from $fz..."
 local t="/tmp/fe-extract-$$"
 mkdir -p "$t"
 log_info "Extracting $fz to $t..."
 unzip -o "$fz" -d "$t" || die "Failed to unzip frontend archive $fz"
 local fs; [[ -d "$t/dist" ]] && fs="$t" || fs="$t/smart-home-web"
 sudo rm -rf "$INSTALL_DIR/smart-home-web" && sudo cp -r "$fs" "$INSTALL_DIR/smart-home-web"
 sudo chown -R "$INSTALL_USER:$INSTALL_USER" "$INSTALL_DIR/smart-home-web"
 sudo find "$INSTALL_DIR/smart-home-web" -type f -exec chmod 644 {} \;
 sudo find "$INSTALL_DIR/smart-home-web" -type d -exec chmod 755 {} \;
 log_success "Frontend installed" && rm -rf "$t"
}
setup_pm2() {
 log_step "Configuring PM2..."
 local ef="$INSTALL_DIR/ecosystem.config.js"
 sudo mkdir -p "$INSTALL_DIR/logs" && sudo chown "$INSTALL_USER:$INSTALL_USER" "$INSTALL_DIR/logs"
 cat > "$ef" << 'ECOSYSTEM'
module.exports = {
 apps: [{
  name: 'smart-home-api',
  script: './build/server.js',
  cwd: 'INSTALL_DIR_PH/smart-home-api',
  instances: 1,
  exec_mode: 'fork',
  combine_logs: true,
  log: 'INSTALL_DIR_PH/logs/smart-home-api.log',
  //log_date_format: 'YYYY-MM-DD HH:mm:ss Z',
  env: { NODE_ENV: 'production' },
  autorestart: true,
  max_restarts: 10,
  min_uptime: '10s',
  max_memory_restart: '300M',
  port:3000,
 }]
};
ECOSYSTEM
 sed -i "s|INSTALL_DIR_PH|$INSTALL_DIR|g" "$ef" && sudo chown "$INSTALL_USER:$INSTALL_USER" "$ef"
 sudo -H -u "$INSTALL_USER" bash -c "cd '$INSTALL_DIR' && pm2 start '$ef'" || die "Failed"
 sudo env PATH=$PATH:/usr/local/bin pm2 startup -u "$INSTALL_USER" --hp "/home/$INSTALL_USER" >/dev/null 2>&1 || true
 sudo -H -u "$INSTALL_USER" bash -c "cd '$INSTALL_DIR' && pm2 save" && log_success "PM2 configured"
}
setup_nginx() {
 [[ "$SKIP_NGINX" == true ]] && return 0
 log_step "Configuring Nginx..."
 local tc="/tmp/sh-nginx-$$.conf"
 local wr="$INSTALL_DIR/smart-home-web/dist"
 generate_nginx_config "$API_PORT" "$wr" "$tc" || die "Failed"
 install_nginx_config "$tc" || die "Failed"
 log_success "Nginx configured"
}
setup_firewall() {
 [[ "$SKIP_FIREWALL" == true ]] && return 0
 log_step "Configuring firewall..."
 command -v ufw &>/dev/null || { log_warning "ufw not found"; return 0; }
 sudo ufw default deny incoming >/dev/null 2>&1 || true
 sudo ufw default allow outgoing >/dev/null 2>&1 || true
 sudo ufw allow 22/tcp >/dev/null 2>&1 || true
 sudo ufw allow "$WEB_PORT/tcp" >/dev/null 2>&1 || true
 sudo ufw allow "$API_PORT/tcp" >/dev/null 2>&1 || true
 sudo ufw allow 443/tcp >/dev/null 2>&1 || true
 echo "y" | sudo ufw enable >/dev/null 2>&1 || log_warning "Failed to enable ufw"
 sudo systemctl enable ssh >/dev/null 2>&1 || true
 sudo systemctl start ssh >/dev/null 2>&1 || true
 log_success "Firewall configured"
}
perform_health_checks() {
 sleep 3
 local db="$INSTALL_DIR/smart-home-api/data/smart-home.db"
 run_health_checks "$API_PORT" "$WEB_PORT" "$db" || return 1
}

cleanup_temp_files() {
 log_step "Cleaning up temporary files..."
 rm -f /tmp/smart-home-api-*.zip
 rm -f /tmp/smart-home-web-*.zip
 rm -f /tmp/sh-nginx-*.conf
 rm -rf /tmp/be-extract-*
 rm -rf /tmp/fe-extract-*
 rm -rf /tmp/data-backup-*
 rm -f /tmp/manifest-*.json
 log_success "Cleanup completed"
}

rotate_install_logs() {
 local max_logs=10
 local log_dir="$INSTALL_DIR/logs"
 [[ ! -d "$log_dir" ]] && return 0

 local log_count=$(find "$log_dir" -name "install-*.log" -type f 2>/dev/null | wc -l)

 if [[ $log_count -gt $max_logs ]]; then
  local excess=$((log_count - max_logs))
  find "$log_dir" -name "install-*.log" -type f -printf '%T@ %p\n' 2>/dev/null | \
   sort -n | head -n $excess | cut -d' ' -f2- | xargs rm -f 2>/dev/null || true
  log_info "Rotated install logs (kept last $max_logs, deleted $excess old logs)"
 fi
}
main() {
    local is_upd=0
    local install_log=""
    log_header "Smart Home System Installer v1.0"
    parse_arguments "$@"

    install_log="$INSTALL_DIR/logs/install-$(date +%Y-%m-%d-%H-%M-%S).log"
    mkdir -p "$(dirname "$install_log")"
    {
    load_manifest
    detect_installation && is_upd=1
    display_configuration "$is_upd"
    #[[ "$NON_INTERACTIVE" != true ]] && confirm "Continue?" || true
    check_system_requirements
    install_system_dependencies
    install_nodejs
    install_pm2
    create_admin_user
    create_user
    setup_directories
    download_packages
    [[ $is_upd -eq 1 ]] && backup_existing_installation "$(read_current_version)"
    install_backend "$BACKEND_ZIP_PATH" "$is_upd"
    generate_env_file
    run_database_migrations
    install_frontend "$FRONTEND_ZIP_PATH"
    setup_pm2
    setup_nginx
    setup_firewall
    perform_health_checks || true
    display_service_summary "$INSTALL_DIR" "$WEB_PORT" "$API_PORT"
    cleanup_temp_files
    rotate_install_logs
    } 2>&1 | tee -a "$install_log"
    log_info "Installation log saved to: $install_log"
    log_success "Installation Complete!"
}
[[ "${BASH_SOURCE[0]:-$0}" == "$0" ]] && main "$@"
