#!/bin/bash
#
# install_for_jruby - Setup for liquidsoap radio automation stack (JRuby edition)
# Target: Linux Mint 22.3 (Ubuntu 24.04 Noble based)
#
# Usage: sudo ./install_for_jruby
# Must be run from within the target directory (e.g., /srv/audio/)
#
# Detects existing Java/JRuby; installs OpenJDK 21 and JRuby 10.1.1.0 into /opt
# only if missing, integrating JRuby via update-alternatives. Installs gems one
# at a time with a raised JVM heap to avoid OOM on low-RAM hosts.
#
set -euo pipefail

# -------------------------------------------------------
# Determine install directory from script location
# -------------------------------------------------------
SCRIPT_PATH="$(readlink -f "${BASH_SOURCE[0]}")"
INSTALL_DIR="$(dirname "$SCRIPT_PATH")"
SERVICE_NAME="$(basename "$INSTALL_DIR")"
LIQUIDSOAP_USER="liquidsoap"

JRUBY_VERSION="10.1.1.0"
JRUBY_HOME="/opt/jruby-${JRUBY_VERSION}"
GEM_HOME_LOCAL="$INSTALL_DIR/.gems"

echo "=== Radio Automation Installer (JRuby) ==="
echo "Install directory: $INSTALL_DIR"
echo "Service name:      $SERVICE_NAME"
echo ""

# -------------------------------------------------------
# 0. Validate environment
# -------------------------------------------------------
if [ "$(id -u)" -ne 0 ]; then
    echo "ERROR: This script must be run as root (sudo)."
    exit 1
fi

if [ ! -d "$INSTALL_DIR" ]; then
    echo "ERROR: Install directory $INSTALL_DIR does not exist."
    exit 1
fi

if [ ! -w "$INSTALL_DIR" ]; then
    echo "ERROR: Cannot write to $INSTALL_DIR. Check permissions."
    exit 1
fi

REQUIRED_DIRS=(
    "music"
    "podcasts"
    "jingles"
    "announcements"
    "playlists"
    "state"
    "logs"
)

# -------------------------------------------------------
# Helper: prompt with default value
# -------------------------------------------------------
prompt() {
    local var_name="$1"
    local prompt_text="$2"
    local default_value="${3:-}"
    local hide_input="${4:-false}"

    if [ -n "$default_value" ]; then
        read -rp "$prompt_text [$default_value]: " input
        if [ -z "$input" ]; then
            eval "$var_name=$default_value"
        else
            eval "$var_name=\$input"
        fi
    else
        if [ "$hide_input" = "true" ]; then
            read -rsp "$prompt_text: " input
            echo ""
            eval "$var_name=\$input"
        else
            read -rp "$prompt_text: " input
            eval "$var_name=\$input"
        fi
    fi
}

prompt_confirm() {
    local var_name="$1"
    local prompt_text="$2"
    local default_value="${3:-no}"
    read -rp "$prompt_text [$default_value]: " input
    if [ -z "$input" ]; then
        input="$default_value"
    fi
    case "$input" in
        [Yy]* ) eval "$var_name=true" ;;
        *      ) eval "$var_name=false" ;;
    esac
}

# -------------------------------------------------------
# 1. Interactive configuration
# -------------------------------------------------------
CONFIG_FILE="$INSTALL_DIR/config.json"

EXISTING_CONFIG=false
if [ -f "$CONFIG_FILE" ]; then
    EXISTING_CONFIG=true
    echo "--- Loading existing config.json for defaults ---"
    ICECAST_HOST_DEFAULT=$(jq -r '.icecast.host // "localhost"' "$CONFIG_FILE")
    ICECAST_PORT_DEFAULT=$(jq -r '.icecast.port // 7777' "$CONFIG_FILE")
    ICECAST_MOUNT_DEFAULT=$(jq -r '.icecast.mount // "/audio.mp3"' "$CONFIG_FILE")
    ICECAST_USERNAME_DEFAULT=$(jq -r '.icecast.username // "source"' "$CONFIG_FILE")
    GPODDER_ENABLE_DEFAULT=$(jq -r 'if .gpodder.enable == true then "yes" else "no" end' "$CONFIG_FILE")
    GPODDER_HOST_DEFAULT=$(jq -r '.gpodder.host // "https://gpodder.net"' "$CONFIG_FILE")
    GPODDER_USERNAME_DEFAULT=$(jq -r '.gpodder.username // ""' "$CONFIG_FILE")
