#!/usr/bin/env bash

set -e

export DEBIAN_FRONTEND=noninteractive

# Automatically restart without asking.
# this gets around needrestart command halting for user input
export RESTART_MODE=l
export POSTHOG_APP_TAG="${POSTHOG_APP_TAG:-latest}"

# Talk to the user
echo "Welcome to the single instance PostHog installer 🦔"
echo ""
echo "⚠️  You REALLY need 8GB or more of memory to run this stack ⚠️"
echo ""
echo "Power user or aspiring power user?"
echo "Check out our docs on deploying PostHog! https://posthog.com/docs/self-host/deploy/hobby"
echo ""

if [ -n "$1" ]
then
    export POSTHOG_APP_TAG=$1
else
    echo "What version of PostHog would you like to install? (We default to 'latest')"
    echo "You can check out available versions here: https://hub.docker.com/r/posthog/posthog/tags"
    read -r POSTHOG_APP_TAG_READ
    if [ -z "$POSTHOG_APP_TAG_READ" ]
    then
        echo "Using default and installing $POSTHOG_APP_TAG"
    else
        export POSTHOG_APP_TAG=$POSTHOG_APP_TAG_READ
        echo "Using provided tag: $POSTHOG_APP_TAG"
    fi
fi

echo ""
if [ -n "$2" ]
then
    export DOMAIN=$2
else
    echo "Let's get the exact domain PostHog will be installed on"
    echo "Make sure that you have a Host A DNS record pointing to this instance!"
    echo "This will be used for TLS 🔐"
    echo "ie: test.posthog.net (NOT an IP address)"
    read -r DOMAIN
    export DOMAIN=$DOMAIN
fi

echo "Ok we'll set up certs for https://$DOMAIN"
echo ""
echo "We will need sudo access so the next question is for you to give us superuser access"
echo "Please enter your sudo password now:"
sudo echo ""

echo "Thanks! 🙏"
echo ""
echo "Ok! We'll take it from here 🚀"

echo "Making sure any stack that might exist is stopped"
sudo -E docker-compose -f docker-compose.yml stop &> /dev/null || true

# send log of this install for continued support!
curl -o /dev/null -L --header "Content-Type: application/json" -d "{
    \"api_key\": \"sTMFPsFhdP1Ssg\",
    \"distinct_id\": \"${DOMAIN}\",
    \"properties\": {\"domain\": \"${DOMAIN}\"},
    \"type\": \"capture\",
    \"event\": \"magic_curl_install_start\"
}" https://us.i.posthog.com/batch/ &> /dev/null

# update apt cache
echo "Grabbing latest apt caches"
sudo apt update

# clone posthog
echo "Installing PostHog 🦔 from Github"
sudo apt install -y git

# try to clone - if folder is already there pull latest for that branch
git clone --filter=blob:none https://github.com/PostHog/posthog.git &> /dev/null || true
cd posthog

if [[ "$POSTHOG_APP_TAG" = "latest-release" ]]
then
    git fetch --tags
    latestReleaseTag=$(git describe --tags "$(git rev-list --tags --max-count=1)")
    echo "Checking out latest PostHog release: $latestReleaseTag"
    echo "Warning PostHog don't create tagged releases anymore. It's way better to use 'latest' than 'latest-release'"
    git checkout "$latestReleaseTag"
elif [[ "$POSTHOG_APP_TAG" = "latest" ]]
then
    echo "Fetching latest changes from origin"
    git fetch origin
    current_branch=$(git branch --show-current)
    if [ -n "$current_branch" ]; then
        echo "Updating branch '$current_branch' to latest from origin"
        git reset --hard "origin/$current_branch"
    else
        echo "On detached HEAD: $(git rev-parse --short HEAD)"
    fi
    echo "Now on commit: $(git rev-parse --short HEAD)"
elif [[ "$POSTHOG_APP_TAG" =~ ^[0-9a-f]{40}$ ]]
then
    echo "Checking out specific commit hash: $POSTHOG_APP_TAG"
    git checkout "$POSTHOG_APP_TAG"
else
    releaseTag="${POSTHOG_APP_TAG/release-/""}"
    git fetch --tags
    echo "Checking out PostHog release: $releaseTag"
    git checkout "$releaseTag"
fi

cd ..

if [ -n "$3" ]
then
    export TLS_BLOCK="acme_ca https://acme-staging-v02.api.letsencrypt.org/directory"
fi

if [ "$REGISTRY_URL" == "" ]
then
    export REGISTRY_URL="posthog/posthog"
fi

# Write .env only on a fresh install. On a re-run (e.g. after a partial
# failure) preserve the existing file, so POSTHOG_SECRET and
# ENCRYPTION_SALT_KEYS are not regenerated, as it would invalidate active
# sessions and corrupt encrypted data on a live instance.
# Mirrors the FileExists(".env") guard in bin/hobby-installer.
# Tag changes on an existing instance are handled by bin/upgrade-hobby.
if [ -f .env ]; then
    echo "Existing .env found, preserving configuration"
