#!/usr/bin/env bash

set -euo pipefail

# Creates PAIRS colliding VM *pairs* (2 VMIs per MAC) for collision gauge testing.
# - Total VMs: PAIRS * 2
# - Total namespaces/projects: PAIRS * 2 (each VM in its own namespace)
#
# Requirements:
# - You need permissions to create projects/namespaces and VMs.
# - KubeVirt must be installed.
#
# Tunables (env vars):
# - PAIRS: number of colliding pairs (default: 200)
# - NS_PREFIX: namespace/project name prefix (default: test-collision)
# - VM_PREFIX: VM name prefix (default: test-vm-collision)
# - MAC_PREFIX: first 5 bytes of the MAC, last byte will be pair index (default: 02:00:00:aa:bb)
# - WAIT_FOR_VMI: "true"/"false" (default: true)
# - WAIT_TIMEOUT: timeout per VMI wait (default: 120s)

PAIRS="${PAIRS:-200}"
NS_PREFIX="${NS_PREFIX:-test-collision}"
VM_PREFIX="${VM_PREFIX:-test-vm-collision}"
MAC_PREFIX="${MAC_PREFIX:-02:00:00:aa:bb}"
WAIT_FOR_VMI="${WAIT_FOR_VMI:-true}"
WAIT_TIMEOUT="${WAIT_TIMEOUT:-120s}"

if ! command -v oc >/dev/null 2>&1; then
  echo "ERROR: oc not found in PATH"
  exit 1
fi

if [[ "${PAIRS}" -lt 1 ]] || [[ "${PAIRS}" -gt 250 ]]; then
  echo "ERROR: PAIRS must be between 1 and 250 (got: ${PAIRS})"
  echo "       (we use the last MAC byte as the pair index)"
  exit 2
fi

namespaces=()

cleanup() {
  # Best-effort cleanup of all namespaces/projects created by this script.
  set +e

  if [[ "${#namespaces[@]}" -eq 0 ]]; then
    return 0
  fi

  echo ""
  echo "Starting cleanup (deleting ${#namespaces[@]} namespaces/projects)..."
  for ns in "${namespaces[@]}"; do
    oc delete namespace "${ns}" --ignore-not-found=true >/dev/null 2>&1 || true
  done
  echo "Cleanup initiated."
}

trap cleanup EXIT INT TERM

create_namespace() {
  local ns="$1"

  # Prefer plain Namespace (works on any Kubernetes).
  # On OpenShift, some users can only create Projects (via `oc new-project`) and not Namespaces directly,
  # so we fall back to that when namespace creation is forbidden.
  if oc get namespace "${ns}" >/dev/null 2>&1; then
    namespaces+=("${ns}")
    return 0
  fi

  # On OpenShift, prefer ProjectRequest (new-project) if allowed.
  # `oc new-project` may switch the current project; we always pass -n explicitly later, so that's OK.
  if oc auth can-i create projectrequests.project.openshift.io >/dev/null 2>&1; then
    if oc new-project "${ns}" --skip-config-write >/dev/null 2>&1; then
      namespaces+=("${ns}")
      return 0
    fi
  fi

  # Fall back to Namespace creation if allowed.
  if oc auth can-i create namespaces >/dev/null 2>&1; then
    if oc create namespace "${ns}" --dry-run=client -o yaml | oc apply -f - >/dev/null 2>&1; then
      namespaces+=("${ns}")
      return 0
    fi
  fi

  echo "ERROR: insufficient permissions to create a project/namespace '${ns}'."
  echo "       Need either:"
  echo "       - create projectrequests.project.openshift.io (OpenShift), or"
  echo "       - create namespaces (Kubernetes)."
  exit 1
}

apply_vm() {
  local ns="$1"
  local vm_name="$2"
  local mac="$3"

  cat <<EOF | oc apply -f -
apiVersion: kubevirt.io/v1
kind: VirtualMachine
metadata:
  name: ${vm_name}
  namespace: ${ns}
spec:
  runStrategy: Always
  template:
    metadata:
      labels:
        kubevirt.io/vm: ${vm_name}
    spec:
      domain:
        devices:
          disks:
          - disk:
              bus: virtio
            name: containerdisk
          - disk:
              bus: virtio
            name: cloudinitdisk
          interfaces:
          - name: default
            masquerade: {}
            macAddress: "${mac}"
        resources:
          requests:
            memory: 128Mi
      networks:
      - name: default
        pod: {}
      volumes:
      - containerDisk:
          image: quay.io/kubevirt/cirros-container-disk-demo
        name: containerdisk
      - cloudInitNoCloud:
          userData: |
            #cloud-config
            password: cirros
            chpasswd: { expire: False }
        name: cloudinitdisk
EOF
}

wait_vmi_running_best_effort() {
  local ns="$1"
  local name="$2"

  oc wait --for=jsonpath='{.status.phase}'=Running \
    --timeout="${WAIT_TIMEOUT}" \
    "vmi/${name}" -n "${ns}" 2>/dev/null || true
}

echo "==================================="
echo "Duplicate MAC Collision Test Script"
echo "==================================="
echo ""
echo "Creating ${PAIRS} colliding VM pairs (${PAIRS} MACs, $((PAIRS * 2)) VMs) across $((PAIRS * 2)) namespaces."
echo ""

for i in $(seq 1 "${PAIRS}"); do
  idx_dec=$((i - 1))
  idx_hex="$(printf '%02x' "${idx_dec}")"
  mac="${MAC_PREFIX}:${idx_hex}"

  # Keep names deterministic and short.
  i3="$(printf '%03d' "${i}")"
  ns_a="${NS_PREFIX}-${i3}-a"
  ns_b="${NS_PREFIX}-${i3}-b"
  vm_a="${VM_PREFIX}-${i3}-a"
  vm_b="${VM_PREFIX}-${i3}-b"

  echo ""
  echo "Pair ${i}/${PAIRS}: MAC=${mac}"
  echo "  - ${ns_a}/${vm_a}"
  echo "  - ${ns_b}/${vm_b}"

  create_namespace "${ns_a}"
  create_namespace "${ns_b}"

  apply_vm "${ns_a}" "${vm_a}" "${mac}"
  apply_vm "${ns_b}" "${vm_b}" "${mac}"

  if [[ "${WAIT_FOR_VMI}" == "true" ]]; then
    echo -n "  waiting for VMIs to reach Running (best effort)... "
    wait_vmi_running_best_effort "${ns_a}" "${vm_a}"
    wait_vmi_running_best_effort "${ns_b}" "${vm_b}"
    echo "done"
  fi
done

echo ""
echo "==================================="
echo "What to check"
echo "==================================="
echo ""
echo "1) Collision events (pick any pair):"
echo "   oc describe vmi -n ${NS_PREFIX}-001-a ${VM_PREFIX}-001-a"
echo "   oc describe vmi -n ${NS_PREFIX}-001-b ${VM_PREFIX}-001-b"
echo ""
echo "2) Metrics (needs access to the kubemacpool deployment/pod):"
echo "   curl /metrics from kubemacpool (see your environment's preferred method)"
echo "   Metric name: kmp_mac_collisions{mac=\"${MAC_PREFIX}:..\"}"
echo ""

echo "Press ENTER to start cleanup and exit..."
read -r
exit 0
