#!/bin/bash
#
# install.sh - Interactive installation script for OpenCurbOS Edge Sensor
#
# Purpose:
#   Installs edge-sensor and web-portal binaries to /opt/opencurb with
#   interactive configuration prompts, TLS certificate generation, and
#   systemd service setup.
#
# Usage:
#   sudo ./install.sh
#
# Exit Codes:
#   0 - Installation successful
#   1 - Invalid input or missing prerequisites
#   2 - Installation failed with rollback
#   3 - Rollback failed (system in inconsistent state)
#
# Dependencies:
#   - systemd (systemctl)
#   - openssl
#   - System packages (installed automatically): uuid-runtime, bc, libgl1, libglib2.0-0,
#     cpufrequtils, apt-transport-https, modemmanager, network-manager
#
# Note: python3 and pip3 are NOT required - binaries are standalone (Nuitka compiled)
#
# Reference:
#   - specs/005-a-makefile-that/contracts/install-script-interface.md
#   - specs/005-a-makefile-that/research.md lines 73-139
#

set -euo pipefail

# ============================================================================
# Global Variables
# ============================================================================

INSTALL_DIR="/opt/opencurb"
SYSTEMD_DIR="/etc/systemd/system"
ENV_FILE="$INSTALL_DIR/.env"

# Track cleanup steps for rollback
declare -a CLEANUP_STEPS=()

# Installation mode
UPGRADE_MODE=false

# Configuration variables
SITE_ID=""
STALL_ID=""
HOSTNAME=""
TIMEZONE=""
TAILSCALE_CONFIGURE=""
MQTT_CONFIGURE=""
MQTT_HOST=""
MQTT_PORT="1883"
MQTT_USERNAME=""
MQTT_PASSWORD=""
GCS_BUCKET=""
REBOOT_REQUIRED=false

# ============================================================================
# Utility Functions
# ============================================================================

log_info() {
    echo "[INFO] $*"
}

log_warn() {
    echo "[WARN] $*" >&2
}

log_error() {
    echo "[ERROR] $*" >&2
}

# Show progress dots while a command runs in the background
# Usage: run_with_progress "Description" "command to run"
run_with_progress() {
    local description="$1"
    local command="$2"

    # Run command in background, redirecting output to log
    eval "$command" > /tmp/apt_progress.log 2>&1 &
    local pid=$!

    # Show progress dots while command runs
    echo -n "[INFO] $description"
    while kill -0 $pid 2>/dev/null; do
        echo -n "."
        sleep 0.5
    done

    # Wait for command to complete and get exit code
    wait $pid
    local exit_code=$?

    # Append to main log
    cat /tmp/apt_progress.log >> /tmp/apt_install.log 2>/dev/null || true
    rm -f /tmp/apt_progress.log

    # Finish the line
    echo ""

    return $exit_code
}

# ============================================================================
# Prerequisite Checks (T037)
# ============================================================================

check_prerequisites() {
    log_info "Checking prerequisites..."

    # Check root privileges
    if [ "$(id -u)" -ne 0 ]; then
        log_error "This script must be run as root (use sudo)"
        exit 1
    fi

    # Check systemd
    if ! command -v systemctl &> /dev/null; then
        log_error "systemd is not available - systemctl command not found"
        exit 1
    fi

    # Check required binaries exist in current directory
    if [ ! -f "edge-sensor" ]; then
        log_error "edge-sensor binary not found in current directory"
        exit 1
    fi

    if [ ! -f "web-portal" ]; then
        log_error "web-portal binary not found in current directory"
        exit 1
    fi

    # Check required templates
    if [ ! -f "edge-sensor.service" ] || [ ! -f "web-portal.service" ]; then
        log_error "systemd service templates not found"
        exit 1
    fi

    if [ ! -f ".env.example" ]; then
        log_error ".env.example template not found"
        exit 1
    fi

    # Check for openssl (needed for TLS)
    if ! command -v openssl &> /dev/null; then
        log_error "openssl is not installed"
        log_error "Install with: sudo apt install openssl"
        exit 1
    fi

    # NOTE: uuid-runtime, bc, and other system packages installed via install_system_packages()
    # NOTE: Python 3 and pip3 checks REMOVED - Nuitka binaries are standalone
    # All Python dependencies (cv2, numpy, pydantic, structlog, etc.) are embedded in the binary
    # No system Python installation required

    log_info " All prerequisites met"
}

# ============================================================================
# System Package Installation (Consolidated)
# ============================================================================

install_system_packages() {
    log_info "Installing system packages..."

    # List of required packages:
    # - uuid-runtime: UUID generation for Site ID and Stall ID
    # - bc: Basic calculator for numeric operations
    # - libgl1: OpenGL library required by OpenCV (cv2)
    # - libglib2.0-0: GLib library required by OpenCV (cv2)
    # - cpufrequtils: CPU frequency scaling for power management
    # - apt-transport-https: HTTPS support for Tailscale repository
    # - modemmanager: 4G cellular modem management
    # - network-manager: Network connection management for cellular

    local packages="uuid-runtime bc libgl1 libglib2.0-0 cpufrequtils apt-transport-https modemmanager network-manager"

    if run_with_progress "Installing system packages (uuid-runtime, bc, libgl1, libglib2.0-0, cpufrequtils, apt-transport-https, modemmanager, network-manager)" "apt-get update -qq && apt-get install -y -qq $packages"; then
        log_info "✓ All system packages installed"
    else
        log_error "Failed to install system packages"
        log_error "See /tmp/apt_install.log for details"
        log_error "Manually install with: sudo apt update && sudo apt install $packages"
        exit 1
    fi

    # Verify critical packages
    local critical_packages="uuidgen bc"
    for pkg_cmd in $critical_packages; do
        if ! command -v "$pkg_cmd" &> /dev/null; then
            log_error "$pkg_cmd command not available after installation"
            exit 1
        fi
    done

    log_info "✓ System packages verified"
}

# ============================================================================
# UUID Validation Functions (T038)
# ============================================================================

validate_uuid() {
    local uuid="$1"
    local regex='^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$'

    if [[ $uuid =~ $regex ]]; then
        return 0
    else
        return 1
    fi
}

read_uuid() {
    local prompt="$1"
    local var_name="$2"
    local attempts=0
    local max_attempts=3
    local input

    while [ $attempts -lt $max_attempts ]; do
        echo ""
        read -p "$prompt (press Enter to generate): " input

        # Generate UUID if empty
        if [ -z "$input" ]; then
            input=$(uuidgen | tr '[:upper:]' '[:lower:]')
            log_info "Generated UUID: $input"
        else
            # Normalize to lowercase
            input=$(echo "$input" | tr '[:upper:]' '[:lower:]')
        fi

        # Validate UUID
        if validate_uuid "$input"; then
            eval "$var_name='$input'"
            return 0
        else
            ((attempts++))
            log_error "Invalid UUID format"
            log_error "Expected format: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"

            if [ $attempts -lt $max_attempts ]; then
                log_info "Please try again (attempt $((attempts + 1))/$max_attempts)"
            fi
        fi
    done

    log_error "Maximum attempts reached"
    return 1
}

# ============================================================================
# Port Validation
# ============================================================================

validate_port() {
    local port="$1"

    if [[ "$port" =~ ^[0-9]+$ ]] && [ "$port" -ge 1 ] && [ "$port" -le 65535 ]; then
        return 0
    else
        return 1
    fi
}

read_port() {
    local prompt="$1"
    local default="$2"
    local var_name="$3"
    local attempts=0
    local max_attempts=3
    local input

    while [ $attempts -lt $max_attempts ]; do
        echo ""
        read -p "$prompt [default: $default]: " input

        # Use default if empty
        if [ -z "$input" ]; then
            input="$default"
        fi

        # Validate port
        if validate_port "$input"; then
            eval "$var_name='$input'"
            return 0
        else
            ((attempts++))
            log_error "Invalid port: must be between 1 and 65535"

            if [ $attempts -lt $max_attempts ]; then
                log_info "Please try again (attempt $((attempts + 1))/$max_attempts)"
            fi
        fi
    done

    log_error "Maximum attempts reached"
    return 1
}

# ============================================================================
# Interactive Prompts (T039)
# ============================================================================

