#!/bin/bash
# Blue/green Podman deploy for an Orbit slot.
# Written by orbit init. Edit this file. Re-run init with --force to replace it.
#
# The process must listen on /sockets/app.sock and serve GET /health
# (or set HEALTH_PATH). Match APP_CMD and ops/Containerfile CMD.
set -euo pipefail

: "${SLOT_NAME:?}"
: "${SLOT_SRC:?}"
: "${SLOT_DATA:?}"
: "${SLOT_ENV:?}"
: "${SLOT_RUN:?}"
: "${SLOT_DEPLOY_ID:?}"
: "${SLOT_GIT_REVISION:?}"
: "${ORBIT_CADDY:?}"
: "${SLOT_TTL:=0}"

# --- adapt these -------------------------------------------------------------
APP_CMD=(@@APP_CMD@@)
HEALTH_PATH=${HEALTH_PATH:-/health}
# Hostnames for the Caddy site. Override with SLOT_CADDY_HOSTS in the slot env.
HOSTS=${SLOT_CADDY_HOSTS:-$SLOT_NAME}
# Set PREVIEW_BASIC_AUTH=0 to skip locking TTL preview slots.
PREVIEW_BASIC_AUTH=${PREVIEW_BASIC_AUTH:-1}
# -----------------------------------------------------------------------------

REV=${SLOT_GIT_REVISION:0:12}
APP_LABEL=${SLOT_NAME//./-}
SHA_DIR=${SLOT_RUN}/sockets/sha/${SLOT_DEPLOY_ID}
HOST_SOCK=${SHA_DIR}/app.sock
STABLE_SOCK=${SLOT_RUN}/app.sock
NAME=orbit.${APP_LABEL}.${SLOT_DEPLOY_ID}
IMAGE=${APP_LABEL}:${SLOT_DEPLOY_ID}
HEALTH_REASON=
BASIC_AUTH_USER=
BASIC_AUTH_HASH=

ensure_preview_auth() {
    # Optional: lock TTL previews with Caddy basic auth. Orbit only sets SLOT_TTL.
    BASIC_AUTH_USER=
    BASIC_AUTH_HASH=
    [[ $PREVIEW_BASIC_AUTH == 1 ]] || return 0
    [[ $SLOT_TTL =~ ^[1-9][0-9]*$ ]] || return 0
    local hash_file=$SLOT_RUN/basic_auth.hash
    if [[ -f $hash_file ]]; then
        BASIC_AUTH_USER=orbit
        BASIC_AUTH_HASH=$(<"$hash_file")
        BASIC_AUTH_HASH=${BASIC_AUTH_HASH//$'\n'/}
        return 0
    fi
    local password
    password=$(openssl rand -base64 24 | tr -d '\n/=+' | head -c 24)
    [[ -n $password ]] || {
        echo "[error] failed to generate preview password"
        exit 1
    }
    BASIC_AUTH_HASH=$(caddy hash-password --plaintext "$password") || {
        echo "[error] caddy hash-password failed"
        exit 1
    }
    install -m 0600 /dev/null "$hash_file"
    printf '%s\n' "$BASIC_AUTH_HASH" >"$hash_file"
    BASIC_AUTH_USER=orbit
    echo "[auth] preview basic auth user=orbit password=$password"
}

check_health() {
    local sock=$1 code
    if [[ ! -S $sock ]]; then
        HEALTH_REASON="socket missing"
        return 1
    fi
    code=$(curl -sS -o /dev/null -w '%{http_code}' --max-time 2 \
        --unix-socket "$sock" "http://localhost${HEALTH_PATH}" 2>&1) || code=curl_failed
    if [[ $code =~ ^2[0-9]{2}$ ]]; then
        return 0
    fi
    HEALTH_REASON=$code
    return 1
}

# Caddy is in group orbit. The container bind() + chmod 660 leaves the
# socket as the deploy user, so the health check passes and Caddy gets 502.
grant_caddy_socket() {
    local sock=$1
    if [[ -S $sock ]] && getent group orbit >/dev/null 2>&1; then
        chgrp orbit "$sock" || true
        chmod 660 "$sock" || true
    fi
}

publish_route() {
    {
        printf '%s {\n' "${HOSTS//,/ }"
        if [[ -n $BASIC_AUTH_HASH ]]; then
            printf '\tbasic_auth {\n'
            printf '\t\t%s %s\n' "$BASIC_AUTH_USER" "$BASIC_AUTH_HASH"
            printf '\t}\n'
        fi
        printf '\treverse_proxy unix/%s\n}\n' "$STABLE_SOCK"
    } >"${SLOT_RUN}/Caddyfile"
    caddy fmt --overwrite "${SLOT_RUN}/Caddyfile"
    caddy fmt --overwrite "$ORBIT_CADDY"
    caddy validate --config "$ORBIT_CADDY"
    caddy reload --config "$ORBIT_CADDY"
}

for command_name in podman curl caddy openssl; do
    command -v "$command_name" >/dev/null 2>&1 || {
        echo "[error] need $command_name in PATH"
        exit 1
    }
done
[[ -f $SLOT_ENV ]] || {
    echo "[error] missing slot environment: $SLOT_ENV"
    exit 1
}

cd "$SLOT_SRC"
mkdir -p "${SLOT_RUN}/sockets/sha" "$SLOT_DATA"
ensure_preview_auth

if [[ $(podman ps --filter "name=^${NAME}$" --format '{{.Names}}' | head -1) == "$NAME" ]]; then
    echo "[ok] already running $NAME"
    check_health "$HOST_SOCK" || {
        echo "[error] running but unhealthy ($HEALTH_REASON) — podman rm -f $NAME"
        exit 1
    }
    grant_caddy_socket "$HOST_SOCK"
    publish_route
    echo "[done] $NAME rev=$REV"
    exit 0
fi

echo "[build] $IMAGE"
podman build -t "$IMAGE" \
    -f "$SLOT_SRC/ops/Containerfile" \
    --ignorefile "$SLOT_SRC/ops/.containerignore" \
    "$SLOT_SRC"

mkdir -p "$SHA_DIR"
OLD=$(podman ps --filter "label=orbit.slot=${SLOT_NAME}" --format '{{.Names}}' | head -1)

echo "[start] $NAME"
podman run \
    -d --name "$NAME" \
    -v "${SHA_DIR}:/sockets:z" \
    -v "${SLOT_DATA}:/data:z" \
    --env-file "$SLOT_ENV" \
@@STARIO_ENV@@    --label "orbit.slot=${SLOT_NAME}" \
    --label "orbit.deploy_id=${SLOT_DEPLOY_ID}" \
    --label "orbit.revision=${REV}" \
    --restart on-failure:3 \
    --memory 512m \
    --cpus 1 \
    "$IMAGE" \
    "${APP_CMD[@]}"

ready=0
for _ in $(seq 1 30); do
    if check_health "$HOST_SOCK"; then
        ready=1
        break
    fi
    sleep 0.5
done
if ((ready == 0)); then
    echo "[fail] health timeout ($HEALTH_REASON); logs:"
    podman logs "$NAME" 2>&1 | tail -30
    podman rm -f "$NAME" || true
    rm -rf "${SHA_DIR:?}" || true
    exit 1
fi

grant_caddy_socket "$HOST_SOCK"
ln -sfn "sockets/sha/${SLOT_DEPLOY_ID}/app.sock" "$STABLE_SOCK"
publish_route

if [[ -n $OLD && $OLD != "$NAME" ]]; then
    echo "[stop] $OLD"
    podman stop -t 30 "$OLD" || podman rm -f "$OLD" || true
fi

echo "[done] $NAME rev=$REV"
