#!/bin/sh
# shellcheck disable=SC2059,SC2034  # pre-existing printf color style + unused PLATFORM var from original

# Monitorable OpenTelemetry Collector Install Script
# Usage: curl -fsSL https://get-mon.ok9k.com/install.sh | sudo sh -s -- --api-key="your-key"
# Optional: --endpoint=https://ingest.monitorable.io (default)

set -e

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

# Default values
# Defaults to the dedicated ingest surface; the legacy api.monitorable.io/otel
# path still works for installed collectors through the migration window.
ENDPOINT="${MONITORABLE_ENDPOINT:-https://ingest.monitorable.io}"
API_KEY=""
INSTALL_DIR="/opt/monitorable"
CONFIG_DIR="/etc/monitorable"
BINARY_NAME="monitorable-otelcol"
SERVICE_NAME="monitorable-collector"
BASE_URL="https://get-mon.ok9k.com"
VERSION="latest"
USER="monitorable"
GROUP="monitorable"

printf "${BLUE}=🚀 Monitorable OpenTelemetry Collector Installer${NC}\n"
printf "${BLUE}=================================================${NC}\n"

# Parse command line arguments
while [ $# -gt 0 ]; do
    case $1 in
        --endpoint=*)
            ENDPOINT="${1#*=}"
            shift
            ;;
        --api-key=*)
            API_KEY="${1#*=}"
            shift
            ;;
        --version=*)
            VERSION="${1#*=}"
            shift
            ;;
        *)
            printf "${RED}Unknown parameter: $1${NC}\n"
            exit 1
            ;;
    esac
done

# Validate required parameters
if [ -z "$ENDPOINT" ] || [ -z "$API_KEY" ]; then
    printf "${RED}Error: Missing required parameters${NC}\n"
    printf "Usage: $0 --api-key=<api-key> [--endpoint=<endpoint>]\n"
    printf "Default endpoint: https://ingest.monitorable.io\n"
    exit 1
fi

printf "${BLUE}Endpoint:${NC} $ENDPOINT\n"
printf "${BLUE}Version:${NC} $VERSION\n"
printf "\n"

# Check if running as root
if [ "$(id -u)" -ne 0 ]; then
   printf "${RED}This script must be run as root (use sudo)${NC}\n" 
   exit 1
fi

# Detect OS and architecture
OS=$(uname -s | tr '[:upper:]' '[:lower:]')
ARCH=$(uname -m)

case $ARCH in
    x86_64|amd64)
        ARCH="amd64"
        ;;
    arm64|aarch64)
        ARCH="arm64"
        ;;
    *)
        printf "${RED}Unsupported architecture: $ARCH${NC}\n"
        exit 1
        ;;
esac

# Set platform-specific variables for custom collector
case $OS in
    linux)
        COLLECTOR_URL="$BASE_URL/binaries/otel/$VERSION/monitorable-otelcol-linux-$ARCH"
        CONFIG_URL="$BASE_URL/configs/linux/collector-config.yaml"
        SERVICE_FILE_URL="$BASE_URL/configs/linux/monitorable-collector.service"
        PLATFORM="linux"
        ;;
    darwin)
        COLLECTOR_URL="$BASE_URL/binaries/otel/$VERSION/monitorable-otelcol-darwin-$ARCH"
        CONFIG_URL="$BASE_URL/configs/darwin/collector-config.yaml"
        PLATFORM="darwin"
        ;;
    *)
        printf "${RED}Unsupported OS: $OS${NC}\n"
        printf "Supported platforms: Linux, macOS\n"
        printf "For Windows, please use the PowerShell installation script.\n"
        exit 1
        ;;
esac

printf "${BLUE}Detected platform:${NC} $OS/$ARCH\n"
printf "${BLUE}Collector URL:${NC} $COLLECTOR_URL\n"
printf "\n"

# Check if curl is available
if ! command -v curl >/dev/null 2>&1; then
    printf "${RED}curl is required but not installed.${NC}\n"
    printf "Please install curl and try again.\n"
    exit 1
fi

# Create user and group (Linux only)
if [ "$OS" = "linux" ]; then
    printf "${YELLOW}=👤 Creating user and group...${NC}\n"
    
    # Create group if it doesn't exist
    if ! getent group "$GROUP" >/dev/null 2>&1; then
        printf "Creating group: $GROUP\n"
        groupadd --system "$GROUP"
    fi
    
    # Create user if it doesn't exist
    if ! getent passwd "$USER" >/dev/null 2>&1; then
        printf "Creating user: $USER\n"
        useradd --system --gid "$GROUP" --home-dir /var/lib/monitorable \
                --shell /sbin/nologin --comment "Monitorable Collector" "$USER"
    fi
    
    # Add the user to the docker group to allow access to the Docker socket if group docker exists
    if getent group docker >/dev/null 2>&1; then
        printf "Adding $USER to docker group for Docker container metrics...\n"
        usermod -aG docker "$USER"
    fi
