#!/bin/bash
# Platform detection functions for Mosaic Stack installer
# Provides OS, package manager, architecture, and environment detection

# ============================================================================
# Colors (if terminal supports them)
# ============================================================================

if [[ -t 1 ]]; then
    BOLD='\033[1m'
    ACCENT='\033[38;2;128;90;213m'
    SUCCESS='\033[38;2;47;191;113m'
    WARN='\033[38;2;255;176;32m'
    ERROR='\033[38;2;226;61;45m'
    INFO='\033[38;2;100;149;237m'
    MUTED='\033[38;2;139;127;119m'
    NC='\033[0m'
else
    BOLD=''
    ACCENT=''
    SUCCESS=''
    WARN=''
    ERROR=''
    INFO=''
    MUTED=''
    NC=''
fi

# ============================================================================
# OS Detection
# ============================================================================

# Detect operating system type
# Returns: macos, debian, arch, fedora, linux, unknown
detect_os() {
    case "$OSTYPE" in
        darwin*)
            echo "macos"
            return 0
            ;;
        linux-gnu*)
            if [[ -f /etc/os-release ]]; then
                # shellcheck source=/dev/null
                source /etc/os-release
                case "$ID" in
                    ubuntu|debian|linuxmint|pop|elementary)
                        echo "debian"
                        return 0
                        ;;
                    arch|manjaro|endeavouros|garuda|arcolinux)
                        echo "arch"
                        return 0
                        ;;
                    fedora|rhel|centos|rocky|almalinux|ol)
                        echo "fedora"
                        return 0
                        ;;
                    *)
                        echo "linux"
                        return 0
                        ;;
                esac
            else
                echo "linux"
                return 0
            fi
            ;;
        *)
            echo "unknown"
            return 1
            ;;
    esac
}

# Detect if running under WSL (Windows Subsystem for Linux)
# Returns WSL_DISTRO_NAME if in WSL, empty string otherwise
detect_wsl() {
    if [[ -n "${WSL_DISTRO_NAME:-}" ]]; then
        echo "$WSL_DISTRO_NAME"
        return 0
    fi
    
    # Check for WSL in /proc/version
    if [[ -f /proc/version ]]; then
        if grep -qi "microsoft\|wsl" /proc/version 2>/dev/null; then
            # Try to get distro name from os-release
            if [[ -f /etc/os-release ]]; then
                # shellcheck source=/dev/null
                source /etc/os-release
                echo "${NAME:-WSL}"
                return 0
            fi
            echo "WSL"
            return 0
        fi
    fi
    
    return 1
}

# Check if running in WSL
is_wsl() {
    [[ -n "${WSL_DISTRO_NAME:-}" ]] && return 0
    [[ -f /proc/version ]] && grep -qi "microsoft\|wsl" /proc/version 2>/dev/null
}

# Get human-readable OS name
get_os_name() {
    local os="$1"
    
    case "$os" in
        macos)
            echo "macOS"
            ;;
        debian)
            echo "Debian/Ubuntu"
            ;;
        arch)
            echo "Arch Linux"
            ;;
        fedora)
            echo "Fedora/RHEL"
            ;;
        linux)
            echo "Linux"
            ;;
        *)
            echo "Unknown"
            ;;
    esac
}

# ============================================================================
# Package Manager Detection
# ============================================================================

# Detect the system's package manager
# Returns: brew, apt, pacman, dnf, yum, unknown
detect_package_manager() {
    local os="$1"
    
    # First check for Homebrew (available on macOS and Linux)
    if command -v brew &>/dev/null; then
        echo "brew"
        return 0
    fi
    
    # Fall back to OS-specific package managers
    case "$os" in
        macos)
            # macOS without Homebrew
            echo "none"
            return 1
            ;;
        debian)
            if command -v apt-get &>/dev/null; then
                echo "apt"
                return 0
            fi
            ;;
        arch)
            if command -v pacman &>/dev/null; then
                echo "pacman"
                return 0
            fi
            ;;
        fedora)
            if command -v dnf &>/dev/null; then
                echo "dnf"
                return 0
            elif command -v yum &>/dev/null; then
                echo "yum"
                return 0
            fi
            ;;
    esac
    
    echo "unknown"
    return 1
}

