#!/bin/bash
# peer-reinject.sh — generic .after-start hook: re-seed peers from peer-capture state.
# Uses admin_addStaticPeer for geth, admin_addPeer otherwise (connect-peers.sh).
# In-network vantage (rpc_chains). Idempotent; best-effort (never fails the restart).
#
# Seed order (one path for us and customers):
#   1. Own persisted peerset (peer-state/<compose>.json from peer-capture)
#   2. Public bootstrap list https://stakesquid.eu/bootstrap/<network>.json
#      (third-party enodes only — same JSON customers curl)
#
# Usage: peer-reinject.sh <compose-path>
# Env:   PEER_BOOTSTRAP=0 to disable bootstrap fallback
#        BOOTSTRAP_BASE_URL to override (default https://stakesquid.eu/bootstrap)
set -euo pipefail

COMPOSE_PATH="${1:-}"
if [ -z "$COMPOSE_PATH" ]; then
    echo "Usage: $0 <compose-path>" >&2
    exit 1
fi

BASEPATH="$(cd "$(dirname "$0")" && pwd)"
STATE_DIR="$BASEPATH/peer-state"
SAFE="$(echo "$COMPOSE_PATH" | sed 's|[^a-zA-Z0-9._-]|_|g')"
STATE="$STATE_DIR/${SAFE}.json"
CURL_IMAGE="${PEER_CURL_IMAGE:-curlimages/curl:8.5.0}"
LOG="${PEER_HOOK_LOG:-/var/log/peer-hooks.log}"
USE_BOOTSTRAP="${PEER_BOOTSTRAP:-1}"

log() { echo "$(date '+%F %T') peer-reinject[$COMPOSE_PATH]: $*" | tee -a "$LOG" >&2; }

case "$COMPOSE_PATH" in
    *nitro*|*zksync*|*zk-sync*)
        log "skip (nitro/zksync)"
        exit 0
        ;;
esac

CLIENT_TYPE="unknown"
ENODES=()
SOURCE="none"

if [ -f "$STATE" ]; then
    CLIENT_TYPE="$(jq -r '.client_type // "unknown"' "$STATE" 2>/dev/null || echo unknown)"
    mapfile -t ENODES < <(jq -r '.peers[].id // empty' "$STATE" 2>/dev/null | grep -E '^enode://' | head -n 50 || true)
    if [ "${#ENODES[@]}" -gt 0 ]; then
        SOURCE="peerset"
    fi
fi

# Secondary seed: public bootstrap API (same files customers use).
if [ "${#ENODES[@]}" -eq 0 ] && [ "$USE_BOOTSTRAP" != "0" ]; then
    if [ -x "$BASEPATH/bootstrap-peers.sh" ]; then
        mapfile -t ENODES < <("$BASEPATH/bootstrap-peers.sh" --from-compose "$COMPOSE_PATH" 2>/dev/null | grep -E '^enode://' | head -n 25 || true)
        if [ "${#ENODES[@]}" -gt 0 ]; then
            SOURCE="bootstrap"
            log "no local peerset — using bootstrap fallback (${#ENODES[@]} enodes)"
        fi
    fi
fi

if [ "${#ENODES[@]}" -eq 0 ]; then
    if [ ! -f "$STATE" ]; then
        log "no capture state at $STATE and bootstrap empty — nothing to reinject"
    else
        log "no enodes in $STATE and bootstrap empty"
    fi
    exit 0
fi

BASE="$(basename "$COMPOSE_PATH" | sed 's/--.*//')"
EL=""
# wait briefly for container after recreate
for _ in 1 2 3 4 5 6 7 8 9 10; do
    while read -r n; do
        [ -z "$n" ] && continue
        case "$n" in rpc-*) ;; *) continue ;; esac
        case "$n" in *-node-*|*-relay-*) continue ;; esac
        core="$(echo "$n" | sed -E 's/^rpc-(.+)-[0-9]+$/\1/')"
        if [ "$BASE" = "$core" ] || [[ "$BASE" == "$core"-* ]]; then
            EL="$n"
            break
        fi
    done < <(docker ps --format '{{.Names}}')
    [ -n "$EL" ] && break
    sleep 3
