#!/bin/sh
# One-click "Join Hive" — registers this machine as a Colibri agent.
# Runs in a visible terminal so the operator sees the result.
# Idempotent: safe to re-run on an already-registered agent.

SOCKET="${COLIBRI_SOCKET:-/var/run/colibri/colibri.sock}"
PROVIDER_ENV="/usr/local/etc/colibri/provider.env"

finish() {
    _code="${1:-0}"
    echo ""
    echo "Press Enter to close."
    read -r _
    exit "${_code}"
}

have() {
    command -v "$1" >/dev/null 2>&1
}

# Read a value into the variable named by $1 without echoing keystrokes.
# Used for the Vaultwarden secret + password so they never appear on screen.
read_secret() {
    printf '%s' "$2"
    stty -echo 2>/dev/null
    read -r "$1"
    stty echo 2>/dev/null
    echo ""
}

# Upsert BW_CLIENTID/BW_CLIENTSECRET/BW_PASSWORD into provider.env (root-owned,
# 0600) without leaking secrets through process arguments. The values stay in
# shell variables and a 0600 temp file; provider.env is read and written via mdo
# so the unprivileged operator session can update it.
write_provider_bw() {
    _cache="${HOME:-/home/clawdie}/.cache/clawdie"
    mkdir -p "$_cache" 2>/dev/null
    chmod 0700 "$_cache" 2>/dev/null
    _tmp="$(mktemp "${_cache}/joinhive.XXXXXX")" || return 1
    chmod 0600 "$_tmp" 2>/dev/null

    if have mdo; then
        mdo -u root cat "$PROVIDER_ENV" >"$_tmp" 2>/dev/null || :
    else
        cat "$PROVIDER_ENV" >"$_tmp" 2>/dev/null || :
    fi

    # Drop any existing BW_* definitions, then append the freshly entered ones.
    grep -vE '^(BW_CLIENTID|BW_CLIENTSECRET|BW_PASSWORD)=' "$_tmp" >"${_tmp}.new" 2>/dev/null || : >"${_tmp}.new"
    {
        printf 'BW_CLIENTID=%s\n' "$BW_CLIENTID"
        printf 'BW_CLIENTSECRET=%s\n' "$BW_CLIENTSECRET"
        printf 'BW_PASSWORD=%s\n' "$BW_PASSWORD"
    } >>"${_tmp}.new"
    mv "${_tmp}.new" "$_tmp"
    chmod 0600 "$_tmp" 2>/dev/null

    if have mdo; then
        mdo -u root cp "$_tmp" "$PROVIDER_ENV" && mdo -u root chmod 0600 "$PROVIDER_ENV"
        _rc=$?
    else
        cp "$_tmp" "$PROVIDER_ENV" && chmod 0600 "$PROVIDER_ENV"
        _rc=$?
    fi

    rm -f "$_tmp" "${_tmp}.new" 2>/dev/null
    return $_rc
}

provider_env_has_bw_creds() {
    _check='test -f "$1" && grep -Eq "^BW_CLIENTID=.+" "$1" && grep -Eq "^BW_CLIENTSECRET=.+" "$1" && grep -Eq "^BW_PASSWORD=.+" "$1"'

    if have mdo; then
        mdo -u root sh -c "${_check}" sh "${PROVIDER_ENV}" >/dev/null 2>&1
        return $?
    fi

    sh -c "${_check}" sh "${PROVIDER_ENV}" >/dev/null 2>&1
}

echo "========================================"
echo "  Clawdie — Join Hive"
echo "========================================"
echo ""

# 1. Check daemon
if [ ! -S "$SOCKET" ]; then
    echo "[1/4] Starting colibri daemon..."
    if have mdo; then
        if ! mdo -u root service colibri_daemon start; then
            echo "       WARNING: could not start colibri_daemon via mdo."
        fi
    elif [ "$(id -u)" -eq 0 ]; then
        if ! service colibri_daemon start; then
            echo "       WARNING: could not start colibri_daemon."
        fi
    else
        echo "       WARNING: mdo is unavailable and this user is not root."
    fi

    _tries=0
    while [ "$_tries" -lt 5 ] && [ ! -S "$SOCKET" ]; do
        sleep 1
        _tries=$(( _tries + 1 ))
    done
else
    echo "[1/4] Daemon already running."
fi

if [ ! -S "$SOCKET" ]; then
    echo "       ERROR: Colibri socket is still missing: ${SOCKET}"
    echo "       Check: mdo -u root service colibri_daemon status"
    finish 1
fi

