#!/usr/bin/env bash
set -euo pipefail
# forge_tunnel_configs.sh — Generate WireGuard configs for the SCLS forge tunnel
#
# Forge tunnel: 172.28.50.0/28, port 45247
# Hub: Engine (669427) @ 172.28.50.1
# Spokes: all other nodes @ 172.28.50.{2..8}
#
# Generates keypairs, builds hub config, builds per-node spoke configs,
# and outputs firewalld commands. All output goes to:
#   /build-v2/deploy/forge/  (mapped to Y:\669427\build-v2\deploy\forge\)
#
# Usage: bash forge_tunnel_configs.sh

# ---------------------------------------------------------------------------
# Constants
# ---------------------------------------------------------------------------
readonly SCRIPT_NAME="forge_tunnel_configs"
readonly ENGINE_PUBLIC_IP="76.13.222.157"
readonly FORGE_PORT=45247
readonly FORGE_SUBNET="172.28.50.0/28"
readonly FORGE_IFACE="forge"

# Output directory
readonly OUTPUT_DIR="/build-v2/deploy/forge"
readonly KEYS_DIR="${OUTPUT_DIR}/keys"

# Node definitions: ID NAME WG_IP FORGE_IP
# Engine is the hub — gets .1
declare -A NODE_NAMES=(
    [414342]="Shield"
    [558201]="Origin"
    [244939]="Proxy"
    [333941]="Filter"
    [199335]="DNS"
    [669427]="Engine"
    [714820]="Payment"
    [869377]="Vault"
)

declare -A NODE_FORGE_IPS=(
    [669427]="172.28.50.1"   # Engine (hub)
    [414342]="172.28.50.2"   # Shield
    [558201]="172.28.50.3"   # Origin
    [244939]="172.28.50.4"   # Proxy
    [333941]="172.28.50.5"   # Filter
    [199335]="172.28.50.6"   # DNS
    [714820]="172.28.50.7"   # Payment
    [869377]="172.28.50.8"   # Vault
)

declare -A NODE_WG_IPS=(
    [414342]="10.69.69.1"
    [558201]="10.69.69.2"
    [244939]="10.69.69.3"
    [333941]="10.69.69.4"
    [199335]="10.69.69.5"
    [669427]="10.69.69.6"
    [714820]="10.69.69.7"
    [869377]="10.69.69.8"
)

# Spoke node IDs (all except Engine)
readonly SPOKE_IDS=(414342 558201 244939 333941 199335 714820 869377)
readonly HUB_ID="669427"

# All node IDs
readonly ALL_NODE_IDS=(669427 414342 558201 244939 333941 199335 714820 869377)

# ---------------------------------------------------------------------------
# Logging
# ---------------------------------------------------------------------------
log() {
    local level="$1"; shift
    printf '[%s] [%s] [%s] %s\n' \
        "$(date -u '+%Y-%m-%dT%H:%M:%S.%NZ' 2>/dev/null || date -u '+%Y-%m-%dT%H:%M:%SZ')" \
        "$SCRIPT_NAME" "$level" "$*"
}

die() {
    log "FATAL" "$*"
    exit 1
}

# ---------------------------------------------------------------------------
# Preflight checks
# ---------------------------------------------------------------------------
command -v wg &>/dev/null || die "wireguard-tools (wg) not found — install first"

# ---------------------------------------------------------------------------
# Create output directories
# ---------------------------------------------------------------------------
log "INFO" "Creating output directories under ${OUTPUT_DIR}"
mkdir -p "$KEYS_DIR"
mkdir -p "${OUTPUT_DIR}/configs/hub"
mkdir -p "${OUTPUT_DIR}/configs/spokes"
mkdir -p "${OUTPUT_DIR}/firewalld"
chmod 700 "$KEYS_DIR"

# ---------------------------------------------------------------------------
# Step 1: Generate keypairs for all nodes
# ---------------------------------------------------------------------------
log "INFO" "Generating WireGuard keypairs for ${#ALL_NODE_IDS[@]} nodes..."

declare -A PRIVKEYS PUBKEYS