prompt_configuration() {
    echo ""
    echo "=========================================="
    echo "  OpenCurbOS Edge Sensor Installation"
    echo "=========================================="
    echo ""
    echo "This script will install the edge sensor service and web portal."
    echo "You will be prompted for configuration values."
    echo ""

    # Site ID
    log_info "Site ID identifies your parking facility in OpenCurbOS"
    if ! read_uuid "Enter Site ID" SITE_ID; then
        log_error "Failed to read Site ID"
        exit 1
    fi

    # Stall ID
    log_info "Stall ID identifies this specific parking stall"
    if ! read_uuid "Enter Stall ID" STALL_ID; then
        log_error "Failed to read Stall ID"
        exit 1
    fi

    # Hostname
    log_info "Device hostname for easy identification on the network"
    local default_hostname="opencurb-${STALL_ID: -8}"  # Last 8 chars of STALL_ID
    echo ""
    read -p "Enter hostname [default: $default_hostname]: " HOSTNAME
    HOSTNAME=${HOSTNAME:-$default_hostname}

    # Validate hostname (lowercase alphanumeric and hyphens only)
    if ! [[ "$HOSTNAME" =~ ^[a-z0-9-]+$ ]]; then
        log_error "Invalid hostname. Must contain only lowercase letters, numbers, and hyphens."
        exit 1
    fi

    # Timezone selection
    log_info "Select timezone for accurate time synchronization"
    echo ""
    echo "US Timezones:"
    echo "  1) America/New_York    (Eastern Time)"
    echo "  2) America/Chicago     (Central Time)"
    echo "  3) America/Denver      (Mountain Time)"
    echo "  4) America/Los_Angeles (Pacific Time)"
    echo "  5) America/Phoenix     (Arizona - No DST)"
    echo "  6) America/Anchorage   (Alaska Time)"
    echo "  7) Pacific/Honolulu    (Hawaii Time)"
    echo ""

    local tz_choice=""
    local attempts=0
    local max_attempts=3

    while [ $attempts -lt $max_attempts ]; do
        read -p "Enter timezone number (1-7) [default: 4 - Pacific]: " tz_choice
        tz_choice=${tz_choice:-4}

        case "$tz_choice" in
            1) TIMEZONE="America/New_York"; break ;;
            2) TIMEZONE="America/Chicago"; break ;;
            3) TIMEZONE="America/Denver"; break ;;
            4) TIMEZONE="America/Los_Angeles"; break ;;
            5) TIMEZONE="America/Phoenix"; break ;;
            6) TIMEZONE="America/Anchorage"; break ;;
            7) TIMEZONE="Pacific/Honolulu"; break ;;
            *)
                ((attempts++))
                log_error "Invalid selection. Please enter a number between 1 and 7."
                if [ $attempts -lt $max_attempts ]; then
                    log_info "Please try again (attempt $((attempts + 1))/$max_attempts)"
                fi
                ;;
        esac
    done

    if [ -z "$TIMEZONE" ]; then
        log_error "Maximum attempts reached for timezone selection"
        exit 1
    fi

    log_info "Selected timezone: $TIMEZONE"

    # Tailscale Configuration prompt
    echo ""
    log_info "Tailscale provides secure remote access to the device"
    log_warn "If you are currently connected over Tailscale, skip this step to avoid disconnection"
    echo ""
    read -p "Install and configure Tailscale now? (y/n) [default: y]: " TAILSCALE_CONFIGURE
    TAILSCALE_CONFIGURE=${TAILSCALE_CONFIGURE:-y}

    # MQTT Configuration prompt
    echo ""
    read -p "Configure MQTT settings now? (y/n) [default: y]: " MQTT_CONFIGURE
    MQTT_CONFIGURE=${MQTT_CONFIGURE:-y}

    if [[ "$MQTT_CONFIGURE" =~ ^[Yy]$ ]]; then
        # MQTT Host
        echo ""
        read -p "Enter MQTT broker hostname [default: mqtt.opencurbos.com]: " MQTT_HOST
        MQTT_HOST=${MQTT_HOST:-mqtt.opencurbos.com}

        # MQTT Port
        if ! read_port "Enter MQTT broker port" "1883" MQTT_PORT; then
            log_error "Failed to read MQTT port"
            exit 1
        fi

        # MQTT Username
        echo ""
        read -p "Enter MQTT username: " MQTT_USERNAME
        if [ -z "$MQTT_USERNAME" ]; then
            log_warn "MQTT username is empty (anonymous connection)"
        fi

        # MQTT Password (no echo)
        echo ""
        read -s -p "Enter MQTT password: " MQTT_PASSWORD
        echo ""  # newline after password
        if [ -z "$MQTT_PASSWORD" ]; then
            log_warn "MQTT password is empty (anonymous connection)"
        fi
    else
        log_info "MQTT configuration deferred - configure via web portal after installation"
    fi

    # GCS Bucket (optional)
    echo ""
    read -p "Enter GCS bucket name [default: opencurb-camera-images]: " GCS_BUCKET
    GCS_BUCKET=${GCS_BUCKET:-opencurb-camera-images}

    # Display configuration summary
    echo ""
    echo "=========================================="
    echo "  Configuration Summary"
    echo "=========================================="
    echo "Site ID: $SITE_ID"
    echo "Stall ID: $STALL_ID"
    echo "Hostname: $HOSTNAME"
    echo "Timezone: $TIMEZONE"
    if [[ "$TAILSCALE_CONFIGURE" =~ ^[Yy]$ ]]; then
        echo "Tailscale: Install and configure"
    else
        echo "Tailscale: Skip installation"
    fi
    if [[ "$MQTT_CONFIGURE" =~ ^[Yy]$ ]]; then
        echo "MQTT Host: $MQTT_HOST"
        echo "MQTT Port: $MQTT_PORT"
        echo "MQTT Username: $MQTT_USERNAME"
        echo "MQTT Password: ********"
    else
        echo "MQTT: Configure via web portal"
    fi
    echo "GCS Bucket: $GCS_BUCKET"
    echo "=========================================="
    echo ""

    read -p "Proceed with installation? (y/n): " confirm
    if [[ ! "$confirm" =~ ^[Yy]$ ]]; then
        log_info "Installation cancelled by user"
        exit 0
    fi
}

# ============================================================================
# Rollback Mechanism (T040)
# ============================================================================

track_step() {
    local step="$1"
    CLEANUP_STEPS+=("$step")
    log_info "Tracking cleanup step: $step"
}