# ============================================================================
# Architecture Detection
# ============================================================================

# Get system architecture
# Returns: x86_64, aarch64, armv7l, armv6l, unknown
get_arch() {
    local arch
    arch=$(uname -m)
    
    case "$arch" in
        x86_64|amd64)
            echo "x86_64"
            ;;
        aarch64|arm64)
            echo "aarch64"
            ;;
        armv7l|armhf)
            echo "armv7l"
            ;;
        armv6l)
            echo "armv6l"
            ;;
        *)
            echo "unknown"
            ;;
    esac
}

# Check if running on Apple Silicon
is_apple_silicon() {
    [[ "$(detect_os)" == "macos" ]] && [[ "$(get_arch)" == "aarch64" ]]
}

# ============================================================================
# Init System Detection
# ============================================================================

# Detect the init system
# Returns: systemd, openrc, launchd, sysvinit, unknown
detect_init_system() {
    local os
    os=$(detect_os)
    
    case "$os" in
        macos)
            echo "launchd"
            return 0
            ;;
    esac
    
    # Check for systemd
    if command -v systemctl &>/dev/null && pidof systemd &>/dev/null; then
        echo "systemd"
        return 0
    fi
    
    # Check for OpenRC
    if command -v rc-status &>/dev/null; then
        echo "openrc"
        return 0
    fi
    
    # Check for SysVinit
    if command -v service &>/dev/null; then
        echo "sysvinit"
        return 0
    fi
    
    echo "unknown"
    return 1
}

# ============================================================================
# Privilege Helpers
# ============================================================================

# Check if running as root
is_root() {
    [[ "$(id -u)" -eq 0 ]]
}

# Run command with sudo only if not already root
maybe_sudo() {
    if is_root; then
        # Skip -E flag when root (env is already preserved)
        if [[ "${1:-}" == "-E" ]]; then
            shift
        fi
        "$@"
    else
        sudo "$@"
    fi
}

# Ensure sudo is available (Linux only)
require_sudo() {
    local os
    os=$(detect_os)
    
    if [[ "$os" == "macos" ]]; then
        return 0
    fi
    
    if is_root; then
        return 0
    fi
    
    if command -v sudo &>/dev/null; then
        return 0
    fi
    
    echo -e "${ERROR}Error: sudo is required for system installs on Linux${NC}"
    echo "Install sudo or re-run as root."
    return 1
}

# Validate sudo credentials (cache them early)
validate_sudo() {
    if is_root; then
        return 0
    fi
    
    if command -v sudo &>/dev/null; then
        sudo -v 2>/dev/null
        return $?
    fi
    
    return 1
}

# ============================================================================
# TTY and Interactive Detection
# ============================================================================

# Check if running in an interactive terminal
is_interactive() {
    [[ -t 0 && -t 1 ]]
}

# Check if we can prompt the user
is_promptable() {
    [[ -r /dev/tty && -w /dev/tty ]]
}

# Read input from TTY (for prompts when stdin is piped)
read_from_tty() {
    local prompt="$1"
    local var_name="$2"
    
    if is_promptable; then
        echo -e "$prompt" > /dev/tty
        read -r "$var_name" < /dev/tty
    else
        return 1
    fi
}

# ============================================================================
# Shell Detection
# ============================================================================

# Get current shell name
get_shell() {
    basename "${SHELL:-/bin/sh}"
}

# Get shell configuration file
get_shell_rc() {
    local shell
    shell=$(get_shell)
    
    case "$shell" in
        zsh)
            echo "$HOME/.zshrc"
            ;;
        bash)
            echo "$HOME/.bashrc"
            ;;
        fish)
            echo "$HOME/.config/fish/config.fish"
            ;;
        *)
            echo "$HOME/.profile"
            ;;
    esac
}