for node_id in "${ALL_NODE_IDS[@]}"; do
    priv_file="${KEYS_DIR}/${node_id}.key"
    pub_file="${KEYS_DIR}/${node_id}.pub"

    if [[ -f "$priv_file" ]] && [[ -f "$pub_file" ]]; then
        log "INFO" "Keypair already exists for ${node_id} (${NODE_NAMES[$node_id]}), reusing"
        PRIVKEYS[$node_id]="$(< "$priv_file")"
        PUBKEYS[$node_id]="$(< "$pub_file")"
        continue
    fi

    privkey="$(wg genkey)"
    pubkey="$(echo "$privkey" | wg pubkey)"

    printf '%s' "$privkey" > "$priv_file"
    printf '%s' "$pubkey" > "$pub_file"
    chmod 600 "$priv_file"
    chmod 644 "$pub_file"

    PRIVKEYS[$node_id]="$privkey"
    PUBKEYS[$node_id]="$pubkey"

    log "INFO" "Generated keypair for ${node_id} (${NODE_NAMES[$node_id]})"
done

# ---------------------------------------------------------------------------
# Step 2: Build hub config (Engine)
# ---------------------------------------------------------------------------
log "INFO" "Building hub config for Engine (${HUB_ID})..."

hub_config="${OUTPUT_DIR}/configs/hub/forge.conf"

cat > "$hub_config" <<EOF
# forge.conf — Engine (${HUB_ID}) hub config
# Forge tunnel: ${FORGE_SUBNET} on port ${FORGE_PORT}
# Generated: $(date -u '+%Y-%m-%dT%H:%M:%SZ')
# Deploy to: /etc/wireguard/forge.conf

[Interface]
Address = ${NODE_FORGE_IPS[$HUB_ID]}/28
ListenPort = ${FORGE_PORT}
PrivateKey = ${PRIVKEYS[$HUB_ID]}
EOF

# Add each spoke as a peer
for spoke_id in "${SPOKE_IDS[@]}"; do
    cat >> "$hub_config" <<EOF

# ${NODE_NAMES[$spoke_id]} (${spoke_id})
[Peer]
PublicKey = ${PUBKEYS[$spoke_id]}
AllowedIPs = ${NODE_FORGE_IPS[$spoke_id]}/32
EOF
done

chmod 600 "$hub_config"
log "INFO" "Hub config written: ${hub_config}"

# ---------------------------------------------------------------------------
# Step 3: Build spoke configs (all non-Engine nodes)
# ---------------------------------------------------------------------------
log "INFO" "Building spoke configs for ${#SPOKE_IDS[@]} nodes..."

for spoke_id in "${SPOKE_IDS[@]}"; do
    spoke_config="${OUTPUT_DIR}/configs/spokes/${spoke_id}_forge.conf"
    spoke_name="${NODE_NAMES[$spoke_id]}"

    cat > "$spoke_config" <<EOF
# forge.conf — ${spoke_name} (${spoke_id}) spoke config
# Forge tunnel: ${FORGE_SUBNET} on port ${FORGE_PORT}
# Generated: $(date -u '+%Y-%m-%dT%H:%M:%SZ')
# Deploy to: /etc/wireguard/forge.conf

[Interface]
Address = ${NODE_FORGE_IPS[$spoke_id]}/32
PrivateKey = ${PRIVKEYS[$spoke_id]}

[Peer]
# Engine hub (${HUB_ID})
PublicKey = ${PUBKEYS[$HUB_ID]}
Endpoint = ${ENGINE_PUBLIC_IP}:${FORGE_PORT}
AllowedIPs = ${FORGE_SUBNET}
PersistentKeepalive = 25
EOF

    chmod 600 "$spoke_config"
    log "INFO" "Spoke config written: ${spoke_config} (${spoke_name})"
done

# ---------------------------------------------------------------------------
# Step 4: Generate firewalld commands
# ---------------------------------------------------------------------------
log "INFO" "Generating firewalld commands..."

firewalld_script="${OUTPUT_DIR}/firewalld/apply_forge_firewalld.sh"