rollback() {
    local exit_code=$?

    if [ $exit_code -eq 0 ]; then
        # Normal exit, no rollback needed
        return 0
    fi

    log_error "Installation failed, rolling back changes..."

    # Execute cleanup steps in reverse order
    local i
    for ((i=${#CLEANUP_STEPS[@]}-1; i>=0; i--)); do
        local step="${CLEANUP_STEPS[$i]}"
        log_info "Rollback: $step"

        case "$step" in
            "user_created")
                userdel -r opencurb 2>/dev/null || true
                groupdel opencurb 2>/dev/null || true
                ;;
            "directory_created")
                rm -rf "$INSTALL_DIR"
                ;;
            "cert_generated")
                rm -f "$INSTALL_DIR/web-portal.crt" "$INSTALL_DIR/web-portal.key"
                ;;
            "binaries_copied")
                rm -f "$INSTALL_DIR/edge-sensor" "$INSTALL_DIR/web-portal"
                ;;
            "env_created")
                rm -f "$ENV_FILE" "${ENV_FILE}.backup"
                ;;
            "services_installed")
                rm -f "$SYSTEMD_DIR/edge-sensor.service" "$SYSTEMD_DIR/web-portal.service"
                systemctl daemon-reload 2>/dev/null || true
                ;;
            "services_enabled")
                systemctl disable edge-sensor.service web-portal.service 2>/dev/null || true
                ;;
            "services_started")
                systemctl stop edge-sensor.service web-portal.service 2>/dev/null || true
                ;;
            "power_management_configured")
                systemctl disable cpufreq-powersave.service 2>/dev/null || true
                systemctl stop cpufreq-powersave.service 2>/dev/null || true
                rm -f "$SYSTEMD_DIR/cpufreq-powersave.service"
                systemctl daemon-reload 2>/dev/null || true
                ;;
            "hostname_configured")
                # Hostname rollback is not practical - would require knowing previous hostname
                # User should manually revert /etc/hostname and /etc/hosts if needed
                log_warn "Hostname change cannot be automatically rolled back"
                ;;
            "timezone_configured")
                # Timezone rollback not practical - would need to know previous timezone
                log_warn "Timezone change cannot be automatically rolled back"
                ;;
            "tailscale_configured")
                # Disconnect from Tailscale network
                tailscale down 2>/dev/null || true
                # Remove Tailscale (keep config in case of re-install)
                apt-get remove -y tailscale 2>/dev/null || true
                rm -f /etc/apt/sources.list.d/tailscale.list
                apt-get update -qq 2>/dev/null || true
                ;;
            "4g_modem_configured")
                # Remove cellular connection
                nmcli c delete cellular 2>/dev/null || true
                # Stop and disable modem services
                systemctl disable ModemManager NetworkManager 2>/dev/null || true
                systemctl stop ModemManager NetworkManager 2>/dev/null || true
                # Remove modem packages (optional - commented out to avoid breaking existing network)
                # apt-get remove -y modemmanager network-manager 2>/dev/null || true
                log_info "Cellular connection removed"
                ;;
            "wittypi_configured")
                # Disable and remove WittyPi services
                systemctl disable wittypi-firstboot.service 2>/dev/null || true
                systemctl disable wittypi-rtc-sync.service 2>/dev/null || true
                systemctl disable wittypi-rtc-sync.timer 2>/dev/null || true
                systemctl stop wittypi-rtc-sync.timer 2>/dev/null || true
                rm -f "$SYSTEMD_DIR/wittypi-firstboot.service" 2>/dev/null || true
                rm -f "$SYSTEMD_DIR/wittypi-rtc-sync.service" 2>/dev/null || true
                rm -f "$SYSTEMD_DIR/wittypi-rtc-sync.timer" 2>/dev/null || true
                rm -f "$SYSTEMD_DIR/wittypi-rtc-sync-daily.service" 2>/dev/null || true
                # Disable WittyPi daemon
                systemctl disable wittypi.service 2>/dev/null || true
                systemctl stop wittypi.service 2>/dev/null || true
                # Remove WittyPi installation
                rm -rf /opt/wittypi 2>/dev/null || true
                rm -f /etc/init.d/wittypi 2>/dev/null || true
                systemctl daemon-reload 2>/dev/null || true
                log_info "WittyPi software and services removed"
                ;;
            "power_monitor_configured")
                # Stop and disable power monitor service
                systemctl disable power-monitor.service 2>/dev/null || true
                systemctl stop power-monitor.service 2>/dev/null || true
                rm -f "$SYSTEMD_DIR/power-monitor.service" 2>/dev/null || true
                # Remove power monitor installation
                rm -rf /opt/power-monitor 2>/dev/null || true
                # Note: Keep /var/log/power_daily data for analysis
                systemctl daemon-reload 2>/dev/null || true
                log_info "Power monitor service removed"
                ;;
        esac
    done

    log_error "Rollback complete"
    exit 2
}

# Set trap for rollback on error
trap rollback EXIT ERR INT TERM

# ============================================================================
# Upgrade Detection and Backup (T041)
# ============================================================================

backup_existing_config() {
    if [ ! -d "$INSTALL_DIR" ]; then
        return 0
    fi

    log_info "Backing up existing configuration..."

    # Backup .env
    if [ -f "$ENV_FILE" ]; then
        cp "$ENV_FILE" "${ENV_FILE}.backup.$(date +%Y%m%d-%H%M%S)"
        log_info "✓ Backed up .env file"
    fi

    # Backup certificates
    if [ -f "$INSTALL_DIR/web-portal.crt" ]; then
        cp "$INSTALL_DIR/web-portal.crt" "$INSTALL_DIR/web-portal.crt.backup.$(date +%Y%m%d-%H%M%S)"
        log_info "✓ Backed up TLS certificate"
    fi

    if [ -f "$INSTALL_DIR/web-portal.key" ]; then
        cp "$INSTALL_DIR/web-portal.key" "$INSTALL_DIR/web-portal.key.backup.$(date +%Y%m%d-%H%M%S)"
        log_info "✓ Backed up TLS private key"
    fi
}

# ============================================================================
# User and Directory Creation (T042)
# ============================================================================

create_user_and_directories() {
    log_info "Creating opencurb user and directories..."

    # Create group if not exists
    if ! getent group opencurb > /dev/null 2>&1; then
        groupadd -r opencurb
        track_step "user_created"
        log_info "✓ Created opencurb group"
    else
        log_info "✓ opencurb group already exists"
    fi

    # Create user if not exists
    if ! id opencurb > /dev/null 2>&1; then
        useradd -r -g opencurb -s /usr/sbin/nologin -d "$INSTALL_DIR" opencurb
        log_info "✓ Created opencurb user"
    else
        log_info "✓ opencurb user already exists"
    fi

    # Create installation directory
    if [ ! -d "$INSTALL_DIR" ]; then
        mkdir -p "$INSTALL_DIR"
        track_step "directory_created"
        log_info "✓ Created $INSTALL_DIR"
    fi

    # Create tmp directory for Nuitka onefile unpacking (real filesystem, not tmpfs)
    if [ ! -d "$INSTALL_DIR/tmp" ]; then
        mkdir -p "$INSTALL_DIR/tmp"
        log_info "✓ Created $INSTALL_DIR/tmp for Nuitka binary unpacking"
    fi

    # Create certs directory for MQTT mTLS certificates
    if [ ! -d "$INSTALL_DIR/certs" ]; then
        mkdir -p "$INSTALL_DIR/certs"
        log_info "✓ Created $INSTALL_DIR/certs for MQTT mTLS certificates"
    fi

    # Set ownership and permissions
    chown -R opencurb:opencurb "$INSTALL_DIR"
    chmod 755 "$INSTALL_DIR"
    chmod 755 "$INSTALL_DIR/tmp"
    chmod 755 "$INSTALL_DIR/certs"
    log_info "✓ Set directory ownership and permissions"
}

# ============================================================================
# TLS Certificate Generation (T043)
# ============================================================================

generate_certificates() {
    local cert_path="$INSTALL_DIR/web-portal.crt"
    local key_path="$INSTALL_DIR/web-portal.key"

    # Skip if upgrade and valid cert exists
    if [ "$UPGRADE_MODE" = true ] && [ -f "$cert_path" ] && [ -f "$key_path" ]; then
        # Verify certificate is still valid
        if openssl x509 -in "$cert_path" -noout -checkend 86400 2>/dev/null; then
            log_info "Existing TLS certificate is valid, skipping generation"
            return 0
        else
            log_warn "Existing TLS certificate expired or invalid, regenerating"
        fi
    fi

    log_info "Generating TLS certificates..."

    # Check if generate-cert.sh exists in current directory
    if [ -f "./generate-cert.sh" ]; then
        if ./generate-cert.sh "$cert_path" "$key_path" "opencurb:opencurb"; then
            track_step "cert_generated"
            log_info "✓ TLS certificates generated"
        else
            log_error "Failed to generate TLS certificates"
            exit 2
        fi
    else
        log_error "generate-cert.sh script not found"
        exit 1
    fi
}

# ============================================================================
# System Dependency Installation
# ============================================================================

install_system_dependencies() {
    log_info "Configuring system dependencies for Nuitka binaries..."

    # NOTE: libgl1 and libglib2.0-0 installed via install_system_packages()
    # System libraries required by OpenCV (cv2) even though it's compiled into binary
    # Shared libraries (.so files) cannot be embedded in Nuitka binaries

    # Enable and start Bluetooth for BLE scanner
    log_info "Enabling Bluetooth service..."

    if systemctl enable bluetooth 2>/dev/null && systemctl start bluetooth 2>/dev/null; then
        log_info "✓ Bluetooth service enabled and started"

        # Verify bluetooth adapter is up
        sleep 2  # Give bluetooth service time to initialize

        # Unblock Bluetooth if RF-kill is blocking it
        if command -v rfkill &> /dev/null; then
            if rfkill list bluetooth 2>/dev/null | grep -q "blocked: yes"; then
                log_info "Unblocking Bluetooth (RF-kill)..."
                rfkill unblock bluetooth 2>/dev/null || log_warn "Could not unblock Bluetooth via RF-kill"
            fi
        fi

        if command -v hciconfig &> /dev/null; then
            # Bring up bluetooth adapter if it's down
            if ! hciconfig hci0 2>/dev/null | grep -q "UP RUNNING"; then
                log_info "Bringing up Bluetooth adapter..."
                if ! hciconfig hci0 up 2>/dev/null; then
                    log_warn "Could not bring up hci0"
                    log_warn "If RF-kill blocked, run: sudo rfkill unblock bluetooth && sudo hciconfig hci0 up"
                fi
            fi

            # Verify adapter is up
            if hciconfig hci0 2>/dev/null | grep -q "UP RUNNING"; then
                log_info "✓ Bluetooth adapter ready"
            else
                log_warn "Bluetooth adapter not UP - manual intervention may be required"
            fi
        else
            log_warn "hciconfig not available - Bluetooth adapter status unknown"
        fi
    else
        log_warn "Could not enable Bluetooth - BLE scanner may not work"
        log_warn "Manually enable with: sudo systemctl enable bluetooth && sudo systemctl start bluetooth"
    fi
}

