#!/bin/sh
set -eu

mkdir -p /run/bird

SOCK_PATH="/run/bird/bird.ctl"
PROTO="${OBSERVE_PROTO:-rpki_tcp}"
INTERVAL="${OBSERVE_INTERVAL:-30}"
MODE="${OBSERVE_MODE:-event}"
DEBOUNCE_SECS="${OBSERVE_DEBOUNCE_SECS:-1}"
RPKI_HOST="${RPKI_HOST:-host.docker.internal}"
RPKI_PORT="${RPKI_PORT:-323}"

BIRD_CONFIG_PATH="${BIRD_CONFIG_PATH:-/config/bird.conf}"
BIRD_CONFIG_TEMPLATE_PATH="${BIRD_CONFIG_TEMPLATE_PATH:-/config/bird.conf.template}"

ASPA_TABLE="${OBSERVE_ASPA_TABLE:-rtr_aspa}"
ROA4_TABLE="${OBSERVE_ROA4_TABLE:-rtr_roa_v4}"
ROA6_TABLE="${OBSERVE_ROA6_TABLE:-rtr_roa_v6}"

ASPA_COUNT="${OBSERVE_ASPA_COUNT:-3}"
ROA4_COUNT="${OBSERVE_ROA4_COUNT:-3}"
ROA6_COUNT="${OBSERVE_ROA6_COUNT:-3}"

SHOW_ASPA="${SHOW_ASPA:-1}"
SHOW_ROA4="${SHOW_ROA4:-1}"
SHOW_ROA6="${SHOW_ROA6:-1}"
SSH_HOST_PUBKEY_PATH="${SSH_HOST_PUBKEY_PATH:-/config/ssh/ssh_host_rsa_key.pub}"
SSH_KNOWN_HOSTS_PATH="${SSH_KNOWN_HOSTS_PATH:-/run/bird/known_hosts}"

LOG_DIR="${LOG_DIR:-/app/logs}"
LOG_NAME="${LOG_NAME:-${HOSTNAME:-bird-rpki-client}}"
STDOUT_LOG="${LOG_DIR}/${LOG_NAME}.stdout.log"
STDERR_LOG="${LOG_DIR}/${LOG_NAME}.stderr.log"

mkdir -p "$LOG_DIR"
exec >>"$STDOUT_LOG" 2>>"$STDERR_LOG"

if [ -r "$BIRD_CONFIG_TEMPLATE_PATH" ]; then
  RENDERED_CONFIG_PATH="/run/bird/bird.generated.conf"
  envsubst '${RPKI_HOST} ${RPKI_PORT}' < "$BIRD_CONFIG_TEMPLATE_PATH" > "$RENDERED_CONFIG_PATH"
  BIRD_CONFIG_PATH="$RENDERED_CONFIG_PATH"
  echo "[entrypoint] rendered config  : $BIRD_CONFIG_TEMPLATE_PATH -> $BIRD_CONFIG_PATH"
fi

