#!/bin/sh
set -eu

# Log file for debugging
LOG_FILE="/tmp/disco-install.log"

# Helper function for logging
log_step() {
    echo "-> $1"
}

# Helper function for error exits
error_exit() {
    local error_message="$1"
    echo "Error: $error_message"
    echo ""
    echo "Something went wrong during the installation."
    echo "You can review the full log for details: less /tmp/disco-install.log"
    echo ""
    echo "For help, please join our Discord and share the relevant parts of the log:"
    echo "  https://discord.gg/7J4vb5uUwU"
    echo ""
    exit 1
}

TOTAL_STEPS=7

# Pre-flight checks
log_step "[1/$TOTAL_STEPS] Checking system compatibility..."

# Check if root or sudo available
if [ "$(id -u)" -eq 0 ]; then
    SUDO_CMD=""
else
    SUDO_CMD="sudo"
    # Test passwordless sudo
    if ! $SUDO_CMD -n true 2>/dev/null; then
        error_exit "This script requires passwordless sudo access. Please configure passwordless sudo or run as root."
    fi
fi

# Check OS version
if [ -f /etc/os-release ]; then
    . /etc/os-release
    if [ "$ID" != "ubuntu" ] || [ "$VERSION_ID" != "24.04" ]; then
        error_exit "This installer only supports Ubuntu 24.04 LTS. Detected: $ID $VERSION_ID"
    fi
else
    error_exit "Cannot detect OS version. /etc/os-release not found."
fi

# Check for curl and install if needed
if ! command -v curl >/dev/null 2>&1; then
    log_step "Installing curl..."
    $SUDO_CMD apt-get update -qq >> "$LOG_FILE" 2>&1
    $SUDO_CMD apt-get install -y curl >> "$LOG_FILE" 2>&1
fi

# Check for jq
if ! command -v jq >/dev/null 2>&1; then
    $SUDO_CMD apt-get update -qq >> "$LOG_FILE" 2>&1
    $SUDO_CMD apt-get install -y jq >> "$LOG_FILE" 2>&1
fi

# Check if Docker is already installed (likely means installer was already run)
if command -v docker >/dev/null 2>&1; then
    error_exit "Docker is already installed on this system. This installer is designed for fresh Ubuntu 24.04 servers. If you've already run this installer, your Disco instance should already be set up."
fi

# Provision domain
log_step "[2/$TOTAL_STEPS] Provisioning secure domain..."

