#!/bin/bash
# ============================================================
# Hytale F2P Dedicated Server - One-Click Starter
# ============================================================
# Just run: ./start.sh
#
# The script will:
#   1. Look for game files in your F2P launcher install
#   2. Auto-download anything missing
#   3. Auto-update server, assets, and agent on each launch
#   4. Fetch auth tokens and start the server
# ============================================================

set -e

# Configuration (edit these or set as environment variables)
HYTALE_AUTH_DOMAIN="${HYTALE_AUTH_DOMAIN:-auth.sanasol.ws}"
AUTH_SERVER="${AUTH_SERVER:-https://$HYTALE_AUTH_DOMAIN}"
SERVER_NAME="${SERVER_NAME:-My Hytale Server}"
BIND_ADDRESS="${BIND_ADDRESS:-0.0.0.0:5520}"
AUTH_MODE="${AUTH_MODE:-authenticated}"

# Download URLs
DOWNLOAD_BASE="${DOWNLOAD_BASE:-https://download.sanasol.ws/download}"
AGENT_URL="https://github.com/sanasol/hytale-auth-server/releases/latest/download/dualauth-agent.jar"
AGENT_VERSION_API="https://api.github.com/repos/sanasol/hytale-auth-server/releases/latest"

# File names (in current directory)
AGENT_JAR="dualauth-agent.jar"
SERVER_JAR="HytaleServer.jar"
ASSETS_FILE="Assets.zip"
ASSETS_PATH="${ASSETS_PATH:-./Assets.zip}"
VERSION_DIR=".versions"

echo "============================================================"
echo "  Hytale F2P Dedicated Server"
echo "============================================================"
echo ""

# --- Prerequisite Checks ---

if ! command -v curl &>/dev/null; then
    echo "[ERROR] curl is required but not found"
    echo "        Install: sudo apt install curl"
    exit 1
fi

mkdir -p "$VERSION_DIR"

# --- Find Local F2P Launcher Install ---

get_f2p_default_dir() {
    case "$(uname -s)" in
        Darwin) echo "$HOME/Library/Application Support/HytaleF2P" ;;
        Linux)  echo "$HOME/.hytalef2p" ;;
        *)      return 1 ;;
    esac
}

# Read a JSON string field from config.json using available tools
read_config_field() {
    local config_file="$1" field="$2"
    if [ ! -f "$config_file" ]; then return 1; fi

    if command -v python3 &>/dev/null; then
        python3 -c "
import json
try:
    c = json.load(open('$config_file'))
    v = c.get('$field', '').strip()
    if v: print(v)
except: pass
" 2>/dev/null
    elif command -v jq &>/dev/null; then
        jq -r ".$field // empty" "$config_file" 2>/dev/null
    else
        grep -o "\"$field\"[[:space:]]*:[[:space:]]*\"[^\"]*\"" "$config_file" 2>/dev/null | cut -d'"' -f4
    fi
}

find_f2p_install() {
    local default_app_dir
    default_app_dir=$(get_f2p_default_dir) || return 1

    local search_dirs=()

    # Check config.json for custom installPath
    local config_file="$default_app_dir/config.json"
    local custom_path
    custom_path=$(read_config_field "$config_file" "installPath")
    if [ -n "$custom_path" ]; then
        local custom_f2p="$custom_path/HytaleF2P"
        if [ -d "$custom_f2p" ]; then
            search_dirs+=("$custom_f2p")
        fi
    fi

    # Always also check default location
    search_dirs+=("$default_app_dir")

    for base in "${search_dirs[@]}"; do
        for branch in "release" "pre-release"; do
            local game_dir="$base/$branch/package/game/latest"
            if [ -d "$game_dir" ]; then
                echo "$game_dir"
                return 0
            fi
        done
    done
    return 1
}

