#!/bin/bash
# ============================================================================
# Dream Server Installer — Orchestrator
# ============================================================================
# Unified installer - voice-enabled by default, uses docker-compose.yml
# profiles for optional features.
# Mission: M5 (Clonable Dream Setup Server)
#
# This file sources library modules (pure functions, no side effects) then
# runs each install phase in order.  Individual modules live under:
#   installers/lib/      — reusable function libraries
#   installers/phases/   — sequential install steps (execute on source)
#
# See each module's header for what it expects and provides.
# ============================================================================

set -euo pipefail

#=============================================================================
# Cleanup on Failure
#=============================================================================
# Track what phases have completed so we can provide useful context on failure.
export INSTALL_PHASE="init"
cleanup_on_error() {
    local exit_code=$?
    echo ""
    echo -e "\033[0;31m[ERROR] Installation failed during phase: ${INSTALL_PHASE}\033[0m"
    echo -e "\033[0;33m        Log file: ${LOG_FILE:-/tmp/dream-server-install.log}\033[0m"
    echo ""
    echo "The install did not complete. Partial state may exist at:"
    echo "  ${INSTALL_DIR:-~/dream-server}"
    echo ""
    echo "To retry, run the installer again. It will resume safely."
    echo "To start fresh, remove the install directory first:"
    echo "  rm -rf ${INSTALL_DIR:-~/dream-server} && ./install.sh"
    exit "$exit_code"
}
trap cleanup_on_error ERR

#=============================================================================
# Interrupt Protection
#=============================================================================
# Accidental keypresses (Ctrl+C, Ctrl+Z) shouldn't silently kill the install.
# We require a double-tap of Ctrl+C within 3 seconds to actually abort.
LAST_SIGINT=0
interrupt_handler() {
    local now
    now=$(date +%s)
    if (( now - LAST_SIGINT <= 3 )); then
        echo ""
        echo -e "\033[0;33m[!] Install cancelled by user.\033[0m"
        echo -e "\033[0;32m    Log file: ${LOG_FILE:-/tmp/dream-server-install.log}\033[0m"
        exit 130
    fi
    LAST_SIGINT=$now
    echo ""
    echo -e "\033[0;33m[!] Press Ctrl+C again within 3 seconds to cancel the install.\033[0m"
}
trap interrupt_handler INT
# Ignore Ctrl+Z (SIGTSTP) entirely — backgrounding the installer breaks things
trap '' TSTP

#=============================================================================
# Load libraries (pure functions, no side effects)
#=============================================================================
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"

source "$SCRIPT_DIR/installers/lib/constants.sh"
source "$SCRIPT_DIR/installers/lib/logging.sh"
source "$SCRIPT_DIR/installers/lib/ui.sh"
source "$SCRIPT_DIR/installers/lib/detection.sh"
source "$SCRIPT_DIR/installers/lib/host-arch.sh"
source "$SCRIPT_DIR/installers/lib/tier-map.sh"
source "$SCRIPT_DIR/installers/lib/docker-images.sh"
source "$SCRIPT_DIR/installers/lib/compose-select.sh"
source "$SCRIPT_DIR/installers/lib/compose-failure-report.sh"
source "$SCRIPT_DIR/installers/lib/readiness-summary.sh"
source "$SCRIPT_DIR/installers/lib/packaging.sh"
source "$SCRIPT_DIR/installers/lib/progress.sh"
if [[ -f "$SCRIPT_DIR/lib/service-registry.sh" ]]; then 
    source "$SCRIPT_DIR/lib/service-registry.sh" 
    sr_load
    if [[ "${_SR_FAILED:-}" == "true" ]]; then
        warn "Service registry failed to load. Will retry after dependencies are installed."
        _SR_LOADED=false
        _SR_FAILED=false
    fi
fi