else
    ICECAST_HOST_DEFAULT="localhost"
    ICECAST_PORT_DEFAULT="7777"
    ICECAST_MOUNT_DEFAULT="/audio.mp3"
    ICECAST_USERNAME_DEFAULT="source"
    GPODDER_ENABLE_DEFAULT="no"
    GPODDER_HOST_DEFAULT="https://gpodder.net"
    GPODDER_USERNAME_DEFAULT=""
fi

echo ""
echo "--- Icecast Settings ---"
echo ""

ICECAST_HOST=""
ICECAST_PORT=""
ICECAST_MOUNT=""
ICECAST_USERNAME=""
ICECAST_PASSWORD=""

while true; do
    prompt ICECAST_HOST "Icecast host" "$ICECAST_HOST_DEFAULT"
    [[ "$ICECAST_HOST" =~ ^[a-zA-Z0-9._-]+$ ]] && break
    echo "Invalid hostname. Try again."
done

while true; do
    prompt ICECAST_PORT "Source port" "$ICECAST_PORT_DEFAULT"
    [[ "$ICECAST_PORT" =~ ^[0-9]{1,5}$ ]] && (( ICECAST_PORT >= 1 )) && (( ICECAST_PORT <= 65535 )) && break
    echo "Port must be a number between 1 and 65535. Try again."
done