# Find bundled JRE from F2P launcher install
find_f2p_java() {
    local default_app_dir
    default_app_dir=$(get_f2p_default_dir) || return 1

    local search_dirs=()

    # Check config.json for custom javaPath first
    local config_file="$default_app_dir/config.json"
    local custom_java
    custom_java=$(read_config_field "$config_file" "javaPath")
    if [ -n "$custom_java" ] && [ -x "$custom_java" ]; then
        echo "$custom_java"
        return 0
    fi

    # Check custom installPath
    local custom_path
    custom_path=$(read_config_field "$config_file" "installPath")
    if [ -n "$custom_path" ]; then
        local custom_f2p="$custom_path/HytaleF2P"
        [ -d "$custom_f2p" ] && search_dirs+=("$custom_f2p")
    fi

    search_dirs+=("$default_app_dir")

    for base in "${search_dirs[@]}"; do
        for branch in "release" "pre-release"; do
            local jre_dir="$base/$branch/package/jre/latest"
            # Standard path
            if [ -x "$jre_dir/bin/java" ]; then
                echo "$jre_dir/bin/java"
                return 0
            fi
            # macOS bundle path
            if [ -x "$jre_dir/Contents/Home/bin/java" ]; then
                echo "$jre_dir/Contents/Home/bin/java"
                return 0
            fi
        done
    done
    return 1
}

copy_from_f2p() {
    local file="$1" local_path="$2" f2p_path="$3"

    if [ -f "$local_path" ]; then
        return 1  # Already exists locally
    fi

    if [ -f "$f2p_path" ]; then
        echo "[INFO] Found $file in F2P launcher install"
        echo "[INFO]   Copying from: $f2p_path"
        cp "$f2p_path" "$local_path"
        echo "[INFO]   Copied successfully"
        return 0
    fi
    return 1
}

# --- Detect Java ---

JAVA_CMD="java"

# Try F2P bundled JRE first
F2P_JAVA=$(find_f2p_java 2>/dev/null) || true
if [ -n "$F2P_JAVA" ]; then
    echo "[INFO] Found Java in F2P launcher: $F2P_JAVA"
    JAVA_CMD="$F2P_JAVA"
elif ! command -v java &>/dev/null; then
    echo "[ERROR] Java is not installed and no F2P launcher JRE found"
    echo ""
    echo "  Options:"
    echo "    1. Install the F2P launcher first (it includes Java)"
    echo "    2. Install Java 25+ manually:"
    echo "       Windows:       https://download.oracle.com/java/25/latest/jdk-25_windows-x64_bin.exe"
    echo "       macOS:         brew install openjdk"
    echo "       Ubuntu/Debian: sudo apt install openjdk-25-jre"
    echo ""
    exit 1
fi

# Check Java version
JAVA_FULL=$("$JAVA_CMD" -version 2>&1 | head -1)
JAVA_VER=$(echo "$JAVA_FULL" | grep -oP '(?<=")\d+' 2>/dev/null || echo "$JAVA_FULL" | sed 's/.*"\([0-9]*\).*/\1/')
echo "[INFO] Java: $JAVA_FULL"
if [ -n "$JAVA_VER" ] && [ "$JAVA_VER" -lt 25 ] 2>/dev/null; then
    echo "[ERROR] Java $JAVA_VER detected. Java 25+ is REQUIRED."
    echo "        The DualAuth agent requires Java 25 (class file version 69)."
    echo ""
    if [ -n "$F2P_JAVA" ]; then
        echo "  Your F2P launcher JRE is outdated. Update the launcher to get a newer Java."
    else
        echo "  Install Java 25+:"
        echo "    Windows:       https://download.oracle.com/java/25/latest/jdk-25_windows-x64_bin.exe"
        echo "    macOS:         brew install openjdk"
        echo "    Ubuntu/Debian: sudo apt install openjdk-25-jre"
    fi
    echo ""
    exit 1
fi

# --- Find F2P Game Files ---

F2P_DIR=""
if F2P_DIR=$(find_f2p_install 2>/dev/null); then
    echo "[INFO] Found F2P launcher game files: $F2P_DIR"

    # Try to copy HytaleServer.jar from F2P install
    copy_from_f2p "HytaleServer.jar" "$SERVER_JAR" "$F2P_DIR/Server/HytaleServer.jar" || true

    # Try to copy Assets.zip from F2P install
    copy_from_f2p "Assets.zip" "$ASSETS_PATH" "$F2P_DIR/Assets.zip" || true

    echo ""
else
    echo "[INFO] No F2P launcher install found, will download files"
    echo ""