# ============================================================================
# Network and Connectivity
# ============================================================================

# Check if we have internet connectivity
has_internet() {
    local timeout="${1:-5}"
    
    # Try to reach common endpoints
    if command -v curl &>/dev/null; then
        curl -s --max-time "$timeout" https://api.github.com &>/dev/null
        return $?
    elif command -v wget &>/dev/null; then
        wget -q --timeout="$timeout" --spider https://api.github.com
        return $?
    fi
    
    # Fall back to ping
    ping -c 1 -W "$timeout" 8.8.8.8 &>/dev/null 2>&1
}

# Get local IP address
get_local_ip() {
    local ip
    
    # Try hostname command first (macOS)
    if command -v hostname &>/dev/null; then
        ip=$(hostname -I 2>/dev/null | cut -d' ' -f1)
        if [[ -n "$ip" ]]; then
            echo "$ip"
            return 0
        fi
    fi
    
    # Try ip command (Linux)
    if command -v ip &>/dev/null; then
        ip=$(ip route get 1 2>/dev/null | awk '{print $7; exit}')
        if [[ -n "$ip" ]]; then
            echo "$ip"
            return 0
        fi
    fi
    
    # Try ifconfig
    if command -v ifconfig &>/dev/null; then
        ip=$(ifconfig 2>/dev/null | grep -Eo 'inet (addr:)?([0-9]*\.){3}[0-9]*' | grep -Eo '([0-9]*\.){3}[0-9]*' | grep -v '127.0.0.1' | head -1)
        if [[ -n "$ip" ]]; then
            echo "$ip"
            return 0
        fi
    fi
    
    return 1
}

# ============================================================================
# System Resources
# ============================================================================

# Get total RAM in MB
get_total_ram() {
    local os
    os=$(detect_os)
    
    case "$os" in
        macos)
            sysctl -n hw.memsize 2>/dev/null | awk '{print int($1/1024/1024)}'
            ;;
        *)
            if [[ -f /proc/meminfo ]]; then
                awk '/MemTotal/ {print int($2/1024)}' /proc/meminfo
            else
                echo "0"
            fi
            ;;
    esac
}

# Get available disk space in GB for a path
get_available_disk() {
    local path="${1:-.}"
    
    if command -v df &>/dev/null; then
        df -BG "$path" 2>/dev/null | awk 'NR==2 {print $4}' | tr -d 'G'
    else
        echo "0"
    fi
}

