#!/bin/bash
# wg-policy-ctl — CLI management tool for WireGuard Policy Firewall

set -euo pipefail

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

usage() {
    cat <<EOF
Usage: $(basename "$0") <command>

Commands:
    status      Show full health check report
    policy      Display current policy.json formatted
    rules       Show current iptables rules in WG_POLICY chain
    ipset       Show ipset contents
    log         Tail WG_DROP logs (last 50 lines)
    reload      Force re-sync and re-apply policy
    backup      Manual backup of policy + iptables
    stats       Show connection and rule statistics
    validate    Validate policy.json without applying
    help        Show this help
EOF
}

cmd_status() {
    echo "========================================="
    echo " WireGuard Policy Firewall Status"
    echo " $(date '+%Y-%m-%d %H:%M:%S')"
    echo "========================================="
    echo ""
    health_check
}

cmd_policy() {
    if [[ -f "$POLICY_FILE" ]]; then
        jq '.' "$POLICY_FILE"
    else
        log_error "policy.json not found"
        exit 1
    fi
}

cmd_rules() {
    echo "=== IPv4 Chain: $CHAIN ==="
    if iptables -L "$CHAIN" -n -v --line-numbers 2>/dev/null; then
        echo ""
    else
        echo "(chain not found)"
    fi

    echo "=== FORWARD references ==="
    iptables -L FORWARD -n -v --line-numbers 2>/dev/null | grep -i "$CHAIN" || echo "(none)"

    if command -v ip6tables &>/dev/null; then
        echo ""
        echo "=== IPv6 Chain: $CHAIN ==="
        ip6tables -L "$CHAIN" -n -v --line-numbers 2>/dev/null || echo "(chain not found)"
    fi
}

cmd_ipset() {
    for set_name in "$IPSET_V4" "$IPSET_V6"; do
        echo "=== ipset: $set_name ==="
        if ipset list "$set_name" 2>/dev/null; then
            echo ""
        else
            echo "(not found)"
            echo ""
        fi
    done
}

cmd_log() {
    echo "=== Recent WG_DROP log entries ==="
    (journalctl -k --no-pager -n 50 2>/dev/null || dmesg | tail -50) | grep "$LOG_PREFIX" || echo "(no entries)"
}

cmd_reload() {
    log_info "Force reloading policy..."
    if retry /usr/local/bin/wg-sync-policy.sh; then
        if retry /usr/local/bin/wg-policy-engine.sh; then
            log_info "Reload complete"
        else
            log_error "Engine failed"
            exit 1
        fi
    else
        log_error "Sync failed"
        exit 1
    fi
}

cmd_backup() {
    backup_policy
    backup_iptables
    log_info "Manual backup complete. Files in: $BACKUP_DIR"
}

cmd_stats() {
    echo "=== Client Count ==="
    jq '(.clients // {}) | length' "$POLICY_FILE" 2>/dev/null || echo "N/A"

    echo ""
    echo "=== Clients with Access ==="
    jq -r '.clients // {} | to_entries[] | select(.value.access | length > 0) | "\(.key): \(.value.access | join(", "))"' "$POLICY_FILE" 2>/dev/null || echo "N/A"

    echo ""
    echo "=== Clients without Access (Internet Only) ==="
    jq -r '.clients // {} | to_entries[] | select(.value.access | length == 0) | .key' "$POLICY_FILE" 2>/dev/null || echo "N/A"

    echo ""
    echo "=== Active iptables rules ==="
    iptables -L "$CHAIN" -n 2>/dev/null | tail -n +3 | wc -l || echo "N/A"

    echo ""
    echo "=== Drop count (since boot) ==="
    iptables -L "$CHAIN" -n -v 2>/dev/null | grep "DROP" | awk '{sum += $1} END {print sum+0, "packets dropped"}'

    echo ""
    echo "=== ipset entries ==="
    for set_name in "$IPSET_V4" "$IPSET_V6"; do
        local count
        count=$(ipset list "$set_name" 2>/dev/null | grep -c '^[0-9a-f:]' || echo 0)
        echo "  $set_name: $count entries"
    done
}

cmd_validate() {
    log_info "Validating policy.json..."

    if [[ ! -f "$POLICY_FILE" ]]; then
        log_error "File not found: $POLICY_FILE"
        exit 1
    fi

    if ! jq empty "$POLICY_FILE" 2>/dev/null; then
        log_error "Invalid JSON"
        exit 1
    fi

    local errors=0
    local total=0

    jq -r '.clients // {} | to_entries[] | "\(.key)|\(.value.access // [] | join(","))"' "$POLICY_FILE" | \
    while IFS="|" read -r ip access_str; do
        (( total++ ))

        if ! validate_cidr "$ip"; then
            log_error "Invalid client IP: $ip"
            (( errors++ )) || true
        fi

        if [[ -n "$access_str" ]]; then
            IFS=',' read -ra targets <<< "$access_str"
            for target in "${targets[@]}"; do
                if ! validate_cidr "$target"; then
                    log_error "Invalid access target for $ip: $target"
                    (( errors++ )) || true
                fi
            done
        fi
    done

    if (( errors > 0 )); then
        log_error "Validation failed: $errors errors"
        exit 1
    else
        log_info "Validation passed: $total clients, 0 errors"
    fi
}

# === DISPATCH ===
case "${1:-help}" in
    status)   cmd_status ;;
    policy)   cmd_policy ;;
    rules)    cmd_rules ;;
    ipset)    cmd_ipset ;;
    log)      cmd_log ;;
    reload)   cmd_reload ;;
    backup)   cmd_backup ;;
    stats)    cmd_stats ;;
    validate) cmd_validate ;;
    help|*)   usage ;;
esac