# ============================================================================
# Binary Installation (T044)
# ============================================================================

install_binaries() {
    log_info "Installing binaries..."

    # Copy edge-sensor
    cp edge-sensor "$INSTALL_DIR/"
    chmod 755 "$INSTALL_DIR/edge-sensor"
    chown opencurb:opencurb "$INSTALL_DIR/edge-sensor"
    log_info "✓ Installed edge-sensor"

    # Copy web-portal
    cp web-portal "$INSTALL_DIR/"
    chmod 755 "$INSTALL_DIR/web-portal"
    chown opencurb:opencurb "$INSTALL_DIR/web-portal"
    log_info "✓ Installed web-portal"

    track_step "binaries_copied"
}

# ============================================================================
# Configuration File Creation (T045)
# ============================================================================

create_env_file() {
    log_info "Creating configuration file..."

    local temp_file=$(mktemp)
    trap "rm -f $temp_file" RETURN

    # Start with .env.example as template
    if [ -f ".env.example" ]; then
        cp .env.example "$temp_file"
    else
        log_error ".env.example template not found"
        exit 1
    fi

    # Replace placeholders with actual values
    sed -i.bak \
        -e "s/SITE_ID=.*/SITE_ID=$SITE_ID/" \
        -e "s/STALL_ID=.*/STALL_ID=$STALL_ID/" \
        "$temp_file"

    # Configure MQTT settings
    if [[ "$MQTT_CONFIGURE" =~ ^[Yy]$ ]]; then
        sed -i.bak \
            -e "s/MQTT_HOST=.*/MQTT_HOST=$MQTT_HOST/" \
            -e "s/MQTT_PORT=.*/MQTT_PORT=$MQTT_PORT/" \
            -e "s/MQTT_USERNAME=.*/MQTT_USERNAME=$MQTT_USERNAME/" \
            -e "s/MQTT_PASSWORD=.*/MQTT_PASSWORD=$MQTT_PASSWORD/" \
            "$temp_file"
    else
        # Use placeholders for deferred configuration
        sed -i.bak \
            -e "s/MQTT_HOST=.*/MQTT_HOST=CONFIGURE_VIA_WEB_PORTAL/" \
            -e "s/MQTT_USERNAME=.*/MQTT_USERNAME=CONFIGURE_VIA_WEB_PORTAL/" \
            -e "s/MQTT_PASSWORD=.*/MQTT_PASSWORD=CONFIGURE_VIA_WEB_PORTAL/" \
            "$temp_file"
    fi

    # Configure GCS bucket (always set with default)
    sed -i.bak "s/GCS_BUCKET_NAME=.*/GCS_BUCKET_NAME=$GCS_BUCKET/" "$temp_file"

    # Atomic write: backup → write temp → sync → move
    if [ -f "$ENV_FILE" ]; then
        cp "$ENV_FILE" "${ENV_FILE}.backup"
    fi

    sync "$temp_file"
    mv "$temp_file" "$ENV_FILE"

    # Set secure permissions
    chmod 600 "$ENV_FILE"
    chown opencurb:opencurb "$ENV_FILE"

    track_step "env_created"
    log_info "✓ Configuration file created at $ENV_FILE"
}

# ============================================================================
# systemd Service Installation (T046-T047)
# ============================================================================

install_systemd_services() {
    log_info "Installing systemd services..."

    # Copy service files
    cp edge-sensor.service "$SYSTEMD_DIR/"
    cp web-portal.service "$SYSTEMD_DIR/"

    # Set permissions
    chmod 644 "$SYSTEMD_DIR/edge-sensor.service"
    chmod 644 "$SYSTEMD_DIR/web-portal.service"
    chown root:root "$SYSTEMD_DIR/edge-sensor.service"
    chown root:root "$SYSTEMD_DIR/web-portal.service"

    track_step "services_installed"
    log_info "✓ Systemd service files installed"

    # Reload systemd
    systemctl daemon-reload
    log_info "✓ Systemd daemon reloaded"

    # Enable services
    systemctl enable edge-sensor.service
    systemctl enable web-portal.service
    track_step "services_enabled"
    log_info "✓ Services enabled"

    # Start services based on MQTT configuration
    if [[ "$MQTT_CONFIGURE" =~ ^[Yy]$ ]]; then
        # Start both services
        systemctl start edge-sensor.service
        systemctl start web-portal.service
        track_step "services_started"
        log_info "✓ Services started"
    else
        # Start only web-portal (edge-sensor needs MQTT config)
        systemctl start web-portal.service
        track_step "services_started"
        log_info "✓ Web portal started (edge-sensor requires MQTT configuration)"
    fi
}

# ============================================================================
# Power Management Configuration
# ============================================================================

configure_power_management() {
    log_info "Configuring CPU power management for edge deployment..."

    # NOTE: cpufrequtils installed via install_system_packages()

    # Create cpufreq-powersave service file
    log_info "Creating cpufreq-powersave systemd service..."

    cat > "$SYSTEMD_DIR/cpufreq-powersave.service" <<'EOF'
[Unit]
Description=Set CPU governor to powersave
After=multi-user.target

[Service]
Type=oneshot
ExecStart=/usr/bin/cpufreq-set -g powersave -r
RemainAfterExit=yes

[Install]
WantedBy=multi-user.target
EOF

    # Set permissions
    chmod 644 "$SYSTEMD_DIR/cpufreq-powersave.service"
    chown root:root "$SYSTEMD_DIR/cpufreq-powersave.service"

    log_info "✓ Created cpufreq-powersave.service"

    # Reload systemd to pick up new service
    systemctl daemon-reload

    # Enable and start the service
    log_info "Enabling CPU powersave mode..."
    if systemctl enable cpufreq-powersave.service 2>/dev/null && systemctl start cpufreq-powersave.service 2>/dev/null; then
        log_info "✓ CPU governor set to powersave mode"

        # Verify the governor is set
        if command -v cpufreq-info &> /dev/null; then
            current_governor=$(cpufreq-info -p 2>/dev/null | awk '{print $3}')
            if [ "$current_governor" = "powersave" ]; then
                log_info "✓ Verified CPU governor: powersave"
            else
                log_warn "CPU governor verification failed (expected: powersave, got: $current_governor)"
            fi
        fi
    else
        log_warn "Could not enable cpufreq-powersave service"
        log_warn "Manually enable with: sudo systemctl enable --now cpufreq-powersave"
    fi

    track_step "power_management_configured"
}

# ============================================================================
# Hostname Configuration
# ============================================================================

configure_hostname() {
    log_info "Configuring device hostname..."

    local current_hostname=$(hostname)

    # Skip if hostname hasn't changed
    if [ "$current_hostname" = "$HOSTNAME" ]; then
        log_info "✓ Hostname already set to: $HOSTNAME"
        return 0
    fi

    local old_hostname="$current_hostname"

    # Update /etc/hostname
    log_info "Setting hostname to: $HOSTNAME"
    echo "$HOSTNAME" > /etc/hostname

    # Update /etc/hosts
    # Replace old hostname with new hostname in 127.0.1.1 entry
    if grep -q "127.0.1.1" /etc/hosts; then
        sed -i "s/127.0.1.1.*/127.0.1.1\t$HOSTNAME/" /etc/hosts
    else
        # Add entry if it doesn't exist
        echo "127.0.1.1	$HOSTNAME" >> /etc/hosts
    fi

    # Use hostnamectl to set the hostname (immediate effect)
    if command -v hostnamectl &> /dev/null; then
        hostnamectl set-hostname "$HOSTNAME"
        log_info "✓ Hostname set using hostnamectl"
    else
        # Fallback: set hostname directly
        hostname "$HOSTNAME"
        log_info "✓ Hostname set using hostname command"
    fi

    # Verify hostname was set
    local new_hostname=$(hostname)
    if [ "$new_hostname" = "$HOSTNAME" ]; then
        log_info "✓ Hostname changed from '$old_hostname' to '$HOSTNAME'"
        REBOOT_REQUIRED=true
        log_warn "Reboot required for hostname change to take full effect"
    else
        log_warn "Hostname verification failed (expected: $HOSTNAME, got: $new_hostname)"
    fi

    track_step "hostname_configured"
}