fi

# --- Download / Update Functions ---

get_remote_version() {
    local url="$1"
    local headers
    headers=$(curl -sI -L "$url" --connect-timeout 10 --max-time 15 2>/dev/null | tr -d '\r')
    local etag
    etag=$(echo "$headers" | grep -i "^etag:" | tail -1 | sed 's/^[^:]*: *//' | tr -d '"')
    if [ -n "$etag" ]; then printf '%s' "$etag"; return 0; fi
    local lastmod
    lastmod=$(echo "$headers" | grep -i "^last-modified:" | tail -1 | sed 's/^[^:]*: *//')
    if [ -n "$lastmod" ]; then printf '%s' "$lastmod"; return 0; fi
    local length
    length=$(echo "$headers" | grep -i "^content-length:" | tail -1 | sed 's/^[^:]*: *//')
    if [ -n "$length" ]; then printf 'size:%s' "$length"; return 0; fi
    return 1
}

needs_update() {
    local url="$1" dest="$2" name="$3"
    local version_file="${VERSION_DIR}/${name}.version"

    if [ ! -f "$dest" ]; then
        echo "[INFO] $name not found, will download"
        return 0
    fi

    echo "[INFO] Checking for $name updates..."
    local remote_version
    remote_version=$(get_remote_version "$url" 2>/dev/null) || true
    if [ -z "$remote_version" ]; then
        echo "[INFO] Could not check for updates, using existing $name"
        return 1
    fi

    local local_version=""
    [ -f "$version_file" ] && local_version=$(cat "$version_file" 2>/dev/null)

    if [ "$remote_version" = "$local_version" ]; then
        echo "[INFO] $name is up to date"
        return 1
    fi

    if [ -n "$local_version" ]; then
        echo "[INFO] $name update available"
    fi
    return 0
}

save_version() {
    local url="$1" name="$2"
    local version_file="${VERSION_DIR}/${name}.version"
    local ver
    ver=$(get_remote_version "$url" 2>/dev/null) || true
    [ -n "$ver" ] && printf '%s\n' "$ver" > "$version_file"
}

download_file() {
    local url="$1" dest="$2" name="$3" expected_mb="${4:-0}"
    local tmp="${dest}.tmp"

    echo "[INFO] Downloading $name..."
    [ "$expected_mb" -gt 0 ] 2>/dev/null && echo "[INFO]   Expected size: ~${expected_mb} MB"

    for attempt in 1 2 3; do
        rm -f "$tmp" 2>/dev/null || true

        if [ "$expected_mb" -gt 50 ] 2>/dev/null; then
            curl -fL --progress-bar -o "$tmp" "$url" --connect-timeout 15 --max-time 3600 2>&1
        else
            curl -fL -# -o "$tmp" "$url" --connect-timeout 15 --max-time 300 2>&1
        fi

        if [ $? -eq 0 ] && [ -f "$tmp" ]; then
            local size
            size=$(stat -c%s "$tmp" 2>/dev/null || stat -f%z "$tmp" 2>/dev/null || echo 0)
            if [ "$size" -gt 1000 ]; then
                mv -f "$tmp" "$dest"
                local mb=$((size / 1024 / 1024))
                echo "[INFO] $name downloaded (${mb} MB)"
                return 0
            fi
            echo "[WARN] $name download too small (${size} bytes), retrying..."
        fi

        echo "[WARN] Download attempt $attempt failed, retrying..."
        rm -f "$tmp" 2>/dev/null || true
        sleep 2
    done

    echo "[ERROR] Failed to download $name after 3 attempts"
    rm -f "$tmp" 2>/dev/null || true
    return 1
}

# --- Download / Update Server Files ---

# HytaleServer.jar
JAR_URL="${DOWNLOAD_BASE}/HytaleServer.jar"
if needs_update "$JAR_URL" "$SERVER_JAR" "HytaleServer.jar"; then
    if download_file "$JAR_URL" "$SERVER_JAR" "HytaleServer.jar" "150"; then
        save_version "$JAR_URL" "HytaleServer.jar"
    else
        if [ ! -f "$SERVER_JAR" ]; then
            echo "[ERROR] HytaleServer.jar is required. Check your internet connection."
            exit 1
        fi
        echo "[WARN] Update failed, using existing HytaleServer.jar"
    fi