done

if [ -z "$EL" ]; then
    log "no execution container yet — giving up"
    exit 0
fi

IP="$(docker inspect -f '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' "$EL" 2>/dev/null || true)"
# Prefer rpc_chains IP when present
RPC_IP="$(docker inspect -f '{{(index .NetworkSettings.Networks "rpc_chains").IPAddress}}' "$EL" 2>/dev/null || true)"
if [ -n "$RPC_IP" ] && [ "$RPC_IP" != "<no value>" ]; then
    IP="$RPC_IP"
fi
if [ -z "$IP" ]; then
    log "no IP for $EL"
    exit 0
fi

# wait for RPC
for _ in 1 2 3 4 5 6 7 8 9 10 11 12; do
    docker rm -f peer-hook-probe >/dev/null 2>&1 || true
    docker image inspect "$CURL_IMAGE" >/dev/null 2>&1 || docker pull -q "$CURL_IMAGE" >/dev/null
    docker run -d --name peer-hook-probe --network rpc_chains "$CURL_IMAGE" sleep 180 >/dev/null
    if docker exec peer-hook-probe curl -s --max-time 5 -X POST \
        -H 'Content-Type: application/json' \
        --data '{"jsonrpc":"2.0","method":"net_version","params":[],"id":1}' \
        "http://${IP}:8545" 2>/dev/null | grep -q result; then
        break
    fi
    docker rm -f peer-hook-probe >/dev/null 2>&1 || true
    sleep 5
done

add_peer() {
    local enode="$1"
    local method
    if [ "$CLIENT_TYPE" = "geth" ]; then
        method="admin_addStaticPeer"
    else
        method="admin_addPeer"
    fi
    local payload
    payload="$(jq -nc --arg m "$method" --arg e "$enode" \
        '{jsonrpc:"2.0",method:$m,params:[$e],id:1}')"
    local resp
    resp="$(docker exec peer-hook-probe curl -s --max-time 8 -X POST \
        -H 'Content-Type: application/json' --data "$payload" \
        "http://${IP}:8545" 2>/dev/null || true)"
    if echo "$resp" | grep -qi 'method not found'; then
        if [ "$method" = "admin_addStaticPeer" ]; then
            method="admin_addPeer"
        else
            method="admin_addStaticPeer"
        fi
        payload="$(jq -nc --arg m "$method" --arg e "$enode" \
            '{jsonrpc:"2.0",method:$m,params:[$e],id:1}')"
        resp="$(docker exec peer-hook-probe curl -s --max-time 8 -X POST \
            -H 'Content-Type: application/json' --data "$payload" \
            "http://${IP}:8545" 2>/dev/null || true)"
    fi
    if echo "$resp" | grep -q '"result":true'; then
        return 0
    fi
    return 1
}

ADDED=0
FAIL=0
for enode in "${ENODES[@]}"; do
    if add_peer "$enode"; then
        ADDED=$((ADDED + 1))
    else
        FAIL=$((FAIL + 1))
    fi
done

# post count
COUNT="$(docker exec peer-hook-probe curl -s --max-time 8 -X POST \
    -H 'Content-Type: application/json' \
    --data '{"jsonrpc":"2.0","method":"admin_peers","params":[],"id":1}' \
    "http://${IP}:8545" 2>/dev/null | jq -r '.result | length' 2>/dev/null || echo '?')"

docker rm -f peer-hook-probe >/dev/null 2>&1 || true
log "reinjected source=$SOURCE added=$ADDED fail=$FAIL now_peers=$COUNT (from ${#ENODES[@]} enodes)"
exit 0