# ============================================================================
# Timezone Configuration
# ============================================================================

configure_timezone() {
    log_info "Configuring system timezone..."

    # Get current timezone
    local current_tz=$(timedatectl 2>/dev/null | grep "Time zone" | awk '{print $3}' || echo "")

    # Skip if timezone is already set correctly
    if [ "$current_tz" = "$TIMEZONE" ]; then
        log_info "✓ Timezone already set to: $TIMEZONE"
        return 0
    fi

    # Set timezone using timedatectl
    if timedatectl set-timezone "$TIMEZONE" 2>/dev/null; then
        log_info "✓ Timezone set to: $TIMEZONE"
    else
        log_warn "Failed to set timezone using timedatectl"
        log_warn "Manually set with: sudo timedatectl set-timezone $TIMEZONE"
        return 0  # Non-fatal
    fi

    # Verify timezone was set
    local new_tz=$(timedatectl 2>/dev/null | grep "Time zone" | awk '{print $3}' || echo "")
    if [ "$new_tz" = "$TIMEZONE" ]; then
        log_info "✓ Timezone verified: $TIMEZONE"

        # Display current time
        local current_time=$(date "+%Y-%m-%d %H:%M:%S %Z")
        log_info "✓ Current time: $current_time"
    else
        log_warn "Timezone verification failed (expected: $TIMEZONE, got: $new_tz)"
    fi

    track_step "timezone_configured"
}

# ============================================================================
# Tailscale Configuration
# ============================================================================

configure_tailscale() {
    log_info "Installing and configuring Tailscale..."

    # Verify hostname is properly set before Tailscale configuration
    local current_hostname=$(hostname)
    if [ "$current_hostname" != "$HOSTNAME" ]; then
        log_warn "Hostname mismatch detected (expected: $HOSTNAME, got: $current_hostname)"
        log_info "Applying hostname before Tailscale configuration..."
        hostnamectl set-hostname "$HOSTNAME" 2>/dev/null || hostname "$HOSTNAME"
        sleep 1
    fi
    log_info "✓ Hostname verified: $(hostname)"

    # NOTE: apt-transport-https installed via install_system_packages()

    # Add Tailscale's package signing key and repository
    log_info "Adding Tailscale repository..."

    # Download and add GPG key
    if curl -fsSL https://pkgs.tailscale.com/stable/raspbian/bookworm.asc | apt-key add - 2>/dev/null; then
        log_info "✓ Tailscale GPG key added"
    else
        log_warn "Failed to add Tailscale GPG key"
        return 0  # Non-fatal
    fi

    # Add Tailscale repository
    if curl -fsSL https://pkgs.tailscale.com/stable/raspbian/bookworm.list | tee /etc/apt/sources.list.d/tailscale.list >/dev/null; then
        log_info "✓ Tailscale repository added"
    else
        log_warn "Failed to add Tailscale repository"
        return 0  # Non-fatal
    fi

    # Update package list and install Tailscale
    if run_with_progress "Installing Tailscale" "apt-get update -qq && apt-get install -y -qq tailscale"; then
        log_info "✓ Tailscale installed"
    else
        log_warn "Failed to install Tailscale"
        log_warn "Manually install with: curl -fsSL https://tailscale.com/install.sh | sh"
        return 0  # Non-fatal
    fi

    # Enable and start Tailscale with SSH support
    log_info "Starting Tailscale with SSH enabled..."
    log_warn "Tailscale authentication required - follow the URL to authenticate this device"

    # Run tailscale up with --ssh flag
    # Note: This will output an authentication URL that the user must follow
    if tailscale up --ssh; then
        log_info "✓ Tailscale connected with SSH enabled"

        # Get Tailscale IP address
        sleep 2  # Give Tailscale time to establish connection
        local tailscale_ip=$(tailscale ip -4 2>/dev/null || echo "")

        if [ -n "$tailscale_ip" ]; then
            log_info "✓ Tailscale IPv4 address: $tailscale_ip"
            # Store for display in summary
            echo "$tailscale_ip" > /tmp/tailscale_ip.txt
        else
            log_warn "Could not retrieve Tailscale IP address"
        fi
    else
        log_warn "Tailscale connection failed or was cancelled"
        log_warn "Manually connect with: sudo tailscale up --ssh"
        return 0  # Non-fatal
    fi

    track_step "tailscale_configured"
}

# ============================================================================
# 4G Modem Configuration
# ============================================================================

configure_4g_modem() {
    log_info "Configuring 4G modem..."

    # NOTE: modemmanager and network-manager installed via install_system_packages()

    # Enable and start services
    log_info "Enabling ModemManager and NetworkManager services..."
    if systemctl enable ModemManager NetworkManager 2>/dev/null && \
       systemctl start ModemManager NetworkManager 2>/dev/null; then
        log_info "✓ ModemManager and NetworkManager services enabled and started"
    else
        log_warn "Failed to enable modem services"
        log_warn "Manually enable with: sudo systemctl enable --now ModemManager NetworkManager"
        return 0  # Non-fatal
    fi

    # Wait for ModemManager to detect modem
    log_info "Waiting for modem initialization (10 seconds)..."
    sleep 10

    # Check if modem is detected
    if ! mmcli -L 2>/dev/null | grep -q "Modem"; then
        log_warn "No modem detected by ModemManager"
        log_warn "Modem may need to be plugged in or may initialize after reboot"
        log_warn "Manually check with: mmcli -L"
        return 0  # Non-fatal
    fi

    log_info "✓ Modem detected"

    # Check if cellular connection already exists
    if nmcli c show cellular &>/dev/null; then
        log_info "Cellular connection already exists, removing old configuration..."
        nmcli c delete cellular 2>/dev/null || true
    fi

    # Add GSM connection with US Cellular APN
    log_info "Configuring cellular connection with APN uscc00000.enterprise0.usc-cdp..."
    if nmcli c add type gsm ifname cdc-wdm0 con-name cellular gsm.apn "uscc00000.enterprise0.usc-cdp" 2>/dev/null; then
        log_info "✓ Cellular connection created"
    else
        log_warn "Failed to create cellular connection"
        log_warn "Manually configure with: sudo nmcli c add type gsm ifname cdc-wdm0 con-name cellular gsm.apn \"uscc00000.enterprise0.usc-cdp\""
        return 0  # Non-fatal
    fi

    # Enable autoconnect
    if nmcli c modify cellular connection.autoconnect yes 2>/dev/null; then
        log_info "✓ Cellular autoconnect enabled"
    else
        log_warn "Failed to enable autoconnect"
        return 0  # Non-fatal
    fi

    # Bring up connection
    log_info "Activating cellular connection..."
    if nmcli c up cellular 2>/dev/null; then
        log_info "✓ Cellular connection activated"
    else
        log_warn "Failed to activate cellular connection"
        log_warn "Connection may activate after reboot"
        log_warn "Manually activate with: sudo nmcli c up cellular"
        track_step "4g_modem_configured"
        return 0  # Non-fatal
    fi

    # Wait for interface to come up
    sleep 5

    # Check if wwan0 interface exists and has IP
    if ip addr show wwan0 &>/dev/null; then
        local wwan_ip=$(ip -4 addr show wwan0 2>/dev/null | grep -oP '(?<=inet\s)\d+(\.\d+){3}' | head -n1)

        if [ -n "$wwan_ip" ]; then
            log_info "✓ Cellular interface wwan0 active with IP: $wwan_ip"

            # Test connectivity
            log_info "Testing cellular connectivity..."
            if ping -I wwan0 -c 4 -W 5 8.8.8.8 &>/dev/null; then
                log_info "✓ Cellular connectivity verified (4/4 pings to 8.8.8.8)"
                # Store IP for display in summary
                echo "$wwan_ip" > /tmp/wwan_ip.txt
            else
                log_warn "Cellular interface active but ping test failed"
                log_warn "Network may be establishing connection"
            fi
        else
            log_warn "Interface wwan0 exists but no IP address assigned yet"
            log_warn "Connection may complete after reboot"
        fi
    else
        log_warn "Interface wwan0 not found"
        log_warn "Modem may require reboot to initialize properly"
        REBOOT_REQUIRED=true
    fi

    track_step "4g_modem_configured"
}

