#!/usr/bin/env bash
# scripts/provider-vip-standup.sh [--apply]
#
# D-057: stand up the provider-vip MAAS plane so the carve + bundle can resolve it.
# Creates, idempotently and in this order (per MAAS semantics: a space attaches to a
# VLAN, and a subnet inherits the space via its VLAN):
#   1. space  provider-vip
#   2. VLAN   vid=104 on the PROVIDER fabric (the fabric that owns 10.12.4.0/22),
#             mtu mirrored from the metal-internal VLAN
#   3. assign that VLAN -> space provider-vip
#   4. subnet 10.12.8.0/22 on that VLAN; gateway_ip + managed + dns set after
#   5. reserved VIP band 10.12.8.2-.100 (VIPs .50-.60 live in it)
#
# Default is DRY-RUN: resolves every id live by NAME/CIDR (PATTERN-1, no hardcoded
# MAAS ids) and prints each mutation it WOULD run, changing nothing. Pass --apply to
# execute. Idempotent: anything already present is SKIPped; re-runnable.
#
# NOTE: VIDs are PER-FABRIC in MAAS, so VID 104 existing on some OTHER fabric (e.g.
# nothing here, but in general) is irrelevant -- only the provider fabric is checked.
#
# MAAS-only by design (portable to Roosevelt -- no virbr1/host assumptions). The
# jumphost L3 gateway (virbr1.104 = 10.12.8.1) and the virbr1 vlan_filtering gate
# are SEPARATE host steps, not part of this script.
#
# Exit: 0 ok | 1 fatal
#
# CLI forms verified against Canonical MAAS docs (how-to-manage-networks):
#   vlans create $FABRIC name= vid= mtu= ; vlan update $FABRIC $VID space= ;
#   subnets create cidr= vlan= ; subnet update $CIDR key=value ;
#   spaces create name= ; ipranges create type=reserved subnet= start_ip= end_ip=

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

SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
# shellcheck source=scripts/lib-net.sh
. "$SCRIPT_DIR/lib-net.sh"

MAAS_PROFILE="${MAAS_PROFILE:-admin}"
FATAL=0
fail() { echo "FAIL: $*" >&2; FATAL=$((FATAL+1)); }
note() { echo "NOTE: $*"; }
hdr()  { echo; echo "=== $* ==="; }

MODE="dryrun"; [ "${1:-}" = "--apply" ] && MODE="apply"
need_jq || exit 1

# ---- plane definition (constants; lib-net carries the shared CIDR/VID) ------
PROVIDER_CIDR="10.12.4.0/22"        # fabric anchor: VID 104 lives on THIS fabric
PVIP_CIDR="$PROVIDER_VIP_CIDR"      # 10.12.8.0/22
PVIP_VID="$PROVIDER_VIP_VID"        # 104
PVIP_SPACE="provider-vip"
PVIP_GATEWAY="10.12.8.1"           # D-057 routed plane; set "" to omit (default-route watch-item)
PVIP_RANGE_LO="10.12.8.2"
PVIP_RANGE_HI="10.12.8.100"