while true; do
    prompt ICECAST_MOUNT "Mount point" "$ICECAST_MOUNT_DEFAULT"
    [[ "$ICECAST_MOUNT" == /* ]] && break
    echo "Mount point must start with /. Try again."
done

prompt ICECAST_USERNAME "Source username" "$ICECAST_USERNAME_DEFAULT"

while true; do
    prompt ICECAST_PASSWORD "Source password" "" "true"
    [ -n "$ICECAST_PASSWORD" ] && break
    echo "Password cannot be empty. Try again."
done

echo ""
echo "--- gPodder.net Sync Settings ---"
echo ""

GPODDER_ENABLE=false
if [ "$GPODDER_ENABLE_DEFAULT" = "yes" ]; then
    prompt_confirm GPODDER_ENABLE "Enable gPodder.net subscription sync?" "yes"
else
    prompt_confirm GPODDER_ENABLE "Enable gPodder.net subscription sync?" "no"
fi

GPODDER_HOST=""
GPODDER_USERNAME=""
GPODDER_PASSWORD=""

if [ "$GPODDER_ENABLE" = "true" ]; then
    prompt GPODDER_HOST "gPodder host" "$GPODDER_HOST_DEFAULT"
    while true; do
        prompt GPODDER_USERNAME "gPodder username/email" "$GPODDER_USERNAME_DEFAULT"
        [ -n "$GPODDER_USERNAME" ] && break
        echo "Username cannot be empty. Try again."
    done
    while true; do
        prompt GPODDER_PASSWORD "gPodder password" "" "true"
        [ -n "$GPODDER_PASSWORD" ] && break
        echo "Password cannot be empty. Try again."
    done
fi

echo ""
echo "--- Summary ---"
echo "  Icecast:   ${ICECAST_HOST}:${ICECAST_PORT}${ICECAST_MOUNT} (user: ${ICECAST_USERNAME})"
echo "  gPodder:   $( [ "$GPODDER_ENABLE" = "true" ] && echo "enabled (${GPODDER_USERNAME}@${GPODDER_HOST})" || echo "disabled" )"
echo ""

CONFIRM_INSTALL=false
prompt_confirm CONFIRM_INSTALL "Proceed with these settings?" "yes"
if [ "$CONFIRM_INSTALL" != "true" ]; then
    echo "Aborted by user."
    exit 0
fi

# -------------------------------------------------------
# 2. Base system packages (always needed regardless of Java state)
# -------------------------------------------------------
echo ""
echo "--- Installing base system packages ---"
apt-get update
apt-get install -y \
    liquidsoap \
    icecast2 \
    jq \
    curl \
    ca-certificates \
    unzip

# -------------------------------------------------------
# 3. Detect / install Java (OpenJDK 21 headless)
# -------------------------------------------------------
echo ""
echo "--- Checking Java ---"
JAVA_OK=false
if command -v java >/dev/null 2>&1; then
    JAVA_VER_LINE=$(java -version 2>&1 | head -1)
    # Extract major version: handles both '"21.0.x"' and '1.8.0_xxx' styles
    JAVA_MAJOR=$(echo "$JAVA_VER_LINE" | sed -nE 's/.*"([0-9]+)(\.[0-9]+)?".*/\1/p')
    if [ -n "$JAVA_MAJOR" ] && [ "$JAVA_MAJOR" -ge 21 ]; then
        JAVA_OK=true
        echo "  Found suitable Java ($JAVA_VER_LINE)"
    else
        echo "  Java found but version too old ($JAVA_VER_LINE); installing OpenJDK 21."
    fi
else
    echo "  No Java detected; installing OpenJDK 21."
fi

if [ "$JAVA_OK" != "true" ]; then
    apt-get install -y openjdk-21-jre-headless
    echo "  Installed OpenJDK 21."
fi

# -------------------------------------------------------
# 4. Detect / install JRuby
# -------------------------------------------------------
echo ""
echo "--- Checking JRuby ---"
JRUBY_FOUND=""

# Preferred: our pinned home already present
if [ -x "$JRUBY_HOME/bin/jruby" ]; then
    JRUBY_FOUND="$JRUBY_HOME/bin/jruby"
    echo "  Found JRuby at $JRUBY_HOME"
fi

# Fallback: any jruby resolvable on PATH (system, other /opt version, etc.)
if [ -z "$JRUBY_FOUND" ] && command -v jruby >/dev/null 2>&1; then
    DETECTED_JRUBY="$(command -v jruby)"
    DETECTED_JRUBY="$(readlink -f "$DETECTED_JRUBY")"
    JRUBY_FOUND="$DETECTED_JRUBY"
    echo "  Found existing JRuby on PATH: $DETECTED_JRUBY"
    echo "  ($( "$DETECTED_JRUBY" -v 2>/dev/null | head -1 ))"
fi

if [ -z "$JRUBY_FOUND" ]; then
    echo "  No JRuby detected; installing JRuby ${JRUBY_VERSION} to /opt ..."
    JRUBY_TARBALL="/tmp/jruby-dist-${JRUBY_VERSION}-bin.tar.gz"
    # Post-9.1.14.0 releases are distributed via Maven Central as jruby-dist-*
    curl -fsSL "https://repo1.maven.org/maven2/org/jruby/jruby-dist/${JRUBY_VERSION}/jruby-dist-${JRUBY_VERSION}-bin.tar.gz" -o "$JRUBY_TARBALL"
    mkdir -p /opt
    tar -xzf "$JRUBY_TARBALL" -C /opt
    rm -f "$JRUBY_TARBALL"
    ln -sfn "$JRUBY_HOME" /opt/jruby-current
    JRUBY_FOUND="$JRUBY_HOME/bin/jruby"
    echo "  Installed JRuby to $JRUBY_HOME"
fi

# Derive the effective JRuby home from whichever binary we ended up using
EFF_JRUBY_BIN="$JRUBY_FOUND"
EFF_JRUBY_HOME="$(dirname "$(dirname "$EFF_JRUBY_BIN")")"
export PATH="$EFF_JRUBY_HOME/bin:$PATH"

# -------------------------------------------------------
# 5. Integrate JRuby with the system via update-alternatives
# -------------------------------------------------------
echo "--- Integrating JRuby via update-alternatives ---"
update-alternatives --install /usr/local/bin/jruby jruby "$EFF_JRUBY_BIN" 100
# Also expose bundle/gem/rake under alternatives so they resolve system-wide
for tool in bundle gem rake irb; do
    if [ -x "$EFF_JRUBY_HOME/bin/$tool" ]; then
        update-alternatives --install "/usr/local/bin/$tool" "$tool" "$EFF_JRUBY_HOME/bin/$tool" 100
    fi
done
echo "  Registered jruby -> $EFF_JRUBY_BIN (priority 100)"

# Sanity-check that the integrated binary actually runs
if ! "$EFF_JRUBY_BIN" -v >/dev/null 2>&1; then
    echo "ERROR: Integrated JRuby binary failed to execute. Aborting."
    exit 1
fi
echo "  Verified: $("${EFF_JRUBY_BIN}" -v 2>/dev/null | head -1)"

# -------------------------------------------------------
# 6. Install gems into a project-local gem home (one at a time)
# -------------------------------------------------------
echo "--- Installing Ruby gems ---"
export GEM_HOME="$GEM_HOME_LOCAL"
export GEM_PATH="$GEM_HOME_LOCAL"

# Raise the JVM heap for gem operations. JRuby defaults to ~500MB, which is
# too small when bundler resolves multiple gems at once on a low-RAM host.
export JRUBY_OPTS="-J-Xmx1g -J-Xss512k"

install_gem() {
    local name="$1"
    echo "  Installing gem: $name"
    if ! "$EFF_JRUBY_BIN" -S gem install "$name" --no-document; then
        echo "ERROR: Failed to install gem '$name'."
        exit 1
    fi
}

# Install bundler first (needed to drive bundle install), then each dependency
# separately so a single OOM can't take down the whole set.
install_gem bundler
install_gem nokogiri
install_gem sqlite3
install_gem json

# Lock versions against the Gemfile now that every gem is present.
(cd "$INSTALL_DIR" && \
    GEM_HOME="$GEM_HOME_LOCAL" GEM_PATH="$GEM_HOME_LOCAL" \
    JRUBY_OPTS="$JRUBY_OPTS" \
    "$EFF_JRUBY_BIN" -S bundle install --quiet)

echo "  Gems installed to $GEM_HOME_LOCAL"

# -------------------------------------------------------
# 7. Create and verify directory structure
# -------------------------------------------------------
echo "--- Creating directory structure ---"
MISSING=()
for dir in "${REQUIRED_DIRS[@]}"; do
    full_path="$INSTALL_DIR/$dir"
    if [ ! -d "$full_path" ]; then
        mkdir -p "$full_path"
        MISSING+=("$dir")
    fi
done

if [ ${#MISSING[@]} -gt 0 ]; then
    echo "  Created: ${MISSING[*]}"
else
    echo "  All directories already present."
fi

FAILED=()
for dir in "${REQUIRED_DIRS[@]}"; do
    if [ ! -d "$INSTALL_DIR/$dir" ]; then
        FAILED+=("$dir")
    fi
done

if [ ${#FAILED[@]} -gt 0 ]; then
    echo "ERROR: Failed to create directories: ${FAILED[*]}"
    exit 1
fi

# -------------------------------------------------------
# 8. Generate config.json from collected settings
# -------------------------------------------------------
echo "--- Generating config.json ---"

cat > "$CONFIG_FILE" << EOF
{
  "icecast": {
    "host": "${ICECAST_HOST}",
    "port": ${ICECAST_PORT},
    "mount": "${ICECAST_MOUNT}",
    "username": "${ICECAST_USERNAME}",
    "password": "${ICECAST_PASSWORD}"
  },
  "gpodder": {
    "enable": ${GPODDER_ENABLE},
    "host": "${GPODDER_HOST}",
    "username": "${GPODDER_USERNAME}",
    "password": "${GPODDER_PASSWORD}"
  }
}
EOF

chmod 600 "$CONFIG_FILE"
echo "  Written: $CONFIG_FILE"

# -------------------------------------------------------
# 9. Create schedule.txt template (if not present)
# -------------------------------------------------------
SCHEDULE_FILE="$INSTALL_DIR/schedule.txt"
if [ ! -f "$SCHEDULE_FILE" ]; then
    echo "--- Creating schedule.txt template ---"
    cat > "$SCHEDULE_FILE" << 'EOF'
# Radio Schedule
# Format: min hour dom mon dow TYPE TARGET [RUNLENGTH_SECONDS]
# TYPE: show | stream
# RUNLENGTH required for stream entries, ignored for show entries
#
# Examples:
# 0 8 * * 2     show    hardcore_history
# 0 6 * * 1     stream  http://example.org:8000/live.mp3   3600
EOF
else
    echo "--- schedule.txt already exists, skipping ---"
fi

# -------------------------------------------------------
# 10. Verify required project files exist
# -------------------------------------------------------
echo "--- Verifying project files ---"
PROJECT_FILES=("station.liq" "fetch_podcasts.rb" "update_playlists.rb" "Gemfile")
FILE_ERRORS=()
for f in "${PROJECT_FILES[@]}"; do
    if [ ! -f "$INSTALL_DIR/$f" ]; then
        FILE_ERRORS+=("$f")
    else
        echo "  Found: $f"
    fi
done

if [ ${#FILE_ERRORS[@]} -gt 0 ]; then
    echo "WARNING: Missing expected files: ${FILE_ERRORS[*]}"
    echo "         The service will not start until these are in place."
fi

# -------------------------------------------------------
# 11. Create systemd service named after the directory
# -------------------------------------------------------
echo "--- Creating systemd service: ${SERVICE_NAME}.service ---"
cat > "/etc/systemd/system/${SERVICE_NAME}.service" << EOF
[Unit]
Description=Liquidsoap radio automation (${SERVICE_NAME})
After=network.target icecast2.service

[Service]
Type=simple
User=${LIQUIDSOAP_USER}
WorkingDirectory=${INSTALL_DIR}
Environment=GEM_HOME=${GEM_HOME_LOCAL}
Environment=GEM_PATH=${GEM_HOME_LOCAL}
ExecStart=/usr/bin/liquidsoap ${INSTALL_DIR}/station.liq
Restart=on-failure
RestartSec=5
StandardOutput=append:${INSTALL_DIR}/logs/liquidsoap.log
StandardError=append:${INSTALL_DIR}/logs/liquidsoap.log

[Install]
WantedBy=multi-user.target
EOF

# -------------------------------------------------------
# 12. Create cron jobs (hourly fetch + hourly playlist update)
# -------------------------------------------------------
echo "--- Setting up cron jobs ---"

# Cron runs with a minimal PATH, so reference the absolute integrated path.
RUBY_RUN="cd $INSTALL_DIR && GEM_HOME=$GEM_HOME_LOCAL GEM_PATH=$GEM_HOME_LOCAL /usr/local/bin/jruby -S bundle exec"
FETCH_CRON="0 * * * * $RUBY_RUN fetch_podcasts.rb >> $INSTALL_DIR/logs/fetch.log 2>&1"
UPDATE_CRON="30 * * * * $RUBY_RUN update_playlists.rb >> $INSTALL_DIR/logs/update.log 2>&1"

EXISTING_CRON=$(crontab -l -u "$LIQUIDSOAP_USER" 2>/dev/null || true)
CLEANED_CRON=$(echo "$EXISTING_CRON" | grep -v "fetch_podcasts.rb\|update_playlists.rb" || true)

if ! echo "$EXISTING_CRON" | grep -q "fetch_podcasts.rb"; then
    CLEANED_CRON="${CLEANED_CRON:+$CLEANED_CRON
}$FETCH_CRON"
    echo "  Added: fetch_podcasts.rb (hourly)"
else
    echo "  Skipped: fetch_podcasts.rb job already exists"
fi

if ! echo "$EXISTING_CRON" | grep -q "update_playlists.rb"; then
    CLEANED_CRON="${CLEANED_CRON:+$CLEANED_CRON
}$UPDATE_CRON"
    echo "  Added: update_playlists.rb (hourly at :30)"
else
    echo "  Skipped: update_playlists.rb job already exists"
fi

echo "$CLEANED_CRON" | crontab -u "$LIQUIDSOAP_USER" -

# -------------------------------------------------------
# 13. Permissions
# -------------------------------------------------------
echo "--- Setting ownership and permissions ---"
id -u "$LIQUIDSOAP_USER" &>/dev/null || useradd --system --shell /usr/sbin/nologin "$LIQUIDSOAP_USER"
chown -R "${LIQUIDSOAP_USER}:${LIQUIDSOAP_USER}" "$INSTALL_DIR"
chmod 700 "$INSTALL_DIR/state"
chmod 700 "$INSTALL_DIR/logs"

systemctl daemon-reload

# -------------------------------------------------------
# 14. Final verification
# -------------------------------------------------------
echo ""
echo "=== Final Verification ==="
ALL_OK=true
for dir in "${REQUIRED_DIRS[@]}"; do
    if [ -d "$INSTALL_DIR/$dir" ] && [ -r "$INSTALL_DIR/$dir" ] && [ -w "$INSTALL_DIR/$dir" ]; then
        printf "  [OK] %-20s\n" "$dir/"
    else
        printf "  [FAIL] %-20s (missing or inaccessible)\n" "$dir/"
        ALL_OK=false
    fi
done

if [ -f "/etc/systemd/system/${SERVICE_NAME}.service" ]; then
    echo "  [OK] Service file: ${SERVICE_NAME}.service"
else
    echo "  [FAIL] Service file not created"
    ALL_OK=false
fi

if [ -f "$CONFIG_FILE" ]; then
    if jq empty "$CONFIG_FILE" 2>/dev/null; then
        echo "  [OK] Config: config.json (valid JSON)"
    else
        echo "  [FAIL] Config: config.json (invalid JSON)"
        ALL_OK=false
    fi
else
    echo "  [FAIL] Config file missing"
    ALL_OK=false
fi

if [ -f "$SCHEDULE_FILE" ]; then
    echo "  [OK] Schedule: schedule.txt"
else
    echo "  [FAIL] Schedule file missing"
    ALL_OK=false
fi

if command -v java >/dev/null 2>&1; then
    echo "  [OK] Java: $(java -version 2>&1 | head -1)"
else
    echo "  [FAIL] Java not available"
    ALL_OK=false
fi

if [ -x /usr/local/bin/jruby ] && /usr/local/bin/jruby -v >/dev/null 2>&1; then
    echo "  [OK] JRuby: $(/usr/local/bin/jruby -v 2>/dev/null | head -1)"
else
    echo "  [FAIL] Integrated JRuby (/usr/local/bin/jruby) not runnable"
    ALL_OK=false
fi

echo ""
if [ "$ALL_OK" = true ]; then
    echo "=== Installation Complete ==="
else
    echo "=== Installation Finished With Errors ==="
    echo "Review the [FAIL] items above before starting the service."
fi

echo ""
echo "Next steps:"
echo "  1. Place your music in $INSTALL_DIR/music/"
echo "  2. Edit $SCHEDULE_FILE with your show/stream schedule"
echo "  3. Review/edit $INSTALL_DIR/station.liq"
echo "  4. Test: sudo systemctl start icecast2 && sudo systemctl start ${SERVICE_NAME}"
echo "  5. Check logs: tail -f $INSTALL_DIR/logs/liquidsoap.log"
echo ""
echo "Manage shows (run as the liquidsoap user):"
RB_PREFIX="sudo -u liquidsoap env GEM_HOME=$GEM_HOME_LOCAL GEM_PATH=$GEM_HOME_LOCAL /usr/local/bin/jruby -S bundle exec"
echo "  List:        $RB_PREFIX $INSTALL_DIR/fetch_podcasts.rb --list-shows --detail"
echo "  Delete:      $RB_PREFIX $INSTALL_DIR/fetch_podcasts.rb --delete-show <slug>"
echo "  Import OPML: $RB_PREFIX $INSTALL_DIR/fetch_podcasts.rb --import-opml <file.opml>"
echo "  Add show:    $RB_PREFIX $INSTALL_DIR/fetch_podcasts.rb --add-show <feed-url>"
echo "  JSON state:  $RB_PREFIX $INSTALL_DIR/update_playlists.rb --json"