#=============================================================================
# Command Line Args
#=============================================================================
DRY_RUN=false
SKIP_DOCKER=false
FORCE=false
TIER=""
ENABLE_VOICE=true
ENABLE_WORKFLOWS=true
ENABLE_RAG=true
# Default agent flipped to Hermes Agent (Nous Research) on 2026-05-12.
# OpenClaw is deprecated and will be removed in the next release; new
# installs no longer enable it by default. Users who explicitly pass
# --openclaw or upgrade an existing install with OpenClaw enabled keep
# it working until the removal release. See docs/MIGRATION-OPENCLAW-TO-HERMES.md.
ENABLE_HERMES=true
ENABLE_OPENCLAW=false
OPENCLAW_EXPLICIT=false
ENABLE_COMFYUI=true
ENABLE_DREAMFORGE=true
# Langfuse (LLM observability) defaults OFF on all tiers because its
# clickhouse + postgres + minio stack adds ~500MB baseline memory that is
# nontrivial even on Tier 3+ systems. Users opt in via --langfuse, --all,
# the Custom menu, or post-install `dream enable langfuse`.
ENABLE_LANGFUSE=false
INTERACTIVE=true
DREAM_MODE="${DREAM_MODE:-local}"
OFFLINE_MODE=false   # M1 integration: fully air-gapped operation
NO_BOOTSTRAP=false  # Skip bootstrap fast-start, download full model in foreground
BIND_ADDRESS="${BIND_ADDRESS:-127.0.0.1}"
SUMMARY_JSON_FILE="${SUMMARY_JSON_FILE:-}"

usage() {
    cat << EOF
Dream Server Installer v${VERSION}

Usage: $0 [OPTIONS]

Options:
    --dry-run         Show what would be done without making changes
    --skip-docker     Skip Docker installation (assume already installed)
    --force           Overwrite existing installation
    --tier N          Force specific tier (1-4) instead of auto-detect
    --cloud           Cloud mode: skip GPU detection, use LiteLLM + cloud APIs
    --voice           Enable voice services (Whisper + Kokoro)
    --no-voice        Disable voice services
    --workflows       Enable n8n workflow automation
    --no-workflows    Disable n8n workflow automation
    --rag             Enable RAG with Qdrant vector database
    --no-rag          Disable RAG / Qdrant
    --hermes          Enable Hermes Agent (default; new default agent as of 2026-05-12)
    --no-hermes       Disable Hermes Agent
    --openclaw        Enable OpenClaw (DEPRECATED — see docs/MIGRATION-OPENCLAW-TO-HERMES.md)
    --no-openclaw     Disable OpenClaw
    --comfyui         Enable ComfyUI image generation
    --no-comfyui      Disable ComfyUI image generation (saves ~34GB)
    --dreamforge      Enable DreamForge agent system (default)
    --no-dreamforge   Disable DreamForge
    --langfuse        Enable Langfuse LLM observability (off by default)
    --no-langfuse     Explicitly disable Langfuse (for --all overrides)
    --all             Enable all optional services (including Langfuse)
    --non-interactive Run without prompts (use defaults or flags)
    --offline         M1 mode: Configure for fully offline/air-gapped operation
    --lan             Bind services to 0.0.0.0 for LAN access (headless servers)
    --no-bootstrap    Skip bootstrap fast-start (download full model in foreground)
    --summary-json P  Write machine-readable install summary JSON to path P
    -h, --help        Show this help

Tiers:
    1 - Entry Level   (8GB+ VRAM, 7B models)
    2 - Prosumer      (12GB+ VRAM, 14B-32B AWQ models)
    3 - Pro           (24GB+ VRAM, 32B models)
    4 - Enterprise    (48GB+ VRAM or dual GPU, 72B models)

Port Configuration:
    All service ports are configurable via .env (see .env.example).
    Example: WEBUI_PORT=8080 OLLAMA_PORT=11435 ./install.sh

Examples:
    $0                           # Interactive setup
    $0 --tier 2 --voice          # Tier 2 with voice
    $0 --all --non-interactive   # Full stack, no prompts
    $0 --cloud                   # Cloud mode (no GPU needed, uses API keys)
    $0 --offline --all           # Fully offline (M1 mode) with all services
    $0 --dry-run                 # Preview installation

EOF
    exit 0
}