# ============================================================================
# WittyPi 4 RTC and Power Management Configuration
# ============================================================================

configure_wittypi() {
    log_info "Installing and configuring WittyPi 4 Mini (RTC + Power Management)..."

    # Install to system-wide location (persistent across reboots)
    local wittypi_dir="/opt/wittypi"
    local original_dir=$(pwd)

    # Create installation directory
    if [ ! -d "$wittypi_dir" ]; then
        mkdir -p "$wittypi_dir"
        log_info "Created WittyPi installation directory: $wittypi_dir"
    fi

    # Change to installation directory
    cd "$wittypi_dir" || {
        log_warn "Failed to access WittyPi installation directory"
        return 0  # Non-fatal
    }

    # Download WittyPi 4 installation script
    log_info "Downloading WittyPi 4 software to $wittypi_dir..."
    if ! wget -q https://www.uugear.com/repo/WittyPi4/install.sh -O install.sh; then
        log_warn "Failed to download WittyPi installation script"
        cd "$original_dir"
        return 0  # Non-fatal
    fi

    # Run installation script (installs to current directory)
    log_info "Running WittyPi installation script..."
    log_info "This will install i2c-tools, configure I2C, and set up WittyPi software..."

    if sh install.sh 2>&1 | tee /tmp/wittypi_install.log; then
        log_info "✓ WittyPi software installed to $wittypi_dir"
    else
        log_warn "WittyPi installation encountered errors - check /tmp/wittypi_install.log"
        log_warn "Continuing with configuration..."
    fi

    # Return to original directory
    cd "$original_dir"

    # Verify installation
    if [ ! -d "$wittypi_dir/wittypi" ]; then
        log_warn "WittyPi software directory not found at $wittypi_dir/wittypi"
        log_warn "Check /tmp/wittypi_install.log for installation details"
        log_warn "Skipping WittyPi configuration"
        return 0  # Non-fatal
    fi

    log_info "✓ WittyPi installed at: $wittypi_dir/wittypi"

    # Re-enable NTP service (WittyPi installation may have disabled it)
    log_info "Re-enabling NTP for network time synchronization..."
    if timedatectl set-ntp true 2>/dev/null; then
        log_info "✓ NTP service enabled"
    else
        log_warn "Could not enable NTP service"
        log_warn "Manually enable with: sudo timedatectl set-ntp true"
    fi

    # Create one-shot service to configure WittyPi on first boot
    log_info "Creating first-boot WittyPi configuration service..."

    cat > "$SYSTEMD_DIR/wittypi-firstboot.service" <<'EOF'
[Unit]
Description=WittyPi First Boot Configuration (one-time)
After=multi-user.target network.target time-sync.target wittypi.service
RequiresMountsFor=/opt/wittypi
ConditionPathExists=/etc/systemd/system/wittypi-firstboot.service

[Service]
Type=oneshot
ExecStartPre=/bin/sleep 10
ExecStart=/bin/bash -c ' \
    set -e; \
    echo "[WittyPi FirstBoot] Starting configuration..."; \
    \
    WITTYPI_DIR="/opt/wittypi"; \
    \
    if [ ! -f "$WITTYPI_DIR/wittypi/utilities.sh" ]; then \
        echo "[WittyPi FirstBoot] ERROR: utilities.sh not found at $WITTYPI_DIR/wittypi/utilities.sh"; \
        exit 1; \
    fi; \
    \
    echo "[WittyPi FirstBoot] Sourcing utilities from $WITTYPI_DIR/wittypi/utilities.sh..."; \
    . "$WITTYPI_DIR/wittypi/utilities.sh"; \
    echo "[WittyPi FirstBoot] Utilities loaded (I2C_MC_ADDRESS=$I2C_MC_ADDRESS, I2C_CONF_DEFAULT_ON=$I2C_CONF_DEFAULT_ON)"; \
    \
    echo "[WittyPi FirstBoot] Waiting for NTP sync (up to 30 seconds)..."; \
    ntp_synced=false; \
    for i in {1..30}; do \
        if timedatectl status 2>/dev/null | grep -q "System clock synchronized: yes"; then \
            echo "[WittyPi FirstBoot] NTP sync complete after $i seconds"; \
            ntp_synced=true; \
            break; \
        fi; \
        sleep 1; \
    done; \
    \
    if [ "$ntp_synced" = "false" ]; then \
        echo "[WittyPi FirstBoot] WARNING: NTP sync timeout, proceeding with current system time"; \
    fi; \
    \
    echo "[WittyPi FirstBoot] System time before sync: $(get_sys_time)"; \
    echo "[WittyPi FirstBoot] RTC time before sync: $(get_rtc_time)"; \
    \
    echo "[WittyPi FirstBoot] Syncing system time to RTC (system_to_rtc)..."; \
    system_to_rtc; \
    \
    sleep 2; \
    \
    echo "[WittyPi FirstBoot] System time after sync: $(get_sys_time)"; \
    echo "[WittyPi FirstBoot] RTC time after sync: $(get_rtc_time)"; \
    \
    echo "[WittyPi FirstBoot] Setting auto-boot to ON..."; \
    echo "[WittyPi FirstBoot] Command: i2c_write 0x01 $I2C_MC_ADDRESS $I2C_CONF_DEFAULT_ON 0x01"; \
    i2c_write 0x01 "$I2C_MC_ADDRESS" "$I2C_CONF_DEFAULT_ON" 0x01; \
    \
    sleep 2; \
    \
    echo "[WittyPi FirstBoot] Verifying auto-boot setting..."; \
    result=$(i2c_read 0x01 "$I2C_MC_ADDRESS" "$I2C_CONF_DEFAULT_ON"); \
    echo "[WittyPi FirstBoot] Register $I2C_CONF_DEFAULT_ON value: $result"; \
    \
    if [ "$result" = "01" ] || [ "$result" = "0x01" ]; then \
        echo "[WittyPi FirstBoot] ✓ Auto-boot verified as ON"; \
        echo "[WittyPi FirstBoot] Removing first-boot service..."; \
        systemctl disable wittypi-firstboot.service; \
        rm -f /etc/systemd/system/wittypi-firstboot.service; \
        systemctl daemon-reload; \
        echo "[WittyPi FirstBoot] ✓ Configuration complete and service removed"; \
        exit 0; \
    else \
        echo "[WittyPi FirstBoot] ERROR: Auto-boot verification failed (expected 01 or 0x01, got $result)"; \
        echo "[WittyPi FirstBoot] Service will remain enabled for retry on next reboot"; \
        echo "[WittyPi FirstBoot] To manually configure: cd /opt/wittypi/wittypi && sudo ./wittyPi.sh (select 11 -> 1 -> 1)"; \
        exit 1; \
    fi'
StandardOutput=journal
StandardError=journal
TimeoutStartSec=120

[Install]
WantedBy=multi-user.target
EOF

    chmod 644 "$SYSTEMD_DIR/wittypi-firstboot.service"
    chown root:root "$SYSTEMD_DIR/wittypi-firstboot.service"

    # Enable the one-shot service
    systemctl daemon-reload
    if systemctl enable wittypi-firstboot.service 2>/dev/null; then
        log_info "✓ WittyPi first-boot configuration service enabled"
        log_info "✓ On next reboot, WittyPi will be configured automatically"
        log_info "  - RTC will be synced from NTP"
        log_info "  - Auto-boot will be enabled"
        log_info "  - Service will delete itself after completion"
    else
        log_warn "Could not enable wittypi-firstboot service"
    fi

    # Create systemd service to sync system time to RTC before shutdown
    log_info "Creating RTC sync service for shutdown..."

    cat > "$SYSTEMD_DIR/wittypi-rtc-sync.service" <<EOF
[Unit]
Description=Sync system time to WittyPi RTC before shutdown
DefaultDependencies=no
Before=shutdown.target reboot.target halt.target
RequiresMountsFor=$wittypi_dir

[Service]
Type=oneshot
ExecStart=/bin/bash -c 'source $wittypi_dir/wittypi/utilities.sh && system_to_rtc'
StandardOutput=journal
StandardError=journal

[Install]
WantedBy=halt.target reboot.target shutdown.target
EOF

    chmod 644 "$SYSTEMD_DIR/wittypi-rtc-sync.service"
    chown root:root "$SYSTEMD_DIR/wittypi-rtc-sync.service"

    # Create systemd timer for daily RTC sync (keeps RTC accurate over time)
    cat > "$SYSTEMD_DIR/wittypi-rtc-sync.timer" <<EOF
[Unit]
Description=Daily WittyPi RTC synchronization timer
Requires=wittypi-rtc-sync-daily.service

[Timer]
OnCalendar=daily
Persistent=true

[Install]
WantedBy=timers.target
EOF

    cat > "$SYSTEMD_DIR/wittypi-rtc-sync-daily.service" <<EOF
[Unit]
Description=Sync system time to WittyPi RTC (daily)
After=network.target time-sync.target
RequiresMountsFor=$wittypi_dir

[Service]
Type=oneshot
ExecStart=/bin/bash -c 'source $wittypi_dir/wittypi/utilities.sh && system_to_rtc'
StandardOutput=journal
StandardError=journal
EOF

    chmod 644 "$SYSTEMD_DIR/wittypi-rtc-sync.timer"
    chmod 644 "$SYSTEMD_DIR/wittypi-rtc-sync-daily.service"
    chown root:root "$SYSTEMD_DIR/wittypi-rtc-sync.timer"
    chown root:root "$SYSTEMD_DIR/wittypi-rtc-sync-daily.service"

    # Reload systemd and enable services
    systemctl daemon-reload

    if systemctl enable wittypi-rtc-sync.service 2>/dev/null; then
        log_info "✓ RTC sync on shutdown enabled"
    else
        log_warn "Could not enable RTC sync on shutdown"
    fi

    if systemctl enable wittypi-rtc-sync.timer 2>/dev/null; then
        log_info "✓ Daily RTC sync timer enabled"
    else
        log_warn "Could not enable daily RTC sync timer"
    fi

    # Install WittyPi power schedule if available
    log_info "Installing WittyPi power schedule..."

    if [ -f "daily_7am_to_930pm.wpi" ]; then
        # Copy schedule to schedules directory as a template
        cp daily_7am_to_930pm.wpi "$wittypi_dir/wittypi/schedules/"
        chmod 755 "$wittypi_dir/wittypi/schedules/daily_7am_to_930pm.wpi"
        log_info "✓ Copied schedule template to $wittypi_dir/wittypi/schedules/"

        # Copy as active schedule
        cp daily_7am_to_930pm.wpi "$wittypi_dir/wittypi/schedule.wpi"
        chmod 755 "$wittypi_dir/wittypi/schedule.wpi"
        log_info "✓ Installed as active schedule: schedule.wpi"

        # Process the schedule (sets up startup/shutdown times)
        if [ -f "$wittypi_dir/wittypi/runScript.sh" ]; then
            log_info "Processing schedule script..."
            cd "$wittypi_dir/wittypi" || log_warn "Could not change to WittyPi directory"

            # Run the schedule processor
            if bash runScript.sh >> schedule.log 2>&1; then
                log_info "✓ Schedule processed successfully"
                log_info "  Schedule: 7:00 AM to 9:30 PM daily (14.5hr operation)"

                # Return to original directory
                cd "$original_dir"
            else
                log_warn "Schedule processing encountered errors - check $wittypi_dir/wittypi/schedule.log"
                cd "$original_dir"
            fi
        else
            log_warn "runScript.sh not found - schedule will be processed on next boot"
        fi
    else
        log_warn "daily_7am_to_930pm.wpi not found - skipping schedule installation"
        log_info "  You can manually configure schedule later via: sudo $wittypi_dir/wittypi/wittyPi.sh"
    fi

    # Display RTC information
    log_info "RTC Configuration Summary:"
    log_info "  - First-boot service will configure WittyPi after reboot"
    log_info "  - RTC will be synced from NTP on first boot"
    log_info "  - Auto-boot will be enabled on first boot"
    log_info "  - Power schedule: 7:00 AM to 9:30 PM daily (if installed)"
    log_info "  - System clock will sync from RTC on subsequent boots (via WittyPi daemon)"
    log_info "  - RTC updated daily and before shutdown (preserves accurate time)"
    log_info "  - First-boot service will delete itself after successful configuration"

    REBOOT_REQUIRED=true
    log_warn "Reboot required for I2C and WittyPi changes to take full effect"

    track_step "wittypi_configured"
}