cat > "$firewalld_script" <<'EOFHDR'
#!/usr/bin/env bash
set -euo pipefail
# apply_forge_firewalld.sh — Add forge tunnel port to firewalld
#
# Run on EACH node to allow forge WireGuard traffic on the public interface.
# Also creates a dedicated firewalld zone for the forge tunnel interface.
#
# Usage: sudo bash apply_forge_firewalld.sh

EOFHDR

cat >> "$firewalld_script" <<EOF
FORGE_PORT="${FORGE_PORT}"
FORGE_IFACE="${FORGE_IFACE}"
FORGE_SUBNET="${FORGE_SUBNET}"

log() {
    printf '[%s] [forge_firewalld] [%s] %s\n' "\$(date -u '+%Y-%m-%dT%H:%M:%SZ')" "\$1" "\$2"
}

if [[ "\$(id -u)" -ne 0 ]]; then
    log "FATAL" "Must run as root"
    exit 1
fi

# Ensure default zone is drop
current_default="\$(firewall-cmd --get-default-zone 2>/dev/null || echo '')"
if [[ "\$current_default" != "drop" ]]; then
    log "INFO" "Setting default zone to drop (was: \${current_default})"
    firewall-cmd --set-default-zone=drop --permanent 2>/dev/null || true
fi

# Allow forge WireGuard UDP port on the public zone
log "INFO" "Adding \${FORGE_PORT}/udp to public zone"
firewall-cmd --permanent --zone=public --add-port="\${FORGE_PORT}/udp" 2>/dev/null || true

# Create a dedicated zone for the forge interface (per NET-02)
if ! firewall-cmd --get-zones 2>/dev/null | grep -qw "forge"; then
    log "INFO" "Creating firewalld zone: forge"
    firewall-cmd --permanent --new-zone=forge 2>/dev/null || true
fi

# Add forge interface to its dedicated zone
log "INFO" "Assigning interface \${FORGE_IFACE} to zone forge"
firewall-cmd --permanent --zone=forge --add-interface="\${FORGE_IFACE}" 2>/dev/null || true

# Allow traffic within forge subnet
log "INFO" "Adding forge subnet rich rule"
firewall-cmd --permanent --zone=forge --add-rich-rule="rule family=ipv4 source address=\${FORGE_SUBNET} accept" 2>/dev/null || true

# Reload
firewall-cmd --reload 2>/dev/null || true
log "INFO" "Firewalld rules applied successfully"

# Verify
echo ""
echo "--- Forge zone rules ---"
firewall-cmd --zone=forge --list-all 2>/dev/null || true
echo ""
echo "--- Public zone (port check) ---"
firewall-cmd --zone=public --list-ports 2>/dev/null || true
EOF

chmod 755 "$firewalld_script"
log "INFO" "Firewalld script written: ${firewalld_script}"

# ---------------------------------------------------------------------------
# Step 5: Generate deployment script
# ---------------------------------------------------------------------------
log "INFO" "Generating deployment helper script..."

deploy_script="${OUTPUT_DIR}/deploy_forge.sh"

cat > "$deploy_script" <<'EOFHDR'
#!/usr/bin/env bash
set -euo pipefail
# deploy_forge.sh — Deploy forge tunnel configs to all nodes
#
# Run from Engine. Copies configs via SSH, enables the WG interface.
# IMPORTANT: Run hello check-in on each node before executing.
#
# Usage: sudo bash deploy_forge.sh

SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
SSH_KEY="/opt/binary/config/keys/gabriella"
SSH_PORT=9922
SSH_OPTS="-o StrictHostKeyChecking=no -o ConnectTimeout=10"

log() {
    printf '[%s] [deploy_forge] [%s] %s\n' "$(date -u '+%Y-%m-%dT%H:%M:%SZ')" "$1" "$2"
}

EOFHDR

cat >> "$deploy_script" <<EOF
# Deploy hub config locally (Engine)
log "INFO" "Deploying hub config to Engine (local)"
cp "${OUTPUT_DIR}/configs/hub/forge.conf" /etc/wireguard/forge.conf
chmod 600 /etc/wireguard/forge.conf
chown root:root /etc/wireguard/forge.conf