while [[ $# -gt 0 ]]; do
    case $1 in
        --dry-run) DRY_RUN=true; shift ;;
        --skip-docker) SKIP_DOCKER=true; shift ;;
        --force) FORCE=true; shift ;;
        --tier) TIER="$2"; shift 2 ;;
        --cloud) DREAM_MODE="cloud"; shift ;;
        --voice) ENABLE_VOICE=true; shift ;;
        --no-voice) ENABLE_VOICE=false; shift ;;
        --workflows) ENABLE_WORKFLOWS=true; shift ;;
        --no-workflows) ENABLE_WORKFLOWS=false; shift ;;
        --rag) ENABLE_RAG=true; shift ;;
        --no-rag) ENABLE_RAG=false; shift ;;
        --hermes) ENABLE_HERMES=true; shift ;;
        --no-hermes) ENABLE_HERMES=false; shift ;;
        --openclaw) ENABLE_OPENCLAW=true; OPENCLAW_EXPLICIT=true; shift ;;
        --no-openclaw) ENABLE_OPENCLAW=false; OPENCLAW_EXPLICIT=true; shift ;;
        --comfyui) ENABLE_COMFYUI=true; shift ;;
        --no-comfyui) ENABLE_COMFYUI=false; shift ;;
        --dreamforge) ENABLE_DREAMFORGE=true; shift ;;
        --no-dreamforge) ENABLE_DREAMFORGE=false; shift ;;
        --langfuse) ENABLE_LANGFUSE=true; shift ;;
        # NOTE: with --all, --no-langfuse must appear AFTER --all on the command
        # line (flag processing is case-loop ordered, matching comfyui/dreamforge).
        --no-langfuse) ENABLE_LANGFUSE=false; shift ;;
        # --all enables Hermes (the new default agent) but NOT OpenClaw —
        # the deprecated agent is opt-in via --openclaw for the deprecation
        # release. Will be dropped entirely in the removal release.
        --all) ENABLE_VOICE=true; ENABLE_WORKFLOWS=true; ENABLE_RAG=true; ENABLE_HERMES=true; ENABLE_OPENCLAW=false; ENABLE_COMFYUI=true; ENABLE_DREAMFORGE=true; ENABLE_LANGFUSE=true; shift ;;
        --non-interactive) INTERACTIVE=false; shift ;;
        --offline) OFFLINE_MODE=true; shift ;;
        --lan) BIND_ADDRESS="0.0.0.0"; shift ;;
        --no-bootstrap) NO_BOOTSTRAP=true; shift ;;
        --summary-json) SUMMARY_JSON_FILE="$2"; shift 2 ;;
        -h|--help) usage ;;
        *) error "Unknown option: $1" ;;
    esac
done

if [[ "$OPENCLAW_EXPLICIT" != "true" && -f "$INSTALL_DIR/extensions/services/openclaw/compose.yaml" ]]; then
    ENABLE_OPENCLAW=true
    log "Existing OpenClaw install detected; preserving it for this deprecation release"
fi

# Detect distro + package manager (after arg parsing so --help still shows
# the correct VERSION before /etc/os-release overwrites it)
detect_pkg_manager

#=============================================================================
# Splash
#=============================================================================
show_stranger_boot
[[ "$INTERACTIVE" == "true" ]] && sleep 5

$DRY_RUN && echo -e "${AMB}>>> DRY RUN MODE — I will simulate everything. No changes made. <<<${NC}\n"

#=============================================================================
# Run phases
#=============================================================================
INSTALL_PHASE="01-preflight";    source "$SCRIPT_DIR/installers/phases/01-preflight.sh"
INSTALL_PHASE="02-detection";    source "$SCRIPT_DIR/installers/phases/02-detection.sh"
INSTALL_PHASE="03-features";     source "$SCRIPT_DIR/installers/phases/03-features.sh"
INSTALL_PHASE="04-requirements"; source "$SCRIPT_DIR/installers/phases/04-requirements.sh"
INSTALL_PHASE="05-docker";       source "$SCRIPT_DIR/installers/phases/05-docker.sh"
INSTALL_PHASE="06-directories";  source "$SCRIPT_DIR/installers/phases/06-directories.sh"
INSTALL_PHASE="07-devtools";     source "$SCRIPT_DIR/installers/phases/07-devtools.sh"
INSTALL_PHASE="08-images";       source "$SCRIPT_DIR/installers/phases/08-images.sh"
INSTALL_PHASE="09-offline";      source "$SCRIPT_DIR/installers/phases/09-offline.sh"
INSTALL_PHASE="10-amd-tuning";   source "$SCRIPT_DIR/installers/phases/10-amd-tuning.sh"
INSTALL_PHASE="11-services";     source "$SCRIPT_DIR/installers/phases/11-services.sh"
INSTALL_PHASE="12-health";       source "$SCRIPT_DIR/installers/phases/12-health.sh"
# Phase 13 is informational (URLs, shortcuts, preflight). It must never fail
# the install — any error here is cosmetic. Run with set +e to prevent
# stray non-zero exit codes (e.g., a crashing privacy-shield health probe)
# from triggering the cleanup_on_error trap.
INSTALL_PHASE="13-summary"
set +e
source "$SCRIPT_DIR/installers/phases/13-summary.sh"
set -e