# ---- live resolvers (read-only; re-queried each call -- the plane mutates) --
maas_q() { maas "$MAAS_PROFILE" "$@"; }
prov_fabric()    { maas_q subnets read | jq -r --arg c "$PROVIDER_CIDR" '.[]|select(.cidr==$c)|.vlan.fabric_id' | head -1; }
subid_of()       { maas_q subnets read | jq -r --arg c "$1" '.[]|select(.cidr==$c)|.id' | head -1; }
subvid_of()      { maas_q subnets read | jq -r --arg c "$1" '.[]|select(.cidr==$c)|.vlan.vid' | head -1; }
sub_field()      { maas_q subnets read | jq -r --arg c "$1" --arg f "$2" '.[]|select(.cidr==$c)|(.[$f] // "")' | head -1; }
space_id()       { maas_q spaces  read | jq -r --arg n "$PVIP_SPACE" '.[]|select(.name==$n)|.id' | head -1; }
metal_mtu()      { maas_q subnets read | jq -r --arg c "$METAL_INTERNAL_CIDR" '.[]|select(.cidr==$c)|.vlan.mtu' | head -1; }
metal_dns()      { maas_q subnets read | jq -r --arg c "$METAL_INTERNAL_CIDR" '.[]|select(.cidr==$c)|(.dns_servers // []|join(","))' | head -1; }
# parent_mtu: VID 104 is a child of enp1s0 (the PROVIDER untagged plane), so its MTU must
# track the provider fabric -- NOT metal-internal (a child of a different fabric). A VLAN
# MTU exceeding its parent's would break the interface.
parent_mtu()     { maas_q subnets read | jq -r --arg c "$PROVIDER_CIDR" '.[]|select(.cidr==$c)|.vlan.mtu' | head -1; }
# vlan obj id of a vid on a fabric (empty if absent)
vlanobj_on_fab() { maas_q vlans read "$1" | jq -r --arg v "$2" '.[]|select((.vid|tostring)==$v)|.id' | head -1; }
# current space NAME of a vid on a fabric
vlanspace_on_fab(){ maas_q vlans read "$1" | jq -r --arg v "$2" '.[]|select((.vid|tostring)==$v)|(.space // "")' | head -1; }

# ---- mutation emitter (runs in apply; prints WOULD in dryrun) ---------------
emit() {
  local desc="$1"; shift
  if [ "$MODE" = "apply" ]; then
    echo "  DO: $desc"
    local out
    if ! out="$(maas "$MAAS_PROFILE" "$@" 2>&1)"; then
      fail "$desc"
      echo "       MAAS said: $(printf '%s' "$out" | grep -viE '^(Success|Machine-readable)' | head -3 | tr '\n' ' ')" >&2
      return 1
    fi
  else
    echo "  WOULD: $desc"
    echo "         maas $MAAS_PROFILE $*"
  fi
}

hdr "provider-vip stand-up  mode=$MODE  ($PVIP_SPACE / $PVIP_CIDR / VID $PVIP_VID)"

# ---- 0) resolve + guard the provider fabric --------------------------------
PROV_FAB="$(prov_fabric)"
[ -n "$PROV_FAB" ] || { fail "provider fabric not found (no MAAS subnet $PROVIDER_CIDR)"; exit 1; }
note "provider fabric_id = $PROV_FAB (VID $PVIP_VID will be created here)"

# guard: if the target subnet already exists, it MUST be on VID 104 (else misconfigured)
if [ -n "$(subid_of "$PVIP_CIDR")" ]; then
  GOTVID="$(subvid_of "$PVIP_CIDR")"
  [ "$GOTVID" = "$PVIP_VID" ] || { fail "subnet $PVIP_CIDR exists on VID '$GOTVID', expected $PVIP_VID -- refusing to proceed"; exit 1; }
fi

# ---- 1) space --------------------------------------------------------------
hdr "space $PVIP_SPACE"
if [ -z "$(space_id)" ]; then
  emit "create space $PVIP_SPACE" spaces create name="$PVIP_SPACE"
else note "space $PVIP_SPACE exists -- SKIP create"; fi

# ---- 2) VLAN VID 104 on the provider fabric --------------------------------
hdr "VLAN VID $PVIP_VID on fabric $PROV_FAB"
if [ -z "$(vlanobj_on_fab "$PROV_FAB" "$PVIP_VID")" ]; then
  MTU="$(parent_mtu)"; { [ -n "$MTU" ] && [ "$MTU" != "null" ]; } || MTU="1500"
  emit "create VLAN vid=$PVIP_VID name=$PVIP_SPACE mtu=$MTU on fabric $PROV_FAB" \
    vlans create "$PROV_FAB" name="$PVIP_SPACE" vid="$PVIP_VID" mtu="$MTU"
else note "VID $PVIP_VID already on fabric $PROV_FAB -- SKIP create"; fi

# ---- 3) assign VLAN -> space (idempotent) ----------------------------------
hdr "assign VID $PVIP_VID -> space $PVIP_SPACE"
CURSPACE="$(vlanspace_on_fab "$PROV_FAB" "$PVIP_VID")"
if [ "$CURSPACE" != "$PVIP_SPACE" ]; then
  SID_SPACE="$(space_id)"; [ -n "$SID_SPACE" ] || SID_SPACE="<provider-vip-space-id>"
  emit "assign fabric $PROV_FAB vid $PVIP_VID -> space $PVIP_SPACE (id $SID_SPACE)" \
    vlan update "$PROV_FAB" "$PVIP_VID" space="$SID_SPACE"