fi

# Create installation and configuration directories
printf "${YELLOW}=📁 Creating directories...${NC}\n"
mkdir -p "$INSTALL_DIR" "$CONFIG_DIR" /var/log/monitorable /var/lib/monitorable

# Set up directories and permissions (Linux only)
if [ "$OS" = "linux" ]; then
    printf "${YELLOW}=🔒 Setting up permissions...${NC}\n"
    
    # Set ownership and permissions
    chown "$USER:$GROUP" /var/log/monitorable /var/lib/monitorable
    chmod 755 /var/log/monitorable /var/lib/monitorable
    chown root:root "$CONFIG_DIR"
    chmod 755 "$CONFIG_DIR"
fi

# Download custom Monitorable OpenTelemetry Collector
printf "${YELLOW}=📦 Downloading Monitorable OpenTelemetry Collector...${NC}\n"
# Download to a temp path and atomically mv into place. On a re-run/upgrade the old
# binary may still be executing, and the kernel refuses to overwrite a file that is
# currently running (ETXTBSY, "text file busy"). A rename swaps the directory entry while
# the running process keeps its now-unlinked inode, so an upgrade needs no prior stop.
if curl -fsSL "$COLLECTOR_URL" -o "$INSTALL_DIR/$BINARY_NAME.tmp"; then
    chmod +x "$INSTALL_DIR/$BINARY_NAME.tmp"
    mv -f "$INSTALL_DIR/$BINARY_NAME.tmp" "$INSTALL_DIR/$BINARY_NAME"
    printf "${GREEN}✅ Monitorable OpenTelemetry Collector installed${NC}\n"
else
    rm -f "$INSTALL_DIR/$BINARY_NAME.tmp"
    printf "${RED}❌ Failed to download Monitorable OpenTelemetry Collector${NC}\n"
    exit 1
fi

# Download configuration file
printf "${YELLOW}=⚙️  Downloading configuration file...${NC}\n"
if curl -fsSL "$CONFIG_URL" -o "$CONFIG_DIR/collector-config.yaml"; then
    printf "${GREEN}✅ Configuration file downloaded${NC}\n"
else
    printf "${RED}❌ Failed to download configuration file${NC}\n"
    exit 1
fi

# Set config file permissions
if [ "$OS" = "linux" ]; then
    chown root:"$GROUP" "$CONFIG_DIR/collector-config.yaml"
    chmod 640 "$CONFIG_DIR/collector-config.yaml"
fi

# Linux: also install the launcher script.
if [ "$OS" = "linux" ]; then
    printf "${YELLOW}=⚙️  Downloading launcher...${NC}\n"

    LAUNCHER_URL="$BASE_URL/configs/linux/monitorable-collector-run.sh"
    if curl -fsSL "$LAUNCHER_URL" -o "$INSTALL_DIR/monitorable-collector-run.sh"; then
        chown root:root "$INSTALL_DIR/monitorable-collector-run.sh"
        chmod 755 "$INSTALL_DIR/monitorable-collector-run.sh"
        printf "${GREEN}✅ Launcher installed${NC}\n"
    else
        printf "${RED}❌ Failed to download launcher${NC}\n"
        exit 1
    fi
fi