else
    # Optional AI provider keys. PostHog's AI features (the Max assistant, SQL/regex assist)
    # run on your own LLM provider key. These are safe to leave blank now — you can add them
    # to .env later and apply with bin/upgrade-hobby. An env var set before running this
    # script (e.g. ANTHROPIC_API_KEY=... ./deploy-hobby) is used as-is without prompting.
    # Only prompt on an interactive terminal — a piped/non-interactive install
    # (CI, `curl | bash`) skips the prompts and relies on the env vars instead.
    # Read silently (-s) — these are secrets and shouldn't echo to the terminal/scrollback.
    if [ -t 0 ] && [ -z "$ANTHROPIC_API_KEY" ]; then
        echo ""
        echo "PostHog AI runs on an Anthropic API key."
        echo "Enter one to enable PostHog AI now, or leave blank to skip (you can add it later):"
        read -rs ANTHROPIC_API_KEY
        echo
    fi
    if [ -t 0 ] && [ -z "$OPENAI_API_KEY" ]; then
        echo "Optionally enter an OpenAI API key to enable SQL and regex AI assist, or leave blank to skip:"
        read -rs OPENAI_API_KEY
        echo
    fi

    POSTHOG_SECRET=$(head -c 28 /dev/urandom | sha224sum -b | head -c 56)
    ENCRYPTION_SALT_KEYS=$(openssl rand -hex 16)
    # Dedicated browserless token so a compromise of the rendering container can't yield
    # Django's SECRET_KEY (the compose file falls back to POSTHOG_SECRET when unset, for
    # installs whose .env predates this variable).
    BROWSERLESS_SECRET=$(openssl rand -hex 32)
    cat > .env <<EOF
POSTHOG_SECRET=$POSTHOG_SECRET
ENCRYPTION_SALT_KEYS=$ENCRYPTION_SALT_KEYS
BROWSERLESS_SECRET=$BROWSERLESS_SECRET
DOMAIN=$DOMAIN
TLS_BLOCK=$TLS_BLOCK
REGISTRY_URL=$REGISTRY_URL
CADDY_TLS_BLOCK=$TLS_BLOCK
CADDY_HOST="$DOMAIN, http://, https://"
POSTHOG_APP_TAG=$POSTHOG_APP_TAG
EOF
    # Only persist AI keys that were actually provided — skip writing a blank
    # ANTHROPIC_API_KEY= line that would just clutter .env with an empty assignment.
    # (A blank value is harmless either way: bin/upgrade-hobby greps for
    # ^ANTHROPIC_API_KEY=.+, so an empty value counts as missing and the user is
    # still offered the key on the next upgrade.)
    [ -n "$ANTHROPIC_API_KEY" ] && echo "ANTHROPIC_API_KEY=$ANTHROPIC_API_KEY" >> .env
    [ -n "$OPENAI_API_KEY" ] && echo "OPENAI_API_KEY=$OPENAI_API_KEY" >> .env
fi

# Download GeoLite2-City.mmdb if it doesn't exist
echo "Downloading GeoIP database file"
sudo apt-get update &&
sudo apt-get install -y --no-install-recommends curl ca-certificates brotli &&
sudo mkdir -p ./share &&
if [ ! -f ./share/GeoLite2-City.mmdb ]; then
    curl -L 'https://mmdbcdn.posthog.net/' --http1.1 | brotli --decompress | sudo tee ./share/GeoLite2-City.mmdb > /dev/null &&
    echo '{"date": "'"$(date +%Y-%m-%d)"'"}' | sudo tee ./share/GeoLite2-City.json > /dev/null
    sudo chmod 644 ./share/GeoLite2-City.mmdb &&
    sudo chmod 644 ./share/GeoLite2-City.json
fi

# write entrypoint
# NOTE: this is duplicated in bin/upgrade-hobby, so if you change it here,
# change it there too.
rm -rf compose
mkdir -p compose
cat > compose/start <<EOF
#!/bin/bash
./compose/wait
./bin/migrate
./bin/docker-server
EOF
chmod +x compose/start

cat > compose/temporal-django-worker <<EOF
#!/bin/bash
./bin/temporal-django-worker
EOF
chmod +x compose/temporal-django-worker

# write wait script
cat > compose/wait <<EOF
#!/usr/bin/env python3

import socket
import time

def loop():
    print("Waiting for ClickHouse and Postgres to be ready")
    try:
        with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
            s.connect(('clickhouse', 9000))
        print("Clickhouse is ready")
        with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
            s.connect(('db', 5432))
        print("Postgres is ready")
    except ConnectionRefusedError as e:
        time.sleep(5)
        loop()