else note "VID $PVIP_VID already in space $PVIP_SPACE -- SKIP"; fi

# ---- 4) subnet + gateway/managed/dns ---------------------------------------
hdr "subnet $PVIP_CIDR on VID $PVIP_VID"
if [ -z "$(subid_of "$PVIP_CIDR")" ]; then
  VOBJ="$(vlanobj_on_fab "$PROV_FAB" "$PVIP_VID")"; [ -n "$VOBJ" ] || VOBJ="<vid-$PVIP_VID-vlan-obj-id>"
  emit "create subnet $PVIP_CIDR vlan=$VOBJ" subnets create cidr="$PVIP_CIDR" vlan="$VOBJ"
else note "subnet $PVIP_CIDR exists -- SKIP create"; fi
# gateway (routed plane); only if set and not already correct
if [ -n "$PVIP_GATEWAY" ]; then
  CURGW="$(sub_field "$PVIP_CIDR" gateway_ip)"
  if [ "$CURGW" != "$PVIP_GATEWAY" ]; then
    emit "subnet $PVIP_CIDR -> gateway_ip=$PVIP_GATEWAY" subnet update "$PVIP_CIDR" gateway_ip="$PVIP_GATEWAY"
  else note "gateway_ip already $PVIP_GATEWAY -- SKIP"; fi
fi
# managed
if [ "$(sub_field "$PVIP_CIDR" managed)" != "true" ]; then
  emit "subnet $PVIP_CIDR -> managed=true" subnet update "$PVIP_CIDR" managed=true
else note "subnet $PVIP_CIDR already managed -- SKIP"; fi
# dns mirrored from metal-internal (only if metal has dns and ours differs)
DNS="$(metal_dns)"
if [ -n "$DNS" ] && [ "$DNS" != "null" ]; then
  CURDNS="$(maas_q subnets read | jq -r --arg c "$PVIP_CIDR" '.[]|select(.cidr==$c)|(.dns_servers // []|join(","))' | head -1)"
  if [ "$CURDNS" != "$DNS" ]; then
    emit "subnet $PVIP_CIDR -> dns_servers=$DNS (mirrors metal-internal)" subnet update "$PVIP_CIDR" dns_servers="$DNS"
  else note "dns_servers already $DNS -- SKIP"; fi
else note "metal-internal has no dns_servers -- leaving $PVIP_CIDR dns unset"; fi

# ---- 5) reserved VIP band --------------------------------------------------
hdr "reserved VIP band $PVIP_RANGE_LO-$PVIP_RANGE_HI"
if maas_q ipranges read | jq -e --arg lo "$PVIP_RANGE_LO" '.[]|select(.start_ip==$lo)' >/dev/null 2>&1; then
  note "reserved range starting $PVIP_RANGE_LO exists -- SKIP"
else
  SUB="$(subid_of "$PVIP_CIDR")"; [ -n "$SUB" ] || SUB="<subnet-$PVIP_CIDR-id>"
  emit "create reserved range $PVIP_RANGE_LO-$PVIP_RANGE_HI on subnet $SUB" \
    ipranges create type=reserved subnet="$SUB" start_ip="$PVIP_RANGE_LO" end_ip="$PVIP_RANGE_HI" \
    comment="provider-vip API VIP band (D-057)"
fi

# ---- result ----------------------------------------------------------------
hdr "resulting provider-vip subnet (live)"
maas_q subnets read | jq -r --arg c "$PVIP_CIDR" '.[]|select(.cidr==$c)
  |{cidr, vid:.vlan.vid, fabric:.vlan.fabric, space, managed, gateway_ip, dns_servers}' \
  || note "(dry-run: plane not created yet -- the WOULD lines above are the plan)"

[ "$FATAL" = 0 ] || { echo; echo "completed with $FATAL failure(s)"; exit 1; }
echo; echo "OK ($MODE)"
