#!/usr/bin/env bash
#
# onx-modsec-rule-toggle — ModSec rule exclusion SET'ini yeniden yaz. DRIVER-AWARE (v3.41).
#
# Hem "devre dışı kurallar" (Kurallar sekmesi) hem "istisnalar" (İstisnalar sekmesi)
# tek bir efektif sete indirgenir; controller union'ı gönderir, script dosyayı
# bütünüyle yeniden üretir (idempotent — fragile line-grep yok).
#
#   Apache → /etc/httpd/modsecurity.d/onox-rule-states.conf (mevcut davranış)
#   v3 (nginx/ols/caddy) → neutral 20-exclusions.conf
#
# ctl:ruleRemoveById = RUNTIME removal → CRS'ten ÖNCE yüklense bile çalışır.
# (SecRuleRemoveById config-order'a bağımlı; before-CRS slot'ta "rule not found"
#  uyarısı verir ve kuralı kaldırmaz — bu yüzden ctl kullanıyoruz.)
#
# Input:  {"exclusions":[{"rule_id":"942100","domain_name":null},
#                         {"rule_id":"981318","domain_name":"example.com"}]}
# Output: {ok, applied, count, driver, file, reloaded}

set -euo pipefail

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

require_root

input="$(cat 2>/dev/null || echo '{}')"
echo "$input" | jq -e 'type == "object"' >/dev/null 2>&1 || input='{}'
count=$(echo "$input" | jq '.exclusions // [] | length')

DRIVER=$(onx_ws_active_driver)
if [[ "$DRIVER" == "apache" ]]; then
    CONF_FILE="/etc/httpd/modsecurity.d/onox-rule-states.conf"
elif [[ "$DRIVER" == "unknown" ]]; then
    onx_die 2 "Aktif web sunucusu tespit edilemedi (apache/ols/nginx/caddy hiçbiri active değil)"
else
    CONF_FILE="${ONX_MODSEC_NEUTRAL_DIR}/${ONX_MODSEC_NEUTRAL_SUBDIR}/20-exclusions.conf"
    mkdir -p "$(dirname "$CONF_FILE")" 2>/dev/null || true
fi

backup=""
if [[ -f "$CONF_FILE" ]]; then
    backup="${CONF_FILE}.onx-bak.$$"
    cp -p "$CONF_FILE" "$backup"
fi

# SecAction id'leri 4000000'den sıralı (deterministik + çakışmasız; aynı rule_id
# hem global hem per-domain hariç tutulabildiği için rule_id'den TÜRETMEYİZ).
tmp="$(mktemp -t onx-modsec.XXXXXX)"
{
    echo "# Onoxsoft Panel managed — DO NOT EDIT (UI: Admin > ModSecurity > Kurallar/İstisnalar)"
    echo "# Driver: ${DRIVER} | Auto-generated $(date -u +%Y-%m-%dT%H:%M:%SZ)"
    echo "# ctl:ruleRemoveById = RUNTIME removal (load-order bağımsız)"
    echo ""
    panel_id=4000000
    echo "$input" | jq -c '.exclusions[]?' | while IFS= read -r entry; do
        rid=$(echo "$entry" | jq -r '.rule_id // empty')
        domain=$(echo "$entry" | jq -r '.domain_name // empty')
        [[ "$rid" =~ ^[0-9]{1,6}$ ]] || continue
        if [[ -n "$domain" ]]; then
            # Domain-scoped removal (SERVER_NAME phase:1'de mevcut)
            echo "SecRule SERVER_NAME \"@streq ${domain}\" \"id:${panel_id},phase:1,nolog,pass,t:none,ctl:ruleRemoveById=${rid}\""
        else
            echo "SecAction \"id:${panel_id},phase:1,nolog,pass,t:none,ctl:ruleRemoveById=${rid}\""
        fi
        panel_id=$((panel_id + 1))
    done
} > "$tmp"
chmod 0644 "$tmp"
mv -f "$tmp" "$CONF_FILE"

reloaded=$(onx_ws_modsec_reload "$DRIVER")
if [[ "$reloaded" != "true" ]]; then
    if [[ -n "$backup" ]]; then
        mv -f "$backup" "$CONF_FILE" 2>/dev/null || true
    else
        rm -f "$CONF_FILE" 2>/dev/null || true
    fi
    onx_ws_modsec_reload "$DRIVER" >/dev/null 2>&1 || true
    onx_log "modsec-rule-toggle: driver=${DRIVER} reload FAILED — rolled back"
    jq -nc --arg driver "$DRIVER" '{ok:false,error:"reload failed — rolled back",driver:$driver}' >&2
    exit 4
fi
[[ -n "$backup" ]] && rm -f "$backup" 2>/dev/null || true

logger -t "onox-modsec" "Exclusion set written driver=${DRIVER} count=${count} reloaded=${reloaded}"

jq -nc --argjson count "$count" --arg driver "$DRIVER" --arg file "$CONF_FILE" \
    '{ok:true, applied:true, count:$count, driver:$driver, file:$file, reloaded:true}'