# 2. Vault creds + provider keys. provider.env is intentionally 0600 (root), so
# read/write via mdo. If the 3 bootstrap values are absent, prompt for them and
# save them; then pull the provider keys (DeepSeek, etc.) from Vaultwarden.
echo "[2/4] Vault credentials..."
if ! provider_env_has_bw_creds; then
    echo ""
    echo "  No Vaultwarden bootstrap credentials found."
    echo "  Enter the 3 values, or press Enter at the first prompt to skip."
    echo ""
    printf "  BW_CLIENTID: "
    read -r BW_CLIENTID
    if [ -n "${BW_CLIENTID:-}" ]; then
        read_secret BW_CLIENTSECRET "  BW_CLIENTSECRET (hidden): "
        read_secret BW_PASSWORD     "  BW_PASSWORD (hidden): "
        if [ -n "${BW_CLIENTSECRET:-}" ] && [ -n "${BW_PASSWORD:-}" ]; then
            if write_provider_bw; then
                echo "       Saved to ${PROVIDER_ENV} (0600)."
            else
                echo "       ERROR: could not write ${PROVIDER_ENV}."
            fi
        else
            echo "       Skipped: secret or password was empty."
        fi
        unset BW_CLIENTSECRET BW_PASSWORD
    else
        echo "       Skipped credential entry."
    fi
    unset BW_CLIENTID
fi

if provider_env_has_bw_creds; then
    echo "       Pulling provider keys from Vaultwarden..."
    if have clawdie-vault-fetch && have bw && have mdo; then
        if mdo -u root clawdie-vault-fetch --bootstrap "$PROVIDER_ENV" --write-env "$PROVIDER_ENV"; then
            echo "       Provider keys updated (DeepSeek and any others present)."
            echo "       Restarting colibri daemon to load the new keys..."
            if mdo -u root service colibri_daemon restart >/dev/null 2>&1; then
                _t=0
                while [ "$_t" -lt 5 ] && [ ! -S "$SOCKET" ]; do
                    sleep 1
                    _t=$(( _t + 1 ))
                done
                # Confirm the auto-spawned agent came up (colibri auto-spawn on boot).
                if have colibri; then
                    _p=0
                    while [ "$_p" -lt 10 ]; do
                        if colibri --socket "$SOCKET" status 2>/dev/null | grep -q '"agents":[1-9]'; then
                            echo "       Agent is live."
                            break
                        fi
                        sleep 1
                        _p=$(( _p + 1 ))
                    done
                    [ "$_p" -lt 10 ] || echo "       NOTE: no agent yet — check: colibri status"
                fi
            else
                echo "       WARNING: daemon restart failed."
                echo "       Run: mdo -u root service colibri_daemon restart"
            fi
        else
            echo "       WARNING: vault fetch did not complete (check network / credentials)."
            echo "       The manual wizard remains available; keys can be added later."
        fi
    else
        echo "       NOTE: clawdie-vault-fetch, bw, or mdo unavailable — skipping key pull."
    fi
else
    echo "       WARNING: provider.env still lacks BW_CLIENTID/BW_CLIENTSECRET/BW_PASSWORD."
    echo "       Vault provisioning is skipped until they are added."
fi

# 2b. Tailscale auth key (separate from vault-fetch — a single standalone item).
if have bw && provider_env_has_bw_creds && ! tailscale status >/dev/null 2>&1; then
    echo "       Fetching Tailscale auth key from vault..."
    _tskey="$(mdo -u root sh -c '
set -eu
. /usr/local/etc/colibri/provider.env
# Reuse existing session if possible; otherwise unlock fresh.
SESSION="$(bw unlock --passwordenv BW_PASSWORD --raw 2>/dev/null || true)"
if [ -z "$SESSION" ]; then
    bw login --apikey >/dev/null 2>&1
    SESSION="$(bw unlock --passwordenv BW_PASSWORD --raw 2>/dev/null)"