DOMAIN_RESPONSE=$(curl -sSL -X POST https://backend.disco.cloud/api/installer/provision-domain)
DISCO_HOST=$(echo "$DOMAIN_RESPONSE" | jq -r '.domain')
PUBLIC_IP=$(echo "$DOMAIN_RESPONSE" | jq -r '.public_ip')

if [ -z "$DISCO_HOST" ] || [ "$DISCO_HOST" = "null" ]; then
    echo "Response: $DOMAIN_RESPONSE" >> "$LOG_FILE"
    error_exit "Failed to provision domain."
fi

if [ -z "$PUBLIC_IP" ] || [ "$PUBLIC_IP" = "null" ]; then
    echo "Response: $DOMAIN_RESPONSE" >> "$LOG_FILE"
    error_exit "Failed to get public IP from provisioning service."
fi

# Install Docker (using convenience script with retry for apt locks)
if ! command -v docker >/dev/null 2>&1; then
    log_step "[3/$TOTAL_STEPS] Installing Docker..."

    export DEBIAN_FRONTEND=noninteractive
    curl -fsSL https://get.docker.com -o /tmp/get-docker.sh

    # Retry if apt locks prevent installation
    MAX_DOCKER_ATTEMPTS=10
    DOCKER_ATTEMPT=0
    while [ $DOCKER_ATTEMPT -lt $MAX_DOCKER_ATTEMPTS ]; do
        if $SUDO_CMD sh /tmp/get-docker.sh >> "$LOG_FILE" 2>&1; then
            break
        fi
        # Check if it was an apt lock issue
        if tail -5 "$LOG_FILE" | grep -q "lock"; then
            DOCKER_ATTEMPT=$((DOCKER_ATTEMPT + 1))
            if [ $DOCKER_ATTEMPT -lt $MAX_DOCKER_ATTEMPTS ]; then
                sleep 5
                continue
            fi
        fi
        # Non-lock error or max attempts reached
        error_exit "Failed to install Docker. The Docker installation script failed."
    done

    rm /tmp/get-docker.sh
fi

# Initialize Disco daemon
log_step "[4/$TOTAL_STEPS] Starting Disco daemon..."

DISCO_IMAGE="letsdiscodev/daemon:latest"

INIT_OUTPUT=$($SUDO_CMD docker run \
    --rm \
    --mount source=disco-data,target=/disco/data \
    --mount type=bind,source=/var/run,target=/host/var/run \
    --mount type=bind,source=/etc,target=/host/etc \
    --mount type=bind,source=$HOME,target=/host/$HOME \
    --mount source=disco-caddy-init-config,target=/initconfig \
    --mount type=bind,source=/var/run/docker.sock,target=/var/run/docker.sock \
    --env DISCO_HOST="$DISCO_HOST" \
    --env DISCO_ADVERTISE_ADDR="$PUBLIC_IP" \
    --env HOST_HOME=$HOME \
    --env DISCO_IMAGE=$DISCO_IMAGE \
    $DISCO_IMAGE \
    disco_init 2>&1)

# Extract API key from output
API_KEY=$(echo "$INIT_OUTPUT" | grep -o 'Created API key: [a-z0-9]\{32\}' | awk '{print $4}')

if [ -z "$API_KEY" ]; then
    echo "Output:" >> "$LOG_FILE"
    echo "$INIT_OUTPUT" >> "$LOG_FILE"
    error_exit "Failed to extract API key from initialization output."
fi

# Wait for daemon to become available (SSL cert + API ready)
log_step "[5/$TOTAL_STEPS] Waiting for Disco daemon to become available..."

MAX_ATTEMPTS=20  # 20 attempts * 15 seconds (10s timeout + 5s sleep) = 5 minutes max
ATTEMPT=0

while [ $ATTEMPT -lt $MAX_ATTEMPTS ]; do
    # Use -k to ignore initial self-signed/invalid cert errors during Let's Encrypt setup
    # Check if daemon is responding with {"disco": true}
    # Use --max-time to prevent hanging if port is blocked
    RESPONSE=$(curl -k -sS --max-time 10 "https://$DISCO_HOST/" 2>/dev/null || true)
    if echo "$RESPONSE" | jq -e '.disco == true' >/dev/null 2>&1; then
        break
    fi
    sleep 5
    ATTEMPT=$((ATTEMPT + 1))
done

if [ $ATTEMPT -eq $MAX_ATTEMPTS ]; then
    echo "Timed out waiting for the Disco daemon to become available at https://$DISCO_HOST." >> "$LOG_FILE"
    echo "" >> "$LOG_FILE"
    echo "This can happen for a few reasons:" >> "$LOG_FILE"
    echo "  1. The server is behind a firewall or NAT that is blocking inbound traffic on port 443." >> "$LOG_FILE"
    echo "  2. DNS has not yet propagated for $DISCO_HOST." >> "$LOG_FILE"
    echo "  3. The Disco daemon container failed to start." >> "$LOG_FILE"
    echo "" >> "$LOG_FILE"
    echo "Troubleshooting steps:" >> "$LOG_FILE"
    echo "  - Ensure your server's firewall allows inbound TCP traffic on port 443." >> "$LOG_FILE"
    echo "  - Check container logs with: sudo docker service logs disco" >> "$LOG_FILE"
    error_exit "Timed out waiting for the Disco daemon to become available at https://$DISCO_HOST. This usually means port 443 is blocked by a firewall."
fi

# Generate invite URL from the Disco daemon
log_step "[6/$TOTAL_STEPS] Generating invite link..."

# Encode API key with trailing colon for Basic auth
AUTH_TOKEN=$(echo -n "$API_KEY:" | base64)

INVITE_RESPONSE=$(curl -sSL -X POST "https://$DISCO_HOST/api/api-key-invites" \
    -H "Accept: application/json" \
    -H "Authorization: Basic $AUTH_TOKEN" \
    -H "Content-Type: application/json" \
    -d '{"name": "installer.sh-ui"}')

INVITE_URL=$(echo "$INVITE_RESPONSE" | jq -r '.apiKeyInvite.url')

if [ -z "$INVITE_URL" ] || [ "$INVITE_URL" = "null" ]; then
    echo "Response: $INVITE_RESPONSE" >> "$LOG_FILE"
    error_exit "Failed to generate invite URL."
fi

# Generate final dashboard URL
FINAL_URL="https://dashboard.disco.cloud/accept-invite?inviteUrl=$INVITE_URL"

# Shorten the URL via the backend
SHORT_URL_RESPONSE=$(curl -sSL -X POST "https://backend.disco.cloud/api/short-urls" \
    -H "Content-Type: application/json" \
    -d "{\"url\":\"$FINAL_URL\"}" 2>/dev/null || echo "")

# Extract the short URL from the response, fallback to FINAL_URL if it fails
if [ -n "$SHORT_URL_RESPONSE" ]; then
    SHORT_URL=$(echo "$SHORT_URL_RESPONSE" | jq -r '.url' 2>/dev/null || echo "$FINAL_URL")
    if [ -z "$SHORT_URL" ] || [ "$SHORT_URL" = "null" ]; then
        SHORT_URL="$FINAL_URL"
    fi
else
    SHORT_URL="$FINAL_URL"
fi

log_step "[7/$TOTAL_STEPS] Installation complete!"
echo ""
echo "Visit this URL to complete setup:"
echo ""
echo "  $SHORT_URL"
echo ""
echo "You can now exit SSH - your Disco instance is running."
echo ""