loop()
EOF
chmod +x compose/wait

# setup docker
if ! command -v docker &> /dev/null; then
    echo "Docker is not installed. Setting up Docker."
    sudo apt install -y apt-transport-https ca-certificates curl software-properties-common
    curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo -E apt-key add -
    sudo add-apt-repository -y "deb [arch=amd64] https://download.docker.com/linux/ubuntu jammy stable"
    sudo apt update
    sudo apt-cache policy docker-ce
    sudo apt install -y docker-ce containerd.io=2.2.0-2~ubuntu.22.04~jammy
else
    echo "Docker is already installed. Skipping installation."
fi

# setup docker-compose
echo "Setting up Docker Compose"
sudo curl -L "https://github.com/docker/compose/releases/download/v2.33.1/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose || true
sudo chmod +x /usr/local/bin/docker-compose

# enable docker without sudo
sudo usermod -aG docker "${USER}" || true

# start up the stack
echo "Configuring Docker Compose...."
rm -f docker-compose.yml
cp posthog/docker-compose.base.yml docker-compose.base.yml
cp posthog/docker-compose.hobby.yml docker-compose.yml
cp posthog/.env.services .env.services
echo "Starting the stack!"
# Retry docker-compose up with --pull always up to 3 times
for attempt in 1 2 3; do
    echo "Starting stack (attempt $attempt/3)..."
    if sudo -E docker-compose -f docker-compose.yml up -d --no-build --pull always; then
        echo "Stack started successfully"
        break
    else
        if [ $attempt -lt 3 ]; then
            echo "Failed to start stack, waiting 30s before retry..."
            sleep 30
        else
            echo "Failed to start stack after 3 attempts"
            exit 1
        fi
    fi
done

if [ -z "$SKIP_HEALTH_CHECK" ]; then
    # Normal hobby deployment: wait for health check
    echo "We will need to wait ~5-10 minutes for things to settle down, migrations to finish, and TLS certs to be issued"
    echo ""
    echo "⏳ Waiting for PostHog web to boot (this will take a few minutes)"
    echo "   (timeout after 10 minutes if app doesn't start)"
    
    # Wait for health check with 600 second timeout
    # Track the start time to measure actual wait time
    HEALTH_START=$(date +%s)
    # shellcheck disable=SC2016 # Single quotes intentional - %{http_code} is curl format string, not shell variable
    if timeout 600 bash -c 'while [[ "$(curl -s -o /dev/null -w ''%{http_code}'' localhost/_health)" != "200" ]]; do sleep 5; done'; then
        HEALTH_END=$(date +%s)
        HEALTH_DURATION=$((HEALTH_END - HEALTH_START))
        echo "⌛️ PostHog looks up! (after ${HEALTH_DURATION} seconds)"
        echo ""
        echo "🎉🎉🎉  Done! 🎉🎉🎉"
        # send log of this install for continued support!
        curl -o /dev/null -L --header "Content-Type: application/json" -d "{
            \"api_key\": \"sTMFPsFhdP1Ssg\",
            \"distinct_id\": \"${DOMAIN}\",
            \"properties\": {\"domain\": \"${DOMAIN}\"},
            \"type\": \"capture\",
            \"event\": \"magic_curl_install_complete\"
        }" https://us.i.posthog.com/batch/ &> /dev/null
    else
        HEALTH_EXIT=$?
        echo ""
        if [ $HEALTH_EXIT -eq 124 ]; then
            echo "❌ Health check timed out after 10 minutes"
            echo "   The application did not start within the expected timeframe"
        else
            echo "❌ Health check failed with exit code: $HEALTH_EXIT"
        fi
        echo ""
        echo "Please check the logs with 'docker-compose logs' for more details."
        echo "Common issues:"
        echo "  - Docker image pull failed: 'docker logs <container>'"
        echo "  - Database migration stuck: 'docker-compose logs db clickhouse'"
        echo "  - Insufficient memory: 'free -h' and 'top'"
        exit 1
    fi
else
    # CI mode: skip health check, exit immediately after docker-compose up
    echo "⏭️  Skipping health check (SKIP_HEALTH_CHECK is set)"
    echo "Stack started, containers are running in background"
fi

echo ""
echo "To stop the stack run 'docker-compose stop'"
echo "To start the stack again run 'docker-compose start'"
echo "If you have any issues at all delete everything in this directory and run the curl command again"
echo ""
# shellcheck disable=SC2016 # we don't want to expand this expression
echo 'To upgrade: run /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/posthog/posthog/HEAD/bin/upgrade-hobby)"'
echo ""
echo "PostHog will be up at the location you provided!"
echo "https://${DOMAIN}"
echo ""
echo "It's dangerous to go alone! Take this: 🦔"