ensure_ssh_known_hosts() {
  if [ -s "$SSH_KNOWN_HOSTS_PATH" ]; then
    return
  fi

  if [ ! -r "$SSH_HOST_PUBKEY_PATH" ]; then
    echo "[entrypoint] WARNING: SSH host key file not found: $SSH_HOST_PUBKEY_PATH"
    return
  fi

  set -- $(awk 'NF >= 2 { print $1, $2; exit }' "$SSH_HOST_PUBKEY_PATH")
  if [ $# -ne 2 ]; then
    echo "[entrypoint] WARNING: invalid SSH host key format in $SSH_HOST_PUBKEY_PATH"
    return
  fi

  key_type="$1"
  key_data="$2"

  if echo "$key_type" | grep -q '^ssh-'; then
    {
      echo "$RPKI_HOST $key_type $key_data"
      echo "[$RPKI_HOST]:$RPKI_PORT $key_type $key_data"
    } > "$SSH_KNOWN_HOSTS_PATH"
  else
    cp "$SSH_HOST_PUBKEY_PATH" "$SSH_KNOWN_HOSTS_PATH"
  fi

  chmod 600 "$SSH_KNOWN_HOSTS_PATH" || true
  echo "[entrypoint] generated known_hosts: $SSH_KNOWN_HOSTS_PATH"
}

print_first_n_objects() {
  table_name="$1"
  max_objects="$2"

  birdc -s "$SOCK_PATH" show route table "$table_name" all 2>/dev/null | awk -v max="$max_objects" '
    BEGIN {
      count = 0
    }

    # 直接跳过空行
    /^[[:space:]]*$/ {
      next
    }

    # 保留 birdc 的表头
    /^BIRD / {
      print
      next
    }

    /^Table / {
      print
      next
    }

    # 非缩进且不是表头，视为一个新对象的开始
    /^[^[:space:]]/ {
      count++
      if (count > max) {
        exit
      }
      print
      next
    }

    # 缩进内容，只有在已进入前 max 个对象时才打印
    {
      if (count > 0 && count <= max) {
        print
      }
    }
  ' || true
}

print_snapshot() {
  echo "==== $(date +"%Y-%m-%dT%H:%M:%S%:z") RPKI snapshot ($PROTO) ===="
  birdc -s "$SOCK_PATH" show protocols all "$PROTO" || true

  if [ "$SHOW_ASPA" = "1" ]; then
    echo "---- ASPA table ($ASPA_TABLE, first ${ASPA_COUNT} objects) ----"
    print_first_n_objects "$ASPA_TABLE" "$ASPA_COUNT"
  fi

  if [ "$SHOW_ROA4" = "1" ]; then
    echo "---- ROA4 table ($ROA4_TABLE, first ${ROA4_COUNT} objects) ----"
    print_first_n_objects "$ROA4_TABLE" "$ROA4_COUNT"
  fi

  if [ "$SHOW_ROA6" = "1" ]; then
    echo "---- ROA6 table ($ROA6_TABLE, first ${ROA6_COUNT} objects) ----"
    print_first_n_objects "$ROA6_TABLE" "$ROA6_COUNT"
  fi
}

is_rpki_related_event() {
  line="$1"
  echo "$line" | grep -Eiq "$PROTO|$ASPA_TABLE|$ROA4_TABLE|$ROA6_TABLE|rpki|rtr"
}

echo "[entrypoint] starting bird"
echo "[entrypoint] config           : $BIRD_CONFIG_PATH"
echo "[entrypoint] observe proto    : $PROTO"
echo "[entrypoint] observe mode     : $MODE"
echo "[entrypoint] observe interval : $INTERVAL (used when mode=interval)"
echo "[entrypoint] debounce secs    : $DEBOUNCE_SECS (used when mode=event)"
echo "[entrypoint] target           : $RPKI_HOST:$RPKI_PORT"
echo "[entrypoint] show aspa        : $SHOW_ASPA ($ASPA_TABLE, first $ASPA_COUNT objects)"
echo "[entrypoint] show roa4        : $SHOW_ROA4 ($ROA4_TABLE, first $ROA4_COUNT objects)"
echo "[entrypoint] show roa6        : $SHOW_ROA6 ($ROA6_TABLE, first $ROA6_COUNT objects)"

if [ "$PROTO" = "rpki_ssh" ] || grep -q 'transport[[:space:]]\+ssh' "$BIRD_CONFIG_PATH"; then
  ensure_ssh_known_hosts
fi

if nc -zvw3 "$RPKI_HOST" "$RPKI_PORT"; then
  echo "[entrypoint] TCP connectivity to $RPKI_HOST:$RPKI_PORT OK"
else
  echo "[entrypoint] WARNING: cannot connect to $RPKI_HOST:$RPKI_PORT before BIRD starts"
fi

bird -f -c "$BIRD_CONFIG_PATH" -s "$SOCK_PATH" &
BIRD_PID="$!"

sleep 1

case "$INTERVAL" in
  ''|*[!0-9]*)
    INTERVAL=0
    ;;
esac

case "$MODE" in
  event)
    case "$DEBOUNCE_SECS" in
      ''|*[!0-9]*)
        DEBOUNCE_SECS=1
        ;;
    esac

    echo "[entrypoint] waiting for BIRD monitor events and only printing on RPKI-related changes"
    LAST_PRINT_TS=0

    if birdc -s "$SOCK_PATH" monitor all 2>/dev/null | while IFS= read -r line; do
      [ -n "$line" ] || continue
      if ! is_rpki_related_event "$line"; then
        continue
      fi
      now_ts="$(date +%s)"
      if [ $((now_ts - LAST_PRINT_TS)) -lt "$DEBOUNCE_SECS" ]; then
        continue
      fi
      LAST_PRINT_TS="$now_ts"
      echo "[monitor] $line"
      print_snapshot
    done; then
      :
    else
      echo "[entrypoint] WARNING: birdc monitor failed, fallback to interval polling"
      MODE="interval"
    fi
    ;;
esac

if [ "$MODE" = "interval" ] && [ "$INTERVAL" -gt 0 ]; then
  while kill -0 "$BIRD_PID" 2>/dev/null; do
    print_snapshot
    sleep "$INTERVAL"
  done
fi

wait "$BIRD_PID"
