#!/usr/bin/env bash
# scripts/phase-06-net-setup.sh
#
# Phase-06 Steps 6.0 + 6.1: the capi-mgmt project's "safe/idempotent setup" pair --
#   6.0  keypair (import jumphost pubkey) + security group capi-mgmt-sg (ingress tcp/22 + tcp/6443)
#   6.1  network capi-mgmt-net + subnet capi-mgmt-subnet (10.20.0.0/24, DNS 1.1.1.1/1.0.0.1, D-019)
#        + router capi-mgmt-router with external gateway on provider-ext
# All verify-or-create (idempotent; safe to re-run). D-056 script; human-gated by invocation.
# Step 6.2 (VM + FIP allocation) is a SEPARATE flagged-mutation script.
#
# Tunables via env: PROJ KEYPAIR PUBKEY SG NET SUBNET ROUTER EXT MGMT_CIDR DNS1 DNS2
#                   ROUTER_POLL_TRIES ROUTER_POLL_SLEEP
# Requires: jumphost; admin-openrc (sourced or ~/admin-openrc); openstack; jq; the keypair pubkey.
# Usage:  source ~/admin-openrc && bash scripts/phase-06-net-setup.sh
# Exit:   0 all present/created + router ACTIVE w/ ext-gw | 1 gate fail | 2 precondition
# ASCII + LF.

set -euo pipefail
shopt -s inherit_errexit 2>/dev/null || true

PROJ="${PROJ:-capi-mgmt}"
KEYPAIR="${KEYPAIR:-capi-mgmt-key}"
PUBKEY="${PUBKEY:-$HOME/.ssh/id_ed25519.pub}"
SG="${SG:-capi-mgmt-sg}"
NET="${NET:-capi-mgmt-net}"
SUBNET="${SUBNET:-capi-mgmt-subnet}"
ROUTER="${ROUTER:-capi-mgmt-router}"
EXT="${EXT:-provider-ext}"
MGMT_CIDR="${MGMT_CIDR:-10.20.0.0/24}"
DNS1="${DNS1:-1.1.1.1}"; DNS2="${DNS2:-1.0.0.1}"
ROUTER_POLL_TRIES="${ROUTER_POLL_TRIES:-6}"; ROUTER_POLL_SLEEP="${ROUTER_POLL_SLEEP:-5}"

# --- preconditions ------------------------------------------------------------------
for c in openstack jq; do command -v "$c" >/dev/null 2>&1 || { echo "FAIL: $c not found" >&2; exit 2; }; done
if [ -z "${OS_AUTH_URL:-}" ] && [ -f "$HOME/admin-openrc" ]; then
  # shellcheck disable=SC1091
  . "$HOME/admin-openrc"
fi
[ -n "${OS_AUTH_URL:-}" ] || { echo "FAIL: OS_AUTH_URL unset and no ~/admin-openrc" >&2; exit 2; }
openstack token issue >/dev/null 2>&1 || { echo "FAIL: no scoped token (admin-openrc)" >&2; exit 2; }
openstack network show "$EXT" -f value -c id >/dev/null 2>&1 || { echo "FAIL: external network $EXT absent (phase-04 prereq)" >&2; exit 2; }

# --- 6.0 keypair --------------------------------------------------------------------
if openstack keypair show "$KEYPAIR" >/dev/null 2>&1; then
  echo "[SKIP] keypair $KEYPAIR exists"
else
  [ -f "$PUBKEY" ] || { echo "FAIL: pubkey $PUBKEY not found" >&2; exit 2; }
  openstack keypair create --public-key "$PUBKEY" "$KEYPAIR" >/dev/null
  echo "[OK] keypair $KEYPAIR (from $PUBKEY)"
fi

# --- 6.0 security group + rules -----------------------------------------------------
if openstack security group show "$SG" >/dev/null 2>&1; then
  echo "[SKIP] security group $SG exists"
else
  openstack security group create --project "$PROJ" "$SG" >/dev/null
  echo "[OK] security group $SG"
fi
SGID=$(openstack security group show "$SG" -f value -c id)
[ -n "$SGID" ] || { echo "FAIL: could not resolve $SG id" >&2; exit 1; }
for port in 22 6443; do
  PR=$(openstack security group rule list "$SGID" -f value -c "Port Range" 2>/dev/null || true)
  if grep -qx "${port}:${port}" <<<"$PR"; then   # capture-then-test: the raced form mis-read 'present' as 'absent' -> duplicate create -> 409 aborts under set -e
    echo "[SKIP] sg rule tcp/$port"
  else
    openstack security group rule create --proto tcp --dst-port "$port" "$SGID" >/dev/null
    echo "[OK] sg rule tcp/$port (ingress)"
  fi
done

# --- 6.1 network / subnet / router --------------------------------------------------
if openstack network show "$NET" >/dev/null 2>&1; then
  echo "[SKIP] network $NET exists"
else
  openstack network create --project "$PROJ" "$NET" >/dev/null
  echo "[OK] network $NET"
fi
if openstack subnet show "$SUBNET" >/dev/null 2>&1; then
  echo "[SKIP] subnet $SUBNET exists"
else
  openstack subnet create --project "$PROJ" --network "$NET" --subnet-range "$MGMT_CIDR" \
    --dns-nameserver "$DNS1" --dns-nameserver "$DNS2" "$SUBNET" >/dev/null
  echo "[OK] subnet $SUBNET ($MGMT_CIDR; dns $DNS1,$DNS2)"
fi
if openstack router show "$ROUTER" >/dev/null 2>&1; then
  echo "[SKIP] router $ROUTER exists"
else
  openstack router create --project "$PROJ" "$ROUTER" >/dev/null
  echo "[OK] router $ROUTER"
fi
# external gateway (set is idempotent) + attach subnet (ignore 'already attached')
openstack router set --external-gateway "$EXT" "$ROUTER"
openstack router add subnet "$ROUTER" "$SUBNET" 2>/dev/null || true

# --- verify / GATE: router ACTIVE with external gateway -----------------------------
echo "=== verify ==="
openstack security group rule list "$SGID" -f value -c Protocol -c "Port Range" 2>/dev/null || true
OK=0
for i in $(seq 1 "$ROUTER_POLL_TRIES"); do
  ST=$(openstack router show "$ROUTER" -f value -c status 2>/dev/null || echo '?')
  GW=$(openstack router show "$ROUTER" -f json 2>/dev/null | jq -r '.external_gateway_info // empty')
  echo "[$i] router status=$ST gw=${GW:+set}"
  if [ "$ST" = ACTIVE ] && [ -n "$GW" ]; then OK=1; break; fi
  sleep "$ROUTER_POLL_SLEEP"
done
[ "$OK" = 1 ] || { echo "GATE FAIL: router $ROUTER not ACTIVE with external gateway"; exit 1; }
echo "Summary: phase-06 net-setup complete (keypair + sg + network/subnet/router)."