# Check if system meets minimum requirements
check_minimum_requirements() {
    local min_ram="${1:-2048}"  # MB
    local min_disk="${2:-10}"   # GB
    
    local ram disk
    ram=$(get_total_ram)
    disk=$(get_available_disk "$HOME")
    
    local errors=()
    
    if [[ "$ram" -lt "$min_ram" ]]; then
        errors+=("RAM: ${ram}MB (minimum: ${min_ram}MB)")
    fi
    
    if [[ "$disk" -lt "$min_disk" ]]; then
        errors+=("Disk: ${disk}GB (minimum: ${min_disk}GB)")
    fi
    
    if [[ ${#errors[@]} -gt 0 ]]; then
        echo "System does not meet minimum requirements:"
        printf '  - %s\n' "${errors[@]}"
        return 1
    fi
    
    return 0
}

# ============================================================================
# Downloader Detection
# ============================================================================

DOWNLOADER=""

detect_downloader() {
    if command -v curl &>/dev/null; then
        DOWNLOADER="curl"
        return 0
    fi
    if command -v wget &>/dev/null; then
        DOWNLOADER="wget"
        return 0
    fi
    echo -e "${ERROR}Error: Missing downloader (curl or wget required)${NC}"
    return 1
}

# Download a file securely
download_file() {
    local url="$1"
    local output="$2"
    
    if [[ -z "$DOWNLOADER" ]]; then
        detect_downloader || return 1
    fi
    
    if [[ "$DOWNLOADER" == "curl" ]]; then
        curl -fsSL --proto '=https' --tlsv1.2 --retry 3 --retry-delay 1 --retry-connrefused -o "$output" "$url"
        return $?
    fi
    
    wget -q --https-only --secure-protocol=TLSv1_2 --tries=3 --timeout=20 -O "$output" "$url"
    return $?
}

# Download and execute a script
run_remote_script() {
    local url="$1"
    local tmp
    tmp=$(mktemp)
    
    if ! download_file "$url" "$tmp"; then
        rm -f "$tmp"
        return 1
    fi
    
    /bin/bash "$tmp"
    local ret=$?
    rm -f "$tmp"
    return $ret
}

# ============================================================================
# PATH Management
# ============================================================================

# Store original PATH for later comparison
ORIGINAL_PATH="${PATH:-}"

# Check if a directory is in PATH
path_has_dir() {
    local path="$1"
    local dir="${2%/}"
    
    [[ -z "$dir" ]] && return 1
    
    case ":${path}:" in
        *":${dir}:"*) return 0 ;;
        *) return 1 ;;
    esac
}

# Add directory to PATH in shell config
add_to_path() {
    local dir="$1"
    local rc="${2:-$(get_shell_rc)}"
    
    if [[ ! -f "$rc" ]]; then
        return 1
    fi
    
    # shellcheck disable=SC2016
    local path_line="export PATH=\"${dir}:\$PATH\""
    
    if ! grep -qF "$dir" "$rc" 2>/dev/null; then
        echo "" >> "$rc"
        echo "# Added by Mosaic Stack installer" >> "$rc"
        echo "$path_line" >> "$rc"
        return 0
    fi
    
    return 1
}

# Warn about missing PATH entries
warn_path_missing() {
    local dir="$1"
    local label="$2"
    
    if [[ -z "$dir" ]]; then
        return 0
    fi
    
    if path_has_dir "$ORIGINAL_PATH" "$dir"; then
        return 0
    fi
    
    echo ""
    echo -e "${WARN}→${NC} PATH warning: missing ${label}: ${INFO}${dir}${NC}"
    echo -e "This can make commands show as \"not found\" in new terminals."
    echo -e "Fix by adding to your shell config:"
    echo -e "  export PATH=\"${dir}:\$PATH\""
}

# ============================================================================
# Temp File Management
# ============================================================================

TMPFILES=()

# Create a tracked temp file
create_temp_file() {
    local f
    f=$(mktemp)
    TMPFILES+=("$f")
    echo "$f"
}

# Create a tracked temp directory
create_temp_dir() {
    local d
    d=$(mktemp -d)
    TMPFILES+=("$d")
    echo "$d"
}

# Cleanup all temp files
cleanup_temp_files() {
    local f
    for f in "${TMPFILES[@]:-}"; do
        rm -rf "$f" 2>/dev/null || true
    done
}

# Set up cleanup trap
setup_cleanup_trap() {
    trap cleanup_temp_files EXIT
}

# ============================================================================
# Platform Summary
# ============================================================================

# Print a summary of the detected platform
print_platform_summary() {
    local os pkg arch init wsl
    
    os=$(detect_os)
    pkg=$(detect_package_manager "$os")
    arch=$(get_arch)
    init=$(detect_init_system)
    wsl=$(detect_wsl)
    
    echo -e "${BOLD}Platform Detection:${NC}"
    echo -e "  OS:           ${INFO}$(get_os_name "$os")${NC}"
    echo -e "  Architecture: ${INFO}$arch${NC}"
    echo -e "  Package Mgr:  ${INFO}$pkg${NC}"
    echo -e "  Init System:  ${INFO}$init${NC}"
    
    if [[ -n "$wsl" ]]; then
        echo -e "  WSL:          ${INFO}$wsl${NC}"
    fi
    
    echo -e "  Shell:        ${INFO}$(get_shell)${NC}"
    echo -e "  RAM:          ${INFO}$(get_total_ram)MB${NC}"
    echo -e "  Disk:         ${INFO}$(get_available_disk)GB available${NC}"
}