fi

# Assets.zip
ASSETS_URL="${DOWNLOAD_BASE}/Assets.zip"
if needs_update "$ASSETS_URL" "$ASSETS_PATH" "Assets.zip"; then
    echo "[INFO] Assets.zip is large (~3.3 GB), this may take a while..."
    if download_file "$ASSETS_URL" "$ASSETS_PATH" "Assets.zip" "3300"; then
        save_version "$ASSETS_URL" "Assets.zip"
    else
        if [ ! -f "$ASSETS_PATH" ]; then
            echo "[ERROR] Assets.zip is required. Check your internet connection."
            exit 1
        fi
        echo "[WARN] Update failed, using existing Assets.zip"
    fi
fi

# DualAuth Agent (uses GitHub releases API for version tracking)
check_agent_update() {
    if [ -f "$AGENT_JAR" ]; then
        local agent_size
        agent_size=$(stat -c%s "$AGENT_JAR" 2>/dev/null || stat -f%z "$AGENT_JAR" 2>/dev/null || echo 0)
        if [ "$agent_size" -lt 10000 ]; then
            echo "[WARN] Agent JAR seems corrupt (${agent_size} bytes), re-downloading..."
            rm -f "$AGENT_JAR"
            return 0
        fi

        local version_file="${VERSION_DIR}/${AGENT_JAR}.version"
        local local_version=""
        [ -f "$version_file" ] && local_version=$(cat "$version_file" 2>/dev/null)

        echo "[INFO] Checking for DualAuth Agent updates..."
        local remote_version
        remote_version=$(curl -sf "$AGENT_VERSION_API" --connect-timeout 5 --max-time 10 2>/dev/null | grep -o '"tag_name":"[^"]*"' | cut -d'"' -f4)

        if [ -n "$remote_version" ]; then
            if [ "$local_version" = "$remote_version" ]; then
                echo "[INFO] DualAuth Agent up to date ($local_version)"
                return 1
            else
                echo "[INFO] Agent update: ${local_version:-unknown} -> $remote_version"
                return 0
            fi
        else
            echo "[INFO] Could not check agent updates, using existing"
            return 1
        fi
    else
        return 0
    fi
}

AGENT_REMOTE_VERSION=""
if check_agent_update; then
    echo "[INFO] Downloading DualAuth Agent..."
    if curl -fL -# -o "${AGENT_JAR}.tmp" "$AGENT_URL" --connect-timeout 15 --max-time 120 2>&1 && [ -f "${AGENT_JAR}.tmp" ]; then
        dl_size=$(stat -c%s "${AGENT_JAR}.tmp" 2>/dev/null || stat -f%z "${AGENT_JAR}.tmp" 2>/dev/null || echo 0)
        if [ "$dl_size" -gt 10000 ]; then
            mv -f "${AGENT_JAR}.tmp" "$AGENT_JAR"
            AGENT_REMOTE_VERSION=$(curl -sf "$AGENT_VERSION_API" --connect-timeout 5 --max-time 10 2>/dev/null | grep -o '"tag_name":"[^"]*"' | cut -d'"' -f4)
            [ -n "$AGENT_REMOTE_VERSION" ] && printf '%s\n' "$AGENT_REMOTE_VERSION" > "${VERSION_DIR}/${AGENT_JAR}.version"
            echo "[INFO] DualAuth Agent ready (${AGENT_REMOTE_VERSION:-latest})"
        else
            echo "[WARN] Downloaded agent too small, discarding"
            rm -f "${AGENT_JAR}.tmp"
        fi
    else
        rm -f "${AGENT_JAR}.tmp" 2>/dev/null
        if [ -f "$AGENT_JAR" ]; then
            echo "[WARN] Agent update failed, using existing"
        else
            echo "[ERROR] Failed to download DualAuth Agent"
            echo "[ERROR] Download manually: $AGENT_URL"
            exit 1
        fi
    fi
fi

# --- Final Checks ---

for required in "$SERVER_JAR" "$ASSETS_PATH" "$AGENT_JAR"; do
    if [ ! -f "$required" ]; then
        echo "[ERROR] Required file missing: $required"
        exit 1
    fi