fi
# Handle both naming conventions: hyphenated in vault, underscored env var.
ITEM="$(bw list items --search tailscale-auth-key --session "$SESSION" 2>/dev/null | \
    python3 -c "import sys,json; items=json.load(sys.stdin); ts=[i for i in items if i.get(\"name\",\"\") in (\"tailscale-auth-key\",\"tailscale_auth_key\")]; print(ts[0][\"notes\"] if ts else \"\")" 2>/dev/null || true)"
bw lock >/dev/null 2>&1 || true
printf "%s" "$ITEM"
' 2>/dev/null)"
    if [ -n "${_tskey:-}" ]; then
        echo "$_tskey" | grep -q '^tskey-auth-' && {
            # Pass the key via stdin, not argv, so it never appears in ps.
            printf '%s' "$_tskey" | mdo -u root sh -c '
                set -eu
                f="/usr/local/etc/colibri/provider.env"
                read -r k
                printf "TAILSCALE_AUTH_KEY=%s\n" "$k" >> "$f"
                chmod 0600 "$f"
            '
            echo "       TAILSCALE_AUTH_KEY written to provider.env."
            # onestart: the service defaults to enable=NO on the OOTB image, and
            # onestart bypasses rcvar. With required_files removed it reads the
            # key from provider.env and strips it after a successful join.
            if mdo -u root service clawdie_tailscale_up onestart >/dev/null 2>&1; then
                echo "       Tailscale joined ($(tailscale status 2>/dev/null | head -1 || echo 'up'))."
            else
                echo "       WARNING: tailscale up failed — check the key in Vaultwarden."
            fi
        } || echo "       WARNING: Vaultwarden item found but does not look like an auth key."
    else
        echo "       No tailscale-auth-key item in Vaultwarden (create one to auto-join)."
    fi
fi

# 3. Detect capabilities
HOST=$(hostname 2>/dev/null || echo "clawdie-live")
OS=$(uname -s 2>/dev/null | tr '[:upper:]' '[:lower:]')
[ -n "$OS" ] || OS="unknown"
CAPS="$OS,shell"

# Add optional capabilities
have colibri && CAPS="$CAPS,colibri"
have hermes && CAPS="$CAPS,hermes"
have pi && CAPS="$CAPS,pi"
if have tailscale && tailscale status >/dev/null 2>&1; then
    CAPS="$CAPS,tailscale"
fi
[ "$OS" = "freebsd" ] && CAPS="$CAPS,rc.d,jail,zfs"

# image-render when Pillow imports (py311-pillow on python3=3.11); screenshot
# also needs a live display (the XFCE session).
if python3 -c 'import PIL' >/dev/null 2>&1; then
    CAPS="$CAPS,image-render"
    [ -n "${DISPLAY:-}" ] && CAPS="$CAPS,screenshot"
fi

if have python3; then
    CAPS_JSON=$(printf '%s' "$CAPS" | python3 -c 'import json,sys; print(json.dumps(sys.stdin.read().strip().split(",")))' 2>/dev/null)
else
    CAPS_JSON='["shell"]'
fi
[ -n "$CAPS_JSON" ] || CAPS_JSON='["shell"]'

AGENT_NAME="${HOST}"
echo "[3/4] Registering agent: ${AGENT_NAME}"
echo "       capabilities: ${CAPS}"

if ! have nc; then
    echo "       ERROR: nc is not installed; cannot talk to ${SOCKET}."
    finish 1
fi

RESP=$(printf '{"cmd":"register-agent","name":"%s","capabilities":%s}\n' \
    "$AGENT_NAME" "$CAPS_JSON" \
    | nc -U "$SOCKET" -w 3 2>/dev/null)

if echo "$RESP" | grep -q '"ok":true'; then
    echo "       registered."
elif echo "$RESP" | grep -Eiq 'already exists|unique constraint|constraint failed|agents\.name'; then
    echo "       already registered (idempotent)."
else
    echo "       registration did not complete cleanly."
    if [ -n "$RESP" ]; then
        echo "       response: ${RESP}"
    else
        echo "       no response from ${SOCKET}"
    fi
    finish 1
fi

# 4. Apply identity wallpaper as visual confirmation
echo "[4/4] Agent ${AGENT_NAME} is live on the Colibri board."
echo ""

if have clawdie-wallpaper-gen && have xfconf-query; then
    echo "       Setting identity wallpaper..."
    # Let the generator pick a policy-compliant path (project-local tmp/ or an
    # app-owned cache dir) and report it on stdout — no host-global /tmp here.
    WP=$(clawdie-wallpaper-gen 2>/dev/null)
    if [ -n "$WP" ] && [ -f "$WP" ]; then
        # XFCE keys backdrops by connector name (monitorHDMI-1, monitoreDP-1, ...),
        # not a fixed "monitor0". Set every existing last-image property so the
        # change actually applies on real hardware.
        _applied=0
        for _prop in $(xfconf-query -c xfce4-desktop -l 2>/dev/null | grep '/last-image$'); do
            xfconf-query -c xfce4-desktop -p "$_prop" -s "$WP" 2>/dev/null && _applied=1
        done
        # First boot / headless: no backdrop props exist yet — create the default.
        if [ "$_applied" -eq 0 ]; then
            xfconf-query -c xfce4-desktop \
                -p /backdrop/screen0/monitor0/workspace0/last-image \
                -n -t string -s "$WP" 2>/dev/null
        fi
        xfdesktop --reload >/dev/null 2>&1 || true
    fi
fi

echo "  Check:  colibri status"
echo "  Tasks:  colibri list-tasks --status started"
echo "  Board:  colibri list-agents"
echo ""
echo "Hive joined."
finish 0
