#!/usr/bin/env bash
# =============================================================================
# onx-firewall-rule-remove — Remove a firewall rule by id, ip, port or service
#
# Input (stdin JSON):
#   {
#     "rule_id":  "<rule-token-from-add>",         (optional, preferred)
#     "type":     "ip" | "port" | "service",
#     "value":    "1.2.3.4" | "443" | "http",
#     "action":   "accept" | "reject" | "drop",
#     "zone":     "public"
#   }
#
# Output (stdout JSON):
#   { "removed": true, "backend": "firewalld|ufw|nftables" }
#
# Deployed to: /usr/local/onoxsoft/bin/onx-firewall-rule-remove
# =============================================================================

set -euo pipefail

SCRIPT_DIR="$(dirname "$(readlink -f "$0")")"
# shellcheck source=_lib/common.sh
source "${SCRIPT_DIR}/_lib/common.sh"

require_root

onx_json_input

TYPE=$(onx_json_field   "type"      "ip")
VALUE=$(onx_json_field  "value"     "")
ACTION=$(onx_json_field "action"    "drop")
ZONE=$(onx_json_field   "zone"      "public")

[[ -n "${VALUE}" ]] || onx_die 1 "value is required"

# Detect backend (same logic as add)
BACKEND=""
if command -v firewall-cmd >/dev/null 2>&1 && \
   { systemctl is-active --quiet firewalld 2>/dev/null || [[ "${MOCK_MODE}" == "1" ]]; }; then
    BACKEND="firewalld"
elif command -v ufw >/dev/null 2>&1; then
    BACKEND="ufw"
elif command -v nft >/dev/null 2>&1; then
    BACKEND="nftables"
else
    onx_die 2 "no supported firewall backend found"
fi

if [[ "${MOCK_MODE}" == "1" ]]; then
    onx_audit "onx-firewall" "MOCK remove type=${TYPE} value=${VALUE} backend=${BACKEND}"
    jq -nc --arg b "${BACKEND}" '{removed:true, backend:$b}'
    exit 0
fi

REMOVED="false"

if [[ "${BACKEND}" == "firewalld" ]]; then
    case "${TYPE}" in
        ip)
            RICH="rule family=ipv4 source address=\"${VALUE}\" ${ACTION}"
            ;;
        port)
            PORT_NUM="${VALUE%%/*}"
            PORT_PROTO="${VALUE##*/}"
            [[ "${PORT_PROTO}" == "${VALUE}" ]] && PORT_PROTO="tcp"
            RICH="rule family=ipv4 port port=\"${PORT_NUM}\" protocol=\"${PORT_PROTO}\" ${ACTION}"
            ;;
        service)
            RICH="rule family=ipv4 service name=\"${VALUE}\" ${ACTION}"
            ;;
        *) onx_die 1 "unknown type '${TYPE}'" ;;
    esac
    if firewall-cmd --zone="${ZONE}" --query-rich-rule="${RICH}" >/dev/null 2>&1; then
        firewall-cmd --permanent --zone="${ZONE}" --remove-rich-rule="${RICH}" >/dev/null \
            || onx_die 3 "firewall-cmd remove failed"
        firewall-cmd --reload >/dev/null 2>&1 || true
        REMOVED="true"
    else
        onx_log "firewalld: rule not present (idempotent remove): ${RICH}"
        REMOVED="true"
    fi

elif [[ "${BACKEND}" == "ufw" ]]; then
    UFW_ACTION="deny"
    [[ "${ACTION}" == "accept" ]] && UFW_ACTION="allow"
    [[ "${ACTION}" == "reject" ]] && UFW_ACTION="reject"
    case "${TYPE}" in
        ip)      ufw delete "${UFW_ACTION}" from "${VALUE}" >/dev/null 2>&1 || true ;;
        port)    ufw delete "${UFW_ACTION}" "${VALUE}"      >/dev/null 2>&1 || true ;;
        service) ufw delete "${UFW_ACTION}" "${VALUE}"      >/dev/null 2>&1 || true ;;
    esac
    ufw reload >/dev/null 2>&1 || true
    REMOVED="true"

elif [[ "${BACKEND}" == "nftables" ]]; then
    # Locate and delete by matching expr on input chain
    case "${TYPE}" in
        ip)
            HANDLE=$(nft -a list chain inet filter input 2>/dev/null \
                | grep "ip saddr ${VALUE}" \
                | grep -oE 'handle [0-9]+' \
                | awk '{print $2}' | head -1)
            ;;
        port)
            PORT_NUM="${VALUE%%/*}"
            HANDLE=$(nft -a list chain inet filter input 2>/dev/null \
                | grep "dport ${PORT_NUM}" \
                | grep -oE 'handle [0-9]+' \
                | awk '{print $2}' | head -1)
            ;;
        service)
            HANDLE=""
            ;;
    esac
    if [[ -n "${HANDLE}" ]]; then
        nft delete rule inet filter input handle "${HANDLE}" \
            || onx_log "WARNING: nft delete handle ${HANDLE} returned non-zero"
        REMOVED="true"
    else
        onx_log "nftables: matching rule not found"
        REMOVED="true"
    fi
fi

onx_audit "onx-firewall" "remove type=${TYPE} value=${VALUE} backend=${BACKEND}"

jq -nc --argjson r "${REMOVED}" --arg b "${BACKEND}" '{removed:$r, backend:$b}'