# Enable and start forge interface on Engine
systemctl enable wg-quick@forge 2>/dev/null || true
wg-quick up forge 2>/dev/null || wg syncconf forge <(wg-quick strip forge) 2>/dev/null || true
log "INFO" "Engine forge interface configured"

EOF

for spoke_id in "${SPOKE_IDS[@]}"; do
    wg_ip="${NODE_WG_IPS[$spoke_id]}"
    spoke_name="${NODE_NAMES[$spoke_id]}"

    cat >> "$deploy_script" <<EOF
# Deploy to ${spoke_name} (${spoke_id})
log "INFO" "Deploying forge config to ${spoke_name} (${spoke_id}) @ ${wg_ip}"
scp -i "\$SSH_KEY" -P "\$SSH_PORT" \$SSH_OPTS \\
    "${OUTPUT_DIR}/configs/spokes/${spoke_id}_forge.conf" \\
    "admin@${wg_ip}:/tmp/forge.conf.tmp"

ssh -i "\$SSH_KEY" -p "\$SSH_PORT" \$SSH_OPTS "admin@${wg_ip}" \\
    "sudo mv /tmp/forge.conf.tmp /etc/wireguard/forge.conf && \\
     sudo chmod 600 /etc/wireguard/forge.conf && \\
     sudo chown root:root /etc/wireguard/forge.conf && \\
     sudo systemctl enable wg-quick@forge 2>/dev/null || true && \\
     sudo wg-quick up forge 2>/dev/null || sudo wg syncconf forge <(wg-quick strip forge) 2>/dev/null || true"

log "INFO" "${spoke_name} forge interface configured"

EOF
done

cat >> "$deploy_script" <<'EOFEND'
log "INFO" "All forge tunnel configs deployed. Run firewalld script on each node."
echo ""
echo "Next steps:"
echo "  1. Run apply_forge_firewalld.sh on EACH node (including Engine)"
echo "  2. Verify connectivity: ping -c 3 172.28.50.X from each node"
echo "  3. Check WG status: sudo wg show forge"
EOFEND

chmod 755 "$deploy_script"
log "INFO" "Deployment script written: ${deploy_script}"

# ---------------------------------------------------------------------------
# Summary
# ---------------------------------------------------------------------------
echo ""
echo "========================================================================"
echo "  Forge Tunnel Configuration Generation Complete"
echo "========================================================================"
echo ""
echo "  Subnet:     ${FORGE_SUBNET}"
echo "  Port:       ${FORGE_PORT}/udp"
echo "  Hub:        Engine (${HUB_ID}) @ ${NODE_FORGE_IPS[$HUB_ID]}"
echo ""
printf '  %-8s  %-10s  %-14s  %s\n' "NODE_ID" "NAME" "FORGE_IP" "PUBKEY_PREFIX"
printf '  %-8s  %-10s  %-14s  %s\n' "--------" "----------" "--------------" "------------"

for node_id in "${ALL_NODE_IDS[@]}"; do
    pubkey_prefix="${PUBKEYS[$node_id]:0:12}"
    printf '  %-8s  %-10s  %-14s  %s...\n' \
        "$node_id" "${NODE_NAMES[$node_id]}" "${NODE_FORGE_IPS[$node_id]}" "$pubkey_prefix"
done

echo ""
echo "  Output directory: ${OUTPUT_DIR}/"
echo ""
echo "  Files generated:"
echo "    configs/hub/forge.conf           — Engine hub config"
for spoke_id in "${SPOKE_IDS[@]}"; do
    echo "    configs/spokes/${spoke_id}_forge.conf  — ${NODE_NAMES[$spoke_id]} spoke config"
done
echo "    firewalld/apply_forge_firewalld.sh  — Firewalld setup script"
echo "    deploy_forge.sh                     — Deployment helper"
echo "    keys/*.key, keys/*.pub              — WireGuard keypairs"
echo ""
echo "========================================================================"

log "INFO" "Forge tunnel config generation complete."
