#!/usr/bin/env bash

# VPN connections, via NetworkManager.
#
# Quickshell.Networking covers Wi-Fi and wired devices but has no surface for
# VPN or WireGuard connections at all, so this is the one sanctioned exception
# to the no-nmcli rule stated in services/Connectivity.qml: there is nothing
# else to talk to. The moment Quickshell grows VPN support, this helper is what
# gets deleted.
#
# Usage:
#   panama-vpn list        -> {"connections":[{"name","uuid","kind","active","timestamp"}],"error":""}
#   panama-vpn up <uuid>
#   panama-vpn down <uuid>
#
# `up` is bounded and self-cleaning: a VPN whose server is unreachable is
# exactly the case this exists for, and NetworkManager's default is to keep
# trying for 90 seconds while every packet on a full-tunnel profile goes into
# the void. Waiting a bounded time and deactivating on failure is what makes
# the quick-settings toggle safe to press on a broken profile.

set -uo pipefail

emit_error() {
    printf '{"connections":[],"error":%s}\n' "$(jq -Rn --arg e "$1" '$e')"
    exit 0
}

command -v nmcli >/dev/null 2>&1 || emit_error 'nmcli is not available'

cmd_list() {
    local rows active
    # TIMESTAMP is when the connection last activated successfully; the toggle
    # uses it to pick the profile the person most recently used.
    rows="$(nmcli -t -f NAME,UUID,TYPE,TIMESTAMP connection show 2>/dev/null)" \
        || emit_error 'NetworkManager did not answer'
    active="$(nmcli -t -f UUID connection show --active 2>/dev/null)" || active=""

    # Fields are parsed from the end because NAME may contain escaped colons;
    # UUID, TYPE and TIMESTAMP never do.
    awk -F: -v active="$active" '
        $(NF-1) == "wireguard" || $(NF-1) == "vpn" {
            timestamp = $NF; kind = $(NF-1); uuid = $(NF-2);
            name = $1;
            for (i = 2; i <= NF-3; i++) name = name FS $i;
            gsub(/\\:/, ":", name);
            is_active = index(active, uuid) > 0 ? "true" : "false";
            printf "%s\t%s\t%s\t%s\t%s\n", name, uuid, kind, is_active, timestamp;
        }
    ' <<<"$rows" | jq -Rn '
        {"connections": [inputs | split("\t")
            | {name: .[0], uuid: .[1], kind: .[2],
               active: (.[3] == "true"), timestamp: (.[4] | tonumber? // 0)}],
         "error": ""}'
}

cmd_up() {
    local uuid="$1" output
    if ! output="$(nmcli -w 25 connection up uuid "$uuid" 2>&1)"; then
        # Roll the half-activated connection back down so a dead server does
        # not leave the machine with a black-hole default route.
        nmcli connection down uuid "$uuid" >/dev/null 2>&1 || true
        printf '%s\n' "$output" >&2
        exit 1
    fi
}

cmd_down() {
    local uuid="$1" output
    if ! output="$(nmcli connection down uuid "$uuid" 2>&1)"; then
        printf '%s\n' "$output" >&2
        exit 1
    fi
}

case "${1:-}" in
    list) cmd_list ;;
    up)   [[ -n "${2:-}" ]] || { echo 'panama-vpn up needs a connection uuid' >&2; exit 1; }
          cmd_up "$2" ;;
    down) [[ -n "${2:-}" ]] || { echo 'panama-vpn down needs a connection uuid' >&2; exit 1; }
          cmd_down "$2" ;;
    *)    echo 'usage: panama-vpn list | up <uuid> | down <uuid>' >&2; exit 1 ;;
esac
