#!/usr/bin/env bash
# wg_status — Parsea `wg show <iface> dump` a JSON estructurado con peers,
# handshake age, status (online/stale/never), bytes rx/tx.
# Resuelve device_id desde comentarios # DeviceID:<id> en wg0.conf.
#
# Usage:
#   wg_status [interface_name]   # default: wg0
#
# Env:
#   WG_FAKE_DUMP=<path>          # lee dump de archivo en vez de llamar wg show (para tests)

wg_status() {
    local iface="${1:-wg0}"
    local conf="${WG_FAKE_CONF:-/etc/wireguard/${iface}.conf}"
    local now
    now=$(date +%s)

    # --- obtener dump (real o fake) ---
    local dump
    if [[ -n "${WG_FAKE_DUMP:-}" ]]; then
        if [[ ! -f "$WG_FAKE_DUMP" ]]; then
            printf '{"error":"WG_FAKE_DUMP file not found: %s"}\n' "$WG_FAKE_DUMP"
            return 1
        fi
        dump=$(cat "$WG_FAKE_DUMP")
    else
        if ! command -v wg &>/dev/null; then
            printf '{"error":"wg command not found"}\n'
            return 1
        fi
        if ! dump=$(wg show "$iface" dump 2>&1); then
            if echo "$dump" | grep -qi "no such device\|does not exist\|unable to access interface"; then
                printf '{"error":"interface not found"}\n'
                return 1
            fi
            printf '{"error":"%s"}\n' "$(echo "$dump" | head -n1 | sed 's/"/\\"/g')"
            return 1
        fi
    fi

    # --- primera linea: info de la propia interface ---
    # formato: <private_key>\t<public_key>\t<listen_port>\t<fwmark>
    local iface_line
    iface_line=$(echo "$dump" | head -n1)

    local iface_pubkey iface_port
    iface_pubkey=$(echo "$iface_line" | awk -F'\t' '{print $2}')
    iface_port=$(echo "$iface_line"   | awk -F'\t' '{print $3}')

    # --- leer DeviceID map desde wg0.conf ---
    # Busca patron:
    #   # DeviceID:<id>
    #   [Peer]
    #   PublicKey = <pk>
    # Producimos pares "pk\tdevice_id" en un archivo temporal para lookup via awk
    local device_map
    device_map=$(awk '
        /^#[[:space:]]*DeviceID:/ {
            split($0, a, "DeviceID:")
            did = a[2]
            gsub(/^[[:space:]]+|[[:space:]]+$/, "", did)
            pending_did = did
        }
        /^\[Peer\]/ {
            in_peer = 1
        }
        in_peer && /^PublicKey[[:space:]]*=/ {
            pk = $0
            sub(/^PublicKey[[:space:]]*=[[:space:]]*/, "", pk)
            gsub(/^[[:space:]]+|[[:space:]]+$/, "", pk)
            if (pending_did != "") {
                print pk "\t" pending_did
                pending_did = ""
            }
            in_peer = 0
        }
    ' "$conf" 2>/dev/null)

    # --- parsear peers (lineas 2..N del dump) ---
    # formato peer: <public_key>\t<preshared_key>\t<endpoint>\t<allowed_ips>\t<latest_handshake>\t<rx_bytes>\t<tx_bytes>\t<persistent_keepalive>
    local peers_json
    peers_json=$(echo "$dump" | tail -n +2 | awk -v now="$now" -v dmap="$device_map" '
    BEGIN {
        # construir lookup device_id
        n = split(dmap, lines, "\n")
        for (i = 1; i <= n; i++) {
            if (lines[i] != "") {
                split(lines[i], parts, "\t")
                pk_to_did[parts[1]] = parts[2]
            }
        }
        first = 1
        printf "["
    }
    NF >= 7 {
        pk       = $1
        endpoint = $3
        allowed  = $4
        hs       = $5 + 0
        rx       = $6 + 0
        tx       = $7 + 0
        ka       = $8

        # device_id lookup
        did = (pk in pk_to_did) ? pk_to_did[pk] : ""

        # handshake age y status
        if (hs == 0) {
            ago = 0
            status = "never"
        } else {
            ago = now - hs
            if (ago < 180)   status = "online"
            else if (ago < 86400) status = "stale"
            else              status = "stale"
        }

        # persistent_keepalive
        ka_val = (ka == "off" || ka == "") ? 0 : ka + 0

        # endpoint null si "(none)"
        ep_val = (endpoint == "(none)") ? "null" : "\"" endpoint "\""

        # allowed_ips array
        n_ips = split(allowed, ips_arr, ",")
        ips_json = "["
        for (j = 1; j <= n_ips; j++) {
            gsub(/^[[:space:]]+|[[:space:]]+$/, "", ips_arr[j])
            ips_json = ips_json "\"" ips_arr[j] "\""
            if (j < n_ips) ips_json = ips_json ","
        }
        ips_json = ips_json "]"

        if (!first) printf ","
        first = 0

        printf "{"
        printf "\"public_key\":\"%s\"",        pk
        printf ",\"device_id\":\"%s\"",         did
        printf ",\"endpoint\":%s",              ep_val
        printf ",\"allowed_ips\":%s",           ips_json
        printf ",\"latest_handshake_unix\":%d", hs
        printf ",\"latest_handshake_ago_s\":%d",ago
        printf ",\"rx_bytes\":%d",              rx
        printf ",\"tx_bytes\":%d",              tx
        printf ",\"persistent_keepalive\":%d",  ka_val
        printf ",\"status\":\"%s\"",            status
        printf "}"
    }
    END { printf "]" }
    ' FS='\t')

    # --- output final ---
    printf '{"interface":"%s","public_key":"%s","listen_port":%s,"peers":%s}\n' \
        "$iface" "$iface_pubkey" "$iface_port" "$peers_json"
}

# Permitir invocacion directa: bash wg_status.sh [iface]
if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then
    wg_status "$@"
fi