# SMART disk-health capabilities, tiered by detected hardware. NVMe needs CAP_SYS_ADMIN for
# the admin-passthrough ioctl; its controller char node /dev/nvmeX is 0600 root:root, so
# rather than grant the broad CAP_DAC_OVERRIDE we install a udev rule giving the disk group
# read access (the collector opens O_RDONLY) — least privilege. SATA/SAS gets CAP_SYS_RAWIO
# alone (its /dev/sd* block node is already disk-group readable). No physical disk → nothing.
SMART_CAPS=""
SMART_GROUPS=""
if [ "$OS" = "linux" ]; then
    if ls /sys/block 2>/dev/null | grep -q '^nvme'; then
        SMART_CAPS="CAP_SYS_RAWIO CAP_SYS_ADMIN"
        SMART_GROUPS="disk"
        printf "${BLUE}=💽 NVMe detected — enabling SMART (CAP_SYS_RAWIO + CAP_SYS_ADMIN + disk-group udev rule)${NC}\n"
        # Grant the disk group read access to the NVMe controller char nodes so the
        # unprivileged collector can open them (O_RDONLY) without CAP_DAC_OVERRIDE;
        # CAP_SYS_ADMIN still gates the admin ioctl itself.
        if [ -d /etc/udev/rules.d ]; then
            printf '# Installed by the Monitorable collector. NVMe SMART/Identify are admin ioctls on\n# the controller node /dev/nvmeX (default 0600 root:root). Grant the disk group read\n# access so the unprivileged collector can open it O_RDONLY; CAP_SYS_ADMIN (from the\n# systemd unit) still gates the ioctl itself.\nSUBSYSTEM=="nvme", KERNEL=="nvme[0-9]*", GROUP="disk", MODE="0640"\n' \
                > /etc/udev/rules.d/99-monitorable-nvme-smart.rules
            udevadm control --reload-rules 2>/dev/null && udevadm trigger --subsystem-match=nvme 2>/dev/null || \
                printf "${YELLOW}   (udevadm unavailable — NVMe node permissions will apply on next reboot)${NC}\n"
        fi
    elif ls /sys/block 2>/dev/null | grep -qE '^sd'; then
        SMART_CAPS="CAP_SYS_RAWIO"
        SMART_GROUPS="disk"
        printf "${BLUE}=💽 SATA/SAS detected — enabling SMART (CAP_SYS_RAWIO)${NC}\n"
    else
        printf "${BLUE}=💽 No physical disk detected — SMART disabled (no extra privileges)${NC}\n"
    fi
fi

# Platform-specific service setup
if [ "$OS" = "linux" ]; then
    printf "${YELLOW}=🔧 Setting up systemd service...${NC}\n"
    
    # Download service file template
    if curl -fsSL "$SERVICE_FILE_URL" -o "/tmp/monitorable-collector.service"; then
        # Substitute environment variables (no SERVER_ID needed)
        sed -e "s|__MONITORABLE_API_KEY__|$API_KEY|g" \
            -e "s|__MONITORABLE_ENDPOINT__|$ENDPOINT|g" \
            -e "s|__MONITORABLE_SMART_CAPS__|$SMART_CAPS|g" \
            -e "s|__MONITORABLE_SMART_GROUPS__|$SMART_GROUPS|g" \
            "/tmp/monitorable-collector.service" > "/etc/systemd/system/$SERVICE_NAME.service"
        
        rm "/tmp/monitorable-collector.service"
        
        # Enable and start service
        printf "${YELLOW}=▶️  Starting service...${NC}\n"
        systemctl daemon-reload
        systemctl enable "$SERVICE_NAME"
        # Use restart, not start: on a re-run/upgrade the unit may already be active (or
        # crash-looping), and `start` is a no-op on an active unit — the new binary/config
        # would never load. reset-failed first clears any prior crash-loop counters so the
        # NRestarts check below reflects only this (re)start, not stale history.
        systemctl reset-failed "$SERVICE_NAME" 2>/dev/null || true
        systemctl restart "$SERVICE_NAME"

        # Verify the collector actually STAYS up. With Type=simple, systemd reports
        # "active" the instant ExecStart forks — before the process can fail (bad config,
        # missing capability, a port already in use) — so an immediate is-active check is
        # unreliable. Wait for the unit to settle, then treat a non-active state OR any
        # auto-restart (NRestarts > 0, i.e. it already crashed once) as a failed install.
        sleep 4
        NRESTARTS="$(systemctl show -p NRestarts --value "$SERVICE_NAME" 2>/dev/null || echo 0)"
        if systemctl is-active --quiet "$SERVICE_NAME" && [ "${NRESTARTS:-0}" -eq 0 ]; then
            printf "${GREEN}✅ Service started successfully!${NC}\n"
        else
            printf "${RED}❌ The collector failed to start and is restarting in a loop.${NC}\n"
            printf "${YELLOW}Recent logs:${NC}\n"
            journalctl -u "$SERVICE_NAME" -n 20 --no-pager 2>/dev/null || true
            if journalctl -u "$SERVICE_NAME" -n 20 --no-pager 2>/dev/null | grep -q "address already in use"; then
                printf "\n${YELLOW}A port the collector needs is already in use. Current listeners:${NC}\n"
                if command -v ss >/dev/null 2>&1; then
                    ss -ltnp 2>/dev/null || true
                elif command -v netstat >/dev/null 2>&1; then
                    netstat -ltnp 2>/dev/null || true
                fi
            fi
            printf "\n${YELLOW}Follow the logs with:${NC}\n"
            printf "   journalctl -u $SERVICE_NAME -f\n"
            exit 1
        fi

        printf "\n"
        printf "${GREEN}🎉 Installation complete!${NC}\n"
        printf "\n"
        printf "Service status:\n"
        systemctl status "$SERVICE_NAME" --no-pager -l || true

        printf "\n"
        printf "${BLUE}=📡 The collector is now sending metrics to:${NC}\n"
        printf "   $ENDPOINT/v1/metrics\n"
        printf "\n"
        printf "${BLUE}=📋 To view logs:${NC}\n"
        printf "   journalctl -u $SERVICE_NAME -f\n"
        printf "\n"
        printf "${BLUE}=⏹️  To stop the service:${NC}\n"
        printf "   systemctl stop $SERVICE_NAME\n"
        printf "\n"
        printf "${BLUE}=🔄 To restart the service:${NC}\n"
        printf "   systemctl restart $SERVICE_NAME\n"
        printf "\n"
        printf "${GREEN}=🔐 Security: Collector runs as dedicated '$USER' user with full sandboxing${NC}\n"

    else
        printf "${RED}❌ Failed to download service template${NC}\n"
        exit 1
    fi

