#!/usr/bin/env bash
###############################################################################
# firewall_payment.sh — Payment (714820) · 10.69.69.7
# 2 WG tunnels · firewalld · default zone: drop · Minimal config
# Rich rule: 8443/tcp from Shield rain IP (172.23.83.1) on zone-mesh
# Idempotent — safe to re-run at any time.
###############################################################################
set -euo pipefail

readonly SCRIPT_NAME="firewall_payment"
readonly LOG_TAG="[${SCRIPT_NAME}]"
readonly TIMESTAMP_FMT="+%Y-%m-%dT%H:%M:%S%z"

# ── Logging helpers ─────────────────────────────────────────────────────────
log()  { printf '%s %s %s\n' "$(date "${TIMESTAMP_FMT}")" "${LOG_TAG}" "$*"; }
warn() { log "WARN: $*" >&2; }
die()  { log "FATAL: $*" >&2; exit 1; }
trap 'die "Unexpected error on line ${LINENO}"' ERR

# ── Pre-flight checks ──────────────────────────────────────────────────────
[[ $(id -u) -eq 0 ]] || die "Must run as root"
command -v firewall-cmd >/dev/null 2>&1 || die "firewalld not installed"
systemctl is-active --quiet firewalld    || die "firewalld is not running"

log "Starting Payment firewall configuration"

# ── Helpers ─────────────────────────────────────────────────────────────────
ensure_zone() {
    local zone="$1"
    if ! firewall-cmd --permanent --get-zones | grep -qw "${zone}"; then
        log "Creating zone: ${zone}"
        firewall-cmd --permanent --new-zone="${zone}"
    else
        log "Zone already exists: ${zone}"
    fi
}

ensure_port() {
    local zone="$1" port="$2"
    if ! firewall-cmd --permanent --zone="${zone}" --query-port="${port}" 2>/dev/null; then
        log "Adding port ${port} to zone ${zone}"
        firewall-cmd --permanent --zone="${zone}" --add-port="${port}"
    else
        log "Port ${port} already in zone ${zone}"
    fi
}

ensure_source() {
    local zone="$1" src="$2"
    if ! firewall-cmd --permanent --zone="${zone}" --query-source="${src}" 2>/dev/null; then
        log "Adding source ${src} to zone ${zone}"
        firewall-cmd --permanent --zone="${zone}" --add-source="${src}"
    else
        log "Source ${src} already in zone ${zone}"
    fi
}

ensure_interface() {
    local zone="$1" iface="$2"
    if ! firewall-cmd --permanent --zone="${zone}" --query-interface="${iface}" 2>/dev/null; then
        log "Binding interface ${iface} to zone ${zone}"
        firewall-cmd --permanent --zone="${zone}" --add-interface="${iface}"
    else
        log "Interface ${iface} already bound to zone ${zone}"
    fi
}

ensure_rich_rule() {
    local zone="$1" rule="$2"
    if ! firewall-cmd --permanent --zone="${zone}" --query-rich-rule="${rule}" 2>/dev/null; then
        log "Adding rich rule to zone ${zone}: ${rule}"
        firewall-cmd --permanent --zone="${zone}" --add-rich-rule="${rule}"
    else
        log "Rich rule already in zone ${zone}"
    fi
}

###############################################################################
# 1. Default zone → drop
###############################################################################
log "Setting default zone to drop"
firewall-cmd --set-default-zone=drop

###############################################################################
# 2. zone-mesh (wg7) — WireGuard mesh: SSH 9922/tcp
###############################################################################
ensure_zone "zone-mesh"
ensure_interface "zone-mesh" "wg7"
ensure_source "zone-mesh" "10.69.69.0/24"
ensure_port "zone-mesh" "9922/tcp"

###############################################################################
# 3. Rich rule: 8443/tcp from Shield rain tunnel IP on zone-mesh
###############################################################################
RAIN_RULE='rule family="ipv4" source address="172.23.83.1" port port="8443" protocol="tcp" accept'
ensure_rich_rule "zone-mesh" "${RAIN_RULE}"

###############################################################################
# 4. zone-comp (compass) — compass overlay
###############################################################################
ensure_zone "zone-comp"
ensure_interface "zone-comp" "compass"
ensure_source "zone-comp" "172.28.47.0/28"

###############################################################################
# 5. Public zone — UDP tunnel establishment only
###############################################################################
log "Configuring public zone UDP ports for tunnel establishment"
PAYMENT_PUBLIC_UDP=(41820 42783)
for port in "${PAYMENT_PUBLIC_UDP[@]}"; do
    ensure_port "public" "${port}/udp"
done

###############################################################################
# 6. Reload & verify
###############################################################################
log "Reloading firewalld"
firewall-cmd --reload

log "═══════════════════════════════════════════════════════════"
log "Payment firewall configuration complete"
log "Default zone: $(firewall-cmd --get-default-zone)"
log "═══════════════════════════════════════════════════════════"

log "Active zones:"
firewall-cmd --get-active-zones

for z in zone-mesh zone-comp public; do
    log "--- Zone: ${z} ---"
    firewall-cmd --zone="${z}" --list-all 2>/dev/null || log "  (zone ${z} not active)"
done

log "Payment firewall script finished successfully"
