#!/usr/bin/env bash

# nmcli WireGuard abstraction layer for use with my waybar module and rofi custom menu script
#
# requires nmcli on your path
# install to the same directory as wireguard-rofi.sh
#
# usage: ./wireguard.sh [menu|toggle NAME]
# no argument:   print current connections
# json:          print waybar-ready json output
# menu:          print all connections
# toggle NAME:   toggle connection NAME

if ! command -v nmcli >/dev/null 2>&1; then
    echo "err: nmcli not found"
    exit 1
fi

nargs=$#
showmenu="no"
dotoggle="no"
printjson="no"
if [[ $nargs == 1 ]]; then
    if [[ $1 == "menu" ]]; then
        showmenu="yes"
    elif [[ $1 == "json" ]]; then
        printjson="yes"
    fi
elif [[ $nargs == 2 ]]; then
    if [[ $1 == "toggle" ]]; then
        dotoggle="yes"
        conn="$2"
    fi
fi

nmclicmd="nmcli connection"
wgconns="$nmclicmd show"
wgactive="$wgconns --active"

connected=()
available=()

function print_as_json() {
    text="${1%% |*}" # prints out name of first vpn it finds
    alt="${1%%:*}"
    tooltip="${1}"
    [ -n "$1" ] && class="connected" || class="disconnected"
    printf "{\"text\": \"%s\", \"alt\": \"%s\", \"tooltip\": \"%s\", \"class\": \"%s\"}" \
        "$text" \
        "$alt" \
        "$tooltip" \
        "$class"
}

function get_conns {
    while read -r name _ type device; do
        if [[ $type != "wireguard" && ($type != "tun" || $device != "proton0") ]]; then
            continue
        fi

        if [[ $device != "--" ]]; then
            while read -r key value; do
                if [[ $key != "ipv4.addresses:" ]]; then
                    continue
                fi
                connected+=("$name: $value")
            done < <($wgconns "$name")
        else
            available+=("$name")
        fi
    done < <($1)
}

function get_pia {
    if ! command -v piactl >/dev/null 2>&1; then
        return 1
    fi

    status=$(piactl get connectionstate)
    if [[ $status = "Connected" ]]; then
        connected+=("pia: $(piactl get vpnip)")
    fi
}

function get_netbird {
    if ! command -v netbird >/dev/null 2>&1 || [ "$(systemctl is-active netbird)" == "inactive" ]; then
        return 1
    fi

    status="$(netbird status)"

    if echo "$status" | grep -q "^Signal: Connected"; then
        connected+=("netbird:$(echo "$status" | grep "^NetBird IP:" | cut -d':' -f2)")
    fi
}

function print_conns {
    local first="yes"
    local array_print="$1[@]"
    local array_print=("${!array_print}")
    if [[ $2 == "list" ]]; then
        for c in "${array_print[@]}"; do
            output="$1: $c"
        done
    else
        output=""
        for c in "${array_print[@]}"; do
            if [[ "$first" != "yes" ]]; then
                output+=" | "
            fi
            output+="$c"
            first="no"
        done
    fi
    if [[ "$printjson" == "yes" ]]; then
        print_as_json "$output"
    else
        echo "$output"
    fi
}

function array_contains {
    local array_has="$1[@]"
    local array_has=("${!array_has}")
    local element="$2"
    for e in "${array_has[@]}"; do
        if [[ "$e" == *"$element"* ]]; then
            echo "yes"
            return
        fi
    done
    echo "no"
}

if [[ $nargs == 0 ]] || [[ $printjson = "yes" ]]; then
    get_pia
    get_netbird
    get_conns "$wgactive"
    print_conns connected

elif [[ $showmenu == "yes" ]]; then
    get_pia
    get_netbird
    get_conns "$wgconns"
    print_conns connected "list"
    print_conns available "list"

elif [[ $dotoggle == "yes" ]]; then
    get_pia
    get_netbird
    get_conns "$wgconns"

    if [[ "$(array_contains connected "$conn")" == "yes" ]]; then
        $nmclicmd down "$conn"
    elif [[ "$(array_contains available "$conn")" == "yes" ]]; then
        $nmclicmd up "$conn"
    else
        echo "err: connection not found"
        exit 1
    fi

else
    echo "err: wrong args"
    exit 1
fi