elif [ "$OS" = "darwin" ]; then
    printf "${YELLOW}=🔧 Setting up macOS daemon...${NC}\n"
    
    # Create launchd plist
    PLIST_URL="$BASE_URL/configs/darwin/com.monitorable.collector.plist"
    PLIST_FILE="/Library/LaunchDaemons/com.monitorable.collector.plist"
    
    if curl -fsSL "$PLIST_URL" -o "/tmp/com.monitorable.collector.plist"; then
        # Substitute environment variables (no SERVER_ID needed)
        sed -e "s|__MONITORABLE_API_KEY__|$API_KEY|g" \
            -e "s|__MONITORABLE_ENDPOINT__|$ENDPOINT|g" \
            "/tmp/com.monitorable.collector.plist" > "$PLIST_FILE"
        
        rm "/tmp/com.monitorable.collector.plist"
        
        # Set permissions
        chown root:wheel "$PLIST_FILE"
        chmod 644 "$PLIST_FILE"
        
        # Create log directory
        mkdir -p /var/log/monitorable
        
        # Load the daemon. Unload first so a re-run/upgrade re-reads the plist and picks up
        # the new binary (launchctl load on an already-loaded daemon is a no-op).
        launchctl unload "$PLIST_FILE" 2>/dev/null || true
        launchctl load "$PLIST_FILE"
        launchctl start com.monitorable.collector

        # Verify the daemon stays up. A KeepAlive launchd job relaunches a crashing
        # collector, so wait for it to settle, then confirm it has a live PID.
        sleep 4
        if launchctl list com.monitorable.collector 2>/dev/null | grep -q '"PID"'; then
            printf "${GREEN}✅ Daemon started successfully!${NC}\n"
        else
            printf "${RED}❌ The collector failed to start.${NC}\n"
            printf "${YELLOW}Recent logs:${NC}\n"
            tail -n 20 /var/log/monitorable/collector.log 2>/dev/null || true
            exit 1
        fi

        printf "${GREEN}🎉 Installation complete!${NC}\n"
        printf "\n"
        printf "${BLUE}=📡 The collector is now sending metrics to:${NC}\n"
        printf "   $ENDPOINT/v1/metrics\n"
        printf "\n"
        printf "${BLUE}=📋 To view logs:${NC}\n"
        printf "   tail -f /var/log/monitorable/collector.log\n"
        printf "\n"
        printf "${BLUE}=⏹️  To stop the daemon:${NC}\n"
        printf "   launchctl stop com.monitorable.collector\n"
        printf "\n"
        printf "${BLUE}=🔄 To restart the daemon:${NC}\n"
        printf "   launchctl stop com.monitorable.collector && launchctl start com.monitorable.collector\n"

    else
        printf "${RED}❌ Failed to download launchd plist template${NC}\n"
        exit 1
    fi

else
    printf "${GREEN}🎉 Installation complete!${NC}\n"
    printf "\n"
    printf "${BLUE}=▶️  To start the collector manually:${NC}\n"
    printf "   MONITORABLE_API_KEY='$API_KEY' MONITORABLE_ENDPOINT='$ENDPOINT' $INSTALL_DIR/$BINARY_NAME --config=$CONFIG_DIR/collector-config.yaml\n"
    printf "\n"
    printf "${YELLOW}Note: Automatic service creation is only supported on Linux and macOS.${NC}\n"
fi

printf "\n"
printf "${BLUE}=📚 For more information and troubleshooting:${NC}\n"
printf "   https://docs.monitorable.io/collector-installation\n"
printf "\n"
printf "${GREEN}=🎯 Welcome to OpenTelemetry-native monitoring with Monitorable!${NC}\n"
