#!/usr/bin/env bash
#
# onx-modsec-rules-list — CRS rules katalog (BASH-native, awk-extract bug-free).
#
# Output: {"rules":[{rule_id, category, tag, severity, description, phase, enabled}], "total":N}
#
# v33 awk approach extract() "0" döndürüyordu. v43: awk SUBSTR direkt + tek pass.

set -uo pipefail

SCRIPT_DIR="$(dirname "$(readlink -f "$0")")"
# shellcheck source=_lib/common.sh
source "${SCRIPT_DIR}/_lib/common.sh"
set +e   # common.sh'in set -e'sini geri al — awk/grep no-match tolere edilmeli

# v3.41: aktif driver'ın CRS dizinini önceliklendir (v3 driver'lar shared v3 kullanır)
declare -a CRS_PATHS=(
    "${ONX_MODSEC_SHARED_CRS_V3}/rules"
    "/usr/share/mod_modsecurity_crs/rules"
    "/etc/httpd/modsecurity.d/activated_rules"
    "/etc/httpd/modsecurity.d/owasp-crs/rules"
    "/etc/modsecurity.d/owasp-crs/rules"
    "/usr/local/owasp-crs/rules"
    "/etc/nginx/modsec/coreruleset/rules"
)

rules_dir=""
# Önce aktif driver'ın çözülmüş CRS dizini
crs_base=$(onx_modsec_crs_dir auto 2>/dev/null || echo "")
[[ -n "$crs_base" && -d "$crs_base/rules" ]] && rules_dir="$crs_base/rules"
if [[ -z "$rules_dir" ]]; then
    for p in "${CRS_PATHS[@]}"; do
        if [[ -d "$p" ]]; then
            rules_dir="$p"
            break
        fi
    done
fi

if [[ -z "$rules_dir" ]]; then
    jq -nc '{ok:false,error:"OWASP CRS rules directory not found",rules:[],total:0}' >&2
    exit 2
fi

# Single awk pass — substr() direkt (extract function YOK — bug kaçınma)
output=$(awk '
function json_escape(s,    out) {
    out = s ""    # force string
    gsub(/\\/, "\\\\", out)
    gsub(/"/, "\\\"", out)
    return out
}

BEGIN {
    first = 1
    count = 0
    printf "["
}

FNR == 1 {
    # Yeni dosya: kategori adı
    file = FILENAME
    sub(/.*\//, "", file)        # basename
    sub(/\.conf$/, "", file)
    category = file
    sub(/^REQUEST-[0-9]+-/, "", category)
    sub(/^[0-9]+-/, "", category)
    gsub(/_/, " ", category)
    if (length(category) > 40) category = substr(category, 1, 40)
    buf = ""
}

# Backslash continuation: birleştir
/\\[[:space:]]*$/ {
    sub(/\\[[:space:]]*$/, "", $0)
    buf = buf $0
    next
}

{
    line = buf $0
    buf = ""
    if (line !~ /^[[:space:]]*SecRule/) next

    # rule_id (id:NNNNN)
    rule_id = ""
    if (match(line, /id:[0-9]+/)) {
        rule_id = substr(line, RSTART + 3, RLENGTH - 3)  # +3 skip "id:"
    }
    if (rule_id == "") next  # rule_id zorunlu

    # tag (tag:''X'')
    tag = ""
    if (match(line, /tag:'\''[^'\'']+'\''/)) {
        tag = substr(line, RSTART + 5, RLENGTH - 6)  # skip "tag:'"  trim trailing "'"
    } else if (match(line, /tag:[A-Za-z0-9_-]+/)) {
        tag = substr(line, RSTART + 4, RLENGTH - 4)
    }

    # severity
    severity = "INFO"
    if (match(line, /severity:'\''[A-Z]+'\''/)) {
        severity = substr(line, RSTART + 10, RLENGTH - 11)
    } else if (match(line, /severity:[A-Z]+/)) {
        severity = substr(line, RSTART + 9, RLENGTH - 9)
    }

    # msg (msg:''X'')
    msg = category
    if (match(line, /msg:'\''[^'\'']+'\''/)) {
        msg = substr(line, RSTART + 5, RLENGTH - 6)
    }

    # phase
    phase = 2
    if (match(line, /phase:[0-9]/)) {
        phase = substr(line, RSTART + 6, 1) + 0
    }

    # JSON record — explicit string force
    rule_id = rule_id ""; tag = tag ""; severity = severity ""; msg = msg ""

    if (!first) printf ","
    first = 0
    printf "{\"rule_id\":\"%s\",\"category\":\"%s\",\"tag\":\"%s\",\"severity\":\"%s\",\"description\":\"%s\",\"phase\":%d,\"enabled\":true}",
        json_escape(rule_id), json_escape(category), json_escape(tag),
        json_escape(severity), json_escape(msg), phase
    count++
}

END {
    printf "]"
    printf "RULE_COUNT=%d\n", count > "/dev/stderr"
}
' "$rules_dir"/*.conf 2>/tmp/modsec-rules-count.$$)

count=$(grep -oE 'RULE_COUNT=[0-9]+' /tmp/modsec-rules-count.$$ 2>/dev/null | cut -d= -f2 || echo 0)
rm -f /tmp/modsec-rules-count.$$

# Validate + wrap
echo "$output" | jq -c --argjson total "${count:-0}" '{ok:true, rules:., total:$total}' 2>/dev/null \
    || echo "{\"ok\":true,\"rules\":[],\"total\":${count:-0},\"warn\":\"jq parse failed\"}"