# ============================================================================
# Power Monitor Installation and Configuration
# ============================================================================

install_power_monitor() {
    log_info "Installing WittyPi power monitoring service..."

    # Installation directory for power monitor
    local power_monitor_dir="/opt/power-monitor"
    local data_dir="/var/log/power_daily"

    # Create power monitor directory
    if [ ! -d "$power_monitor_dir" ]; then
        mkdir -p "$power_monitor_dir"
        log_info "✓ Created power monitor directory: $power_monitor_dir"
    fi

    # Create data directory for power logs
    if [ ! -d "$data_dir" ]; then
        mkdir -p "$data_dir"
        chmod 755 "$data_dir"
        log_info "✓ Created power data directory: $data_dir"
    fi

    # Check if monitoring script exists in current directory
    if [ ! -f "witty_pi_monitor.sh" ]; then
        log_warn "witty_pi_monitor.sh not found - skipping power monitoring installation"
        return 0  # Non-fatal
    fi

    # Copy monitoring script
    cp witty_pi_monitor.sh "$power_monitor_dir/"
    chmod 755 "$power_monitor_dir/witty_pi_monitor.sh"
    chown root:root "$power_monitor_dir/witty_pi_monitor.sh"
    log_info "✓ Installed witty_pi_monitor.sh to $power_monitor_dir/"

    # Install dependencies for power monitoring
    log_info "Installing power monitoring dependencies..."

    # python3-smbus for INA219 battery monitoring
    if ! python3 -c "import smbus" 2>/dev/null; then
        if run_with_progress "Installing python3-smbus" "apt-get install -y -qq python3-smbus"; then
            log_info "✓ python3-smbus installed"
        else
            log_warn "Failed to install python3-smbus - battery voltage monitoring may not work"
            log_warn "Manually install with: sudo apt install python3-smbus"
        fi
    else
        log_info "✓ python3-smbus already installed"
    fi

    # i2c-tools should already be installed by WittyPi, but verify
    if ! command -v i2cget &> /dev/null; then
        if run_with_progress "Installing i2c-tools" "apt-get install -y -qq i2c-tools"; then
            log_info "✓ i2c-tools installed"
        else
            log_warn "Failed to install i2c-tools"
            log_warn "Manually install with: sudo apt install i2c-tools"
        fi
    else
        log_info "✓ i2c-tools already installed"
    fi

    # Check if service file exists
    if [ ! -f "power-monitor.service" ]; then
        log_warn "power-monitor.service template not found - skipping service installation"
        return 0  # Non-fatal
    fi

    # Copy systemd service file
    cp power-monitor.service "$SYSTEMD_DIR/"
    chmod 644 "$SYSTEMD_DIR/power-monitor.service"
    chown root:root "$SYSTEMD_DIR/power-monitor.service"
    log_info "✓ Installed power-monitor.service"

    # Reload systemd and enable service
    systemctl daemon-reload

    if systemctl enable power-monitor.service 2>/dev/null; then
        log_info "✓ Power monitor service enabled"
    else
        log_warn "Failed to enable power-monitor service"
        return 0  # Non-fatal
    fi

    # Start the service
    if systemctl start power-monitor.service 2>/dev/null; then
        log_info "✓ Power monitor service started"

        # Wait a moment and check status
        sleep 2
        if systemctl is-active power-monitor.service &>/dev/null; then
            log_info "✓ Power monitor is running"
        else
            log_warn "Power monitor service failed to start"
            log_warn "Check logs with: sudo journalctl -u power-monitor -n 50"
        fi
    else
        log_warn "Failed to start power-monitor service"
        log_warn "Service may require WittyPi hardware to be connected"
        log_warn "Manually start with: sudo systemctl start power-monitor"
    fi

    track_step "power_monitor_configured"
    log_info "Power monitoring configuration complete"
    log_info "  - Data directory: $data_dir"
    log_info "  - Monitor script: $power_monitor_dir/witty_pi_monitor.sh"
    log_info "  - Logs: sudo journalctl -u power-monitor -f"
}

