#!/bin/bash
# wg-sync-policy.sh — Reads wg0.conf, validates, generates policy.json atomically
# Fixed: IP validation, atomic write, proper locking, error handling

set -euo pipefail

SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
source "${SCRIPT_DIR}/wg-policy-lib.sh"

# ============================================================
# MAIN
# ============================================================

main() {
    log_info "Starting policy sync..."

    # Validate prerequisites
    if [[ ! -f "$WG_CONF" ]]; then
        log_error "WireGuard config not found: $WG_CONF"
        exit 1
    fi

    if ! command -v jq &>/dev/null; then
        log_error "jq is required but not installed"
        exit 1
    fi

    # Acquire lock
    acquire_lock
    trap 'release_lock' EXIT

    # Backup current policy
    backup_policy

    # Temporary file for atomic write
    local tmp_policy
    tmp_policy="$(mktemp /tmp/wg-policy.XXXXXX)"
    trap "rm -f \"$tmp_policy\" 2>/dev/null; release_lock" EXIT

    echo '{"clients":{}}' > "$tmp_policy"

    # Parse peers from wg0.conf
    # AWK extracts IP and #Access comment per [Peer] block
    local parse_errors=0

    awk '
    BEGIN { RS="\n\\[Peer\\]\n"; FS="\n" }
    NR>1 {
        ip=""; access=""; internet="false"
        for(i=1;i<=NF;i++){
            if($i ~ /^AllowedIPs/) {
                split($i,a," = ")
                gsub(/ /,"",a[2])
                split(a[2],b,",")
                split(b[1],c,"/")
                ip=c[1]
            }
            if($i ~ /^#Access/) {
                sub(/^#Access[ \t]*=?[ \t]*/, "", $i)
                access=$i
            }
            if($i ~ /^#Internet/) {
                if(tolower($i) ~ /true|yes|1|allow/) {
                    internet="true"
                }
            }
        }
        if(ip!="" && ip!="0.0.0.0" && ip!="::") {
            printf "%s|%s|%s\n", ip, access, internet
        }
    }
    ' "$WG_CONF" | while IFS="|" read -r ip access_string internet_flag; do

        # === VALIDATE CLIENT IP ===
        if ! validate_cidr "$ip"; then
            log_warn "Invalid client IP skipped: '$ip'"
            (( parse_errors++ )) || true
            continue
        fi

        # === PARSE AND VALIDATE ACCESS TARGETS ===
        local ACCESS_JSON="[]"

        if [[ -n "$access_string" ]]; then
            # Split by ; and , then validate each entry
            local valid_targets=()
            local IFS_OLD="$IFS"
            IFS=';,'
            read -ra targets <<< "$access_string"
            IFS="$IFS_OLD"

            for target in "${targets[@]}"; do
                # Trim whitespace
                target="$(echo "$target" | sed 's/^[[:space:]]*//;s/[[:space:]]*$//')"

                [[ -z "$target" ]] && continue

                if validate_cidr "$target"; then
                    valid_targets+=("$target")
                else
                    log_warn "Invalid access target skipped for $ip: '$target'"
                    (( parse_errors++ )) || true
                fi
            done

            if (( ${#valid_targets[@]} > 0 )); then
                ACCESS_JSON=$(printf '%s\n' "${valid_targets[@]}" | jq -R . | jq -s .)
            fi
        fi

        # Write to temp policy
        jq --arg ip "$ip" --argjson access "$ACCESS_JSON" --argjson internet "$internet_flag" \
           '.clients[$ip] = {"name": $ip, "access": $access, "internet": $internet}' \
           "$tmp_policy" > "${tmp_policy}.tmp" && mv "${tmp_policy}.tmp" "$tmp_policy"

    done

    # Validate JSON before atomic move
    if ! jq empty "$tmp_policy" 2>/dev/null; then
        log_error "Generated JSON is invalid, aborting. Check $tmp_policy"
        exit 1
    fi

    # Atomic move (same filesystem = atomic rename)
    mv -f "$tmp_policy" "$POLICY_FILE"
    log_info "policy.json updated successfully"

    if (( parse_errors > 0 )); then
        log_warn "$parse_errors validation errors encountered (see warnings above)"
    fi

    local client_count
    client_count=$(jq '(.clients // {}) | length' "$POLICY_FILE")
    log_info "Total clients in policy: $client_count"
}

main "$@"