done

# --- Generate Server ID ---

SERVER_ID_FILE=".server-id"
if [ -f "$SERVER_ID_FILE" ]; then
    SERVER_ID=$(cat "$SERVER_ID_FILE")
    echo "[INFO] Server ID: $SERVER_ID"
else
    SERVER_ID=$(cat /proc/sys/kernel/random/uuid 2>/dev/null || uuidgen 2>/dev/null || python3 -c "import uuid; print(uuid.uuid4())" 2>/dev/null)
    if [ -z "$SERVER_ID" ]; then
        echo "[ERROR] Could not generate UUID. Install uuidgen or python3."
        exit 1
    fi
    printf '%s' "$SERVER_ID" > "$SERVER_ID_FILE"
    echo "[INFO] Generated server ID: $SERVER_ID"
fi

# --- Fetch Tokens ---

echo ""
echo "[INFO] Fetching server tokens from $AUTH_SERVER..."

TEMP_RESPONSE=$(mktemp)
curl -s -X POST "$AUTH_SERVER/server/auto-auth" \
    -H "Content-Type: application/json" \
    -d "{\"server_id\": \"$SERVER_ID\", \"server_name\": \"$SERVER_NAME\"}" \
    --connect-timeout 10 \
    --max-time 30 \
    -o "$TEMP_RESPONSE"

if [ $? -ne 0 ]; then
    echo "[ERROR] Failed to connect to auth server at $AUTH_SERVER"
    rm -f "$TEMP_RESPONSE"
    exit 1
fi

if ! grep -q "sessionToken" "$TEMP_RESPONSE" 2>/dev/null; then
    echo "[ERROR] Invalid response from auth server:"
    cat "$TEMP_RESPONSE"
    rm -f "$TEMP_RESPONSE"
    exit 1
fi

# Extract tokens (python3 > jq > grep fallback)
if command -v python3 &>/dev/null; then
    SESSION_TOKEN=$(python3 -c "import json,sys; print(json.load(open('$TEMP_RESPONSE'))['sessionToken'])")
    IDENTITY_TOKEN=$(python3 -c "import json,sys; print(json.load(open('$TEMP_RESPONSE'))['identityToken'])")
elif command -v jq &>/dev/null; then
    SESSION_TOKEN=$(jq -r '.sessionToken' "$TEMP_RESPONSE")
    IDENTITY_TOKEN=$(jq -r '.identityToken' "$TEMP_RESPONSE")
else
    SESSION_TOKEN=$(grep -o '"sessionToken":"[^"]*"' "$TEMP_RESPONSE" | cut -d'"' -f4)
    IDENTITY_TOKEN=$(grep -o '"identityToken":"[^"]*"' "$TEMP_RESPONSE" | cut -d'"' -f4)
fi

rm -f "$TEMP_RESPONSE"

if [ -z "$SESSION_TOKEN" ] || [ -z "$IDENTITY_TOKEN" ]; then
    echo "[ERROR] Could not extract tokens from auth server response"
    exit 1
fi

echo "[INFO] Tokens received successfully"

# --- Start Server ---

JAVA_ARGS=""
[ -n "${JVM_XMS:-}" ] && JAVA_ARGS="$JAVA_ARGS -Xms$JVM_XMS"
[ -n "${JVM_XMX:-}" ] && JAVA_ARGS="$JAVA_ARGS -Xmx$JVM_XMX"

echo ""
echo "============================================================"
echo "  Starting Hytale Server"
echo "  Name:    $SERVER_NAME"
echo "  Bind:    $BIND_ADDRESS"
echo "  Agent:   $AGENT_JAR"
echo "============================================================"
echo ""

exec "$JAVA_CMD" $JAVA_ARGS -javaagent:"$AGENT_JAR" -jar "$SERVER_JAR" \
    --assets "$ASSETS_PATH" \
    --bind "$BIND_ADDRESS" \
    --auth-mode "$AUTH_MODE" \
    --disable-sentry \
    --session-token "$SESSION_TOKEN" \
    --identity-token "$IDENTITY_TOKEN" \
    "$@"