# ============================================================================
# Installation Summary (T048)
# ============================================================================

display_summary() {
    local edge_status=$(systemctl is-active edge-sensor.service 2>/dev/null || echo "stopped")
    local web_status=$(systemctl is-active web-portal.service 2>/dev/null || echo "stopped")
    local hostname=$(hostname)
    local version=$(cat VERSION 2>/dev/null || echo "unknown")
    local tailscale_ip=$(cat /tmp/tailscale_ip.txt 2>/dev/null || echo "")
    local tailscale_status=$(tailscale status --json 2>/dev/null | grep -q '"BackendState":"Running"' && echo "connected" || echo "disconnected")
    local wwan_ip=$(cat /tmp/wwan_ip.txt 2>/dev/null || echo "")
    local modem_status=$(systemctl is-active ModemManager 2>/dev/null || echo "inactive")
    local cellular_connection=$(nmcli c show --active 2>/dev/null | grep -q "cellular" && echo "connected" || echo "disconnected")
    local wittypi_installed=$(systemctl is-enabled wittypi.service 2>/dev/null && echo "yes" || echo "no")
    local rtc_status=$(timedatectl 2>/dev/null | grep -q "RTC time" && echo "available" || echo "not configured")
    local ntp_status=$(timedatectl 2>/dev/null | grep "NTP service" | awk '{print $3}' || echo "unknown")
    local timezone=$(timedatectl 2>/dev/null | grep "Time zone" | awk '{print $3}' || echo "unknown")
    local current_time=$(date "+%Y-%m-%d %H:%M:%S %Z" 2>/dev/null || echo "unknown")

    echo ""
    echo "=========================================="
    echo "  OpenCurbOS Installation Complete"
    echo "=========================================="
    echo ""
    echo "Version: $version"
    echo "Site ID: $SITE_ID"
    echo "Stall ID: $STALL_ID"
    echo "Hostname: $hostname"
    echo "Timezone: $timezone"
    echo "Current Time: $current_time"
    echo ""
    echo "Installation Directory: $INSTALL_DIR"
    echo "Configuration File: $ENV_FILE"
    echo ""
    echo "Services:"
    echo "  edge-sensor: $edge_status"
    echo "  web-portal: $web_status"
    echo ""

    # WittyPi information
    if [ "$wittypi_installed" = "yes" ]; then
        local firstboot_enabled=$(systemctl is-enabled wittypi-firstboot.service 2>/dev/null && echo "pending" || echo "completed")
        local rtc_sync_enabled=$(systemctl is-enabled wittypi-rtc-sync.service 2>/dev/null && echo "enabled" || echo "disabled")
        local rtc_timer_enabled=$(systemctl is-enabled wittypi-rtc-sync.timer 2>/dev/null && echo "active" || echo "inactive")

        echo "WittyPi 4 Mini (RTC + Power Management):"
        echo "  Installed: yes"
        echo "  First-Boot Config: $firstboot_enabled"
        echo "  RTC Status: $rtc_status"
        echo "  NTP Service: $ntp_status"
        echo "  RTC Sync on Shutdown: $rtc_sync_enabled"
        echo "  Daily RTC Sync: $rtc_timer_enabled"
        echo "  Auto Boot: will be enabled on first boot"
        echo ""
    fi

    # Tailscale information
    if [[ "$TAILSCALE_CONFIGURE" =~ ^[Yy]$ ]]; then
        if [ -n "$tailscale_ip" ]; then
            echo "Tailscale VPN:"
            echo "  Status: $tailscale_status"
            echo "  IPv4 Address: $tailscale_ip"
            echo "  SSH Enabled: yes"
            echo ""
            echo "Web Portal Access (Tailscale):"
            echo "  https://$tailscale_ip:8443"
            echo "  https://$hostname:8443"
            echo ""
            echo "SSH Access (Tailscale):"
            echo "  ssh admin@$tailscale_ip"
            echo "  ssh admin@$hostname"
            echo ""
        else
            echo "Web Portal Access (Tailscale only):"
            echo "  https://$hostname:8443"
            echo "  https://100.x.x.x:8443 (use your Tailscale IP)"
            echo ""
        fi
    else
        echo "Tailscale VPN: Skipped (not installed)"
        echo ""
        echo "Web Portal Access:"
        echo "  Install Tailscale manually for secure remote access:"
        echo "  curl -fsSL https://tailscale.com/install.sh | sh"
        echo "  sudo tailscale up --ssh"
        echo ""
        echo "  After Tailscale setup:"
        echo "  https://$hostname:8443"
        echo "  https://100.x.x.x:8443 (use your Tailscale IP)"
        echo ""
    fi

    # 4G Modem information
    if [ "$modem_status" = "active" ]; then
        echo "4G Cellular Modem:"
        echo "  ModemManager: $modem_status"
        echo "  Connection: $cellular_connection"
        if [ -n "$wwan_ip" ]; then
            echo "  IPv4 Address: $wwan_ip"
            echo "  Interface: wwan0"
            echo "  APN: uscc00000.enterprise0.usc-cdp"
            echo "  Autoconnect: enabled"
        else
            echo "  Status: configured (may complete after reboot)"
        fi
        echo ""
    fi

    if [[ ! "$MQTT_CONFIGURE" =~ ^[Yy]$ ]]; then
        echo "⚠️  MQTT Configuration Required:"
        echo "  1. Access the web portal using Tailscale"
        echo "  2. Configure MQTT broker settings"
        echo "  3. Edge sensor will start automatically after configuration"
        echo ""
    fi

    echo "Service Logs:"
    echo "  sudo journalctl -u edge-sensor -f"
    echo "  sudo journalctl -u web-portal -f"
    echo ""
    echo "Service Management:"
    echo "  sudo systemctl status edge-sensor"
    echo "  sudo systemctl restart edge-sensor"
    echo "  sudo systemctl status web-portal"
    echo "  sudo systemctl restart web-portal"
    echo ""

    # Show reboot warning if hostname was changed
    if [ "$REBOOT_REQUIRED" = true ]; then
        echo "⚠️  REBOOT RECOMMENDED:"
        echo "  Hostname was changed to: $HOSTNAME"
        echo "  For full effect, reboot the system:"
        echo "  sudo reboot"
        echo ""
    fi

    echo "=========================================="
    echo ""
}

# ============================================================================
# Main Installation
# ============================================================================

main() {
    log_info "OpenCurbOS Edge Sensor Installation Starting..."

    # Check prerequisites
    check_prerequisites

    # Install all system packages upfront (fail-fast if packages unavailable)
    install_system_packages

    # Detect upgrade mode and backup existing config
    if [ -d "$INSTALL_DIR" ]; then
        log_info "Existing installation detected at $INSTALL_DIR"
        UPGRADE_MODE=true
        backup_existing_config
    fi

    # Prompt for configuration
    prompt_configuration

    # Create user and directories
    create_user_and_directories

    # Install system dependencies (OpenGL libs for OpenCV, Bluetooth)
    install_system_dependencies

    # NOTE: Python runtime dependencies NOT needed - Nuitka binaries are standalone
    # All Python packages (cv2, numpy, pydantic, etc.) are embedded in the binary
    # install_runtime_dependencies  # DISABLED - not needed for compiled binaries

    # Generate TLS certificates
    generate_certificates

    # Install binaries
    install_binaries

    # Create configuration file
    create_env_file

    # Install and start systemd services
    install_systemd_services

    # Configure CPU power management for edge deployment
    configure_power_management

    # Configure device hostname
    configure_hostname

    # Configure system timezone
    configure_timezone

    # Configure Tailscale VPN with SSH support (if requested)
    if [[ "$TAILSCALE_CONFIGURE" =~ ^[Yy]$ ]]; then
        configure_tailscale
    else
        log_info "Skipping Tailscale installation (can be installed manually later)"
    fi

    # Configure 4G modem for cellular connectivity
    configure_4g_modem

    # Configure WittyPi 4 Mini RTC and power management
    configure_wittypi

    # Install power monitoring service
    install_power_monitor

    # Disable trap for normal exit (no rollback needed)
    trap - EXIT ERR INT TERM

    # Display installation summary
    display_summary

    # Cleanup temporary files
    rm -f /tmp/tailscale_ip.txt 2>/dev/null || true
    rm -f /tmp/wwan_ip.txt 2>/dev/null || true

    log_info "Installation complete!"
    exit 0
}

# Run main
main "$@"
