#!/usr/bin/env bash
###
### arching-kaos-tools
### Tools to interact and build an Arching Kaos Infochain
### Copyright (C) 2021 - 2025  kaotisk
###
### This program is free software: you can redistribute it and/or modify
### it under the terms of the GNU General Public License as published by
### the Free Software Foundation, either version 3 of the License, or
### (at your option) any later version.
###
### This program is distributed in the hope that it will be useful,
### but WITHOUT ANY WARRANTY; without even the implied warranty of
### MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
### GNU General Public License for more details.
###
### You should have received a copy of the GNU General Public License
### along with this program.  If not, see <http://www.gnu.org/licenses/>.
###
source $AK_LIBDIR/_ak_log
source $AK_LIBDIR/_ak_ipfs

cjdnstoolspath="$HOME/cjdns/tools"

_ak_network_stellar_show_ids(){
    i=0
    flag=0
    while [ "$i" -lt "$limit" ]
    do
        test="$(cat $tempfile | jq '.["_embedded"]' | jq .'["records"]' | jq '.['$i']' | jq '.["id"]' | sed -e 's/\"//g')"
        while IFS="" read -r p || [ -n "$p" ]
        do
            if [ "$p" == "$test" ]
            then
                flag=1
                break
            fi
        done < $stellarParticipants
        if [ "$flag" == "0" ]
        then
            echo $test >> $stellarParticipants
            _ak_log_info "Added $test"
        else
            _ak_log_info "$test already there. Skipping..."
        fi
        i="$(expr $i + 1)"
    done
}

_ak_network_stellar_get_next_page(){
    cat $tempfile | jq -r '._links.next.href'
    # cat $tempfile | grep -1 next | grep https | sed -e 's/^.*https/https/' | sed -e 's/\u0026/\&/g; s/"//g; s/\\//g'
}

_ak_network_stellar_proceed_to_next_page(){
    nextURL="$(_ak_network_stellar_get_next_page)"
    curl \
        --connect-timeout 3 \
        "$nextURL" > $tempfile 2>/dev/null
}

_ak_network_stellar_get_asset_holders(){
    if [ ! -z $1 ] && [ $1 -gt 0 ] && [ -n "$1" ]
    then
        limit=$1
    else
        limit=3
    fi
    curl \
        --connect-timeout 3 \
        https://horizon.stellar.org/accounts\?asset\=ARCHINGKAOS:GB4QVKD6NW3CSNO5TNPARAWNPPXOPSSTKB35XCWB7PUNBIQTK3DVELB2\&limit\=$limit > $tempfile 2>/dev/null
}

_ak_network_scan_stellar(){
    tempdir=`_ak_make_temp_directory`
    tempfile="$tempdir/tmp"
    tempaddr="$tempdir/tmpaddr"

    stellarParticipants="$AK_WORKDIR/stellar-network-participants"
    stellarParticipantsOnline="$AK_WORKDIR/stellar-network-participants-online"
    if [ ! -f "$stellarParticipants" ]
    then
        touch $stellarParticipants
    fi
    if [ ! -f "$stellarParticipantsOnline" ]
    then
        touch $stellarParticipantsOnline
    fi
    if [ ! -f "$tempdir" ]
    then
        touch $tempdir
    fi
    if [ ! -f "$tempaddr" ]
    then
        touch $tempaddr
    fi

    if [ ! -z $1 ] && [ "$1" == "-l" ] && [ ! -z $2 ]
    then
        limit=$2
    else
        limit=3
    fi

    _ak_network_stellar_get_asset_holders $limit
    while [ "$(tail -1 $stellarParticipants)" != "null" ]
    do
        _ak_network_stellar_show_ids
        _ak_network_stellar_proceed_to_next_page
    done

    grep G $stellarParticipants > $tempaddr
    cat $tempaddr > $stellarParticipants
    rm $tempfile $tempaddr
    while IFS="" read -r p || [ -n "$p" ]
    do
        flag=0
        test="$(curl \
            --connect-timeout 3 \
            https://horizon.stellar.org/accounts/$p/data/config 2>/dev/null | grep value)"
        if [ $? -eq 0 ]
        then
            addressValuePair="$(printf '%s %s\n' "$p" "$(echo $test | sed -e 's/^.*: "//g; s/"//g' | base64 -d)")"
            while IFS="" read -r x || [ -n "$x" ]
            do
                if [ "$x" == "$addressValuePair" ]
                then
                    flag=1
                    break
                fi
            done < $stellarParticipantsOnline
            if [ "$flag" == "0" ]
            then
                echo $addressValuePair >> $stellarParticipantsOnline
                _ak_log_info "$p is configured with  $(echo $addressValuePair | awk '{ print $2 }'). Added to online participants."
            else
                _ak_log_info "$(echo $addressValuePair | awk '{ print $2 }') is already in $stellarParticipantsOnline. Skipping..."
            fi
        fi
    done < $stellarParticipants
}

_ak_network_scan_ipfs(){
    _ak_not_implemented _ak_network_scan_ipfs
}

_ak_network_scan_cjdns_dump(){
    #
    # Needs CJDNS tools in your PATH
    #
    # Ref: https://github.com/cjdelisle/cjdns
    #
    which dumpLinks > /dev/null 2>&1
    if [ $? -ne 0 ]
    then
        echo "You need to install cjdns and cjdns-tools ... falling back to -w"
        _ak_network_scan_cjdns_full
        exit 0
    fi
    which publictoip6 > /dev/null 2>&1
    if [ $? -ne 0 ]
    then
        echo "You need to install cjdns and cjdns-tools ... falling back to -w"
        _ak_network_scan_cjdns_full
        exit 0
    fi
    TEMPDIR="$(_ak_make_temp_directory)"
    AK_ZPEERSFILE="$HOME/.arching-kaos/peersFile"
    cd $TEMPDIR
    max="$(dumpLinks |wc -l)"
    dumpLinks \
        | cut -d ' ' -f 2,4 \
        | sed 's/ /\n/g' \
        | sort \
        | uniq \
        | grep -v `dumpLinks | grep '0000\.0000\.0000\.0001' \
        | cut -d ' ' -f 2` > dumpedLinks
    counter=0
    count=0
    max="$(cat dumpedLinks| wc -l)"
    printf '[' > walk.aknet
    cat dumpedLinks \
        | while read -r ip || [ -n "$ip" ]
    do
        count="$(( $count + 1 ))"
        _ak_log_debug "Scanning [${count}/${max}] $(publictoip6 $ip)..."
        node_info="$(curl \
            --connect-timeout 3 \
            -A 'akd/0.1.0; https://github.com/arching-kaos' \
            "http://[$(publictoip6 $ip)]:8610/v0/node_info" 2>/dev/null | jq -c -M)"
        if [ $? -eq 0 ] && [ $(echo -n "$node_info" | wc -c) -gt 0 ]
        then
            if [ "$counter" -ne "0" ]
            then
                printf ',' >> walk.aknet
            fi
            if [ ! -n "$node_info" ]
            then
                node_info="null"
            fi
            printf '{"cjdns":{"public_key":"%s","ip":"%s"},"node_info":%s}' \
                "$ip" "`publictoip6 $ip`" "$node_info" >> walk.aknet
            counter="`expr $counter + 1`"
        fi
    done
    printf ']' >> walk.aknet
    mv walk.aknet $AK_ZPEERSFILE.hyperboria
    rm -rf $TEMPDIR
}

_ak_network_scan_cjdns_full(){
    # This scan is using HIA resources to scan the whole cjdns network for peers
    #
    # Ref:
    #  - https://github.com/ircerr/hia/
    #  - http://hia.cjdns.ca/watchlist/c/walk.pubkey
    #  - http://hia.cjdns.ca/watchlist/hia.iplist
    #
    TEMPDIR="$(_ak_make_temp_directory)"
    cd $TEMPDIR
    pubkeys_ips="$TEMPDIR/pi"
    online_ips="$TEMPDIR/oi"
    filtered_online_pubkeys_ips="$TEMPDIR/fopi"
    curl -s \
        --connect-timeout 5 \
        "http://hia.cjdns.ca/watchlist/c/walk.pubkey" > $pubkeys_ips
    if [ $? -ne 0 ]
    then
        _ak_log_error "Couldn't fetch DB from HIA"
        exit 1
    fi
    curl -s \
        --connect-timeout 5 \
        "http://hia.cjdns.ca/watchlist/hia.iplist" > $online_ips
    if [ $? -ne 0 ]
    then
        _ak_log_error "Couldn't fetch iplist from HIA"
        exit 1
    fi
    cat $online_ips | while read line
    do
        grep -F "$line" $pubkeys_ips >> $filtered_online_pubkeys_ips
    done
    counter=0
    count=0
    max="$(cat $filtered_online_pubkeys_ips|wc -l)"
    printf '[' > walk.aknet
    cat $filtered_online_pubkeys_ips \
        | sort \
        | uniq \
        | while read -r pkey ip || [ -n "$ip" ]
    do
        count="$(( $count + 1 ))"
        _ak_log_debug "Scanning [${count}/${max}] $ip..."
        node_info="$(curl \
            --connect-timeout 3 \
            -A 'akd/0.1.0; https://github.com/arching-kaos' \
            "http://[$ip]:8610/v0/node_info" 2>/dev/null | jq -c -M)"
        if [ $? -eq 0 ] && [ $(echo -n "$node_info" | wc -c) -gt 0 ]
        then
            if [ "$counter" -ne "0" ]
            then
                printf ',' >> walk.aknet
            fi
            if [ ! -n "$node_info" ]
            then
                node_info="null"
            fi
            printf '{"cjdns":{"public_key":"%s","ip":"%s"},"node_info":%s}' \
                "$pkey" "$ip" "$node_info" >> walk.aknet
            counter="`expr $counter + 1`"
        fi
    done
    printf ']' >> walk.aknet
    mv walk.aknet $AK_ZPEERSFILE.hyperboria
    rm -rf $TEMPDIR
}

_ak_network_scan_yggdrasil_full(){
    # This scan is using HIA resources to scan the whole cjdns network for peers
    #
    # Ref:
    #  - https://github.com/ircerr/yia/
    #  - http://yia.yggdrasil-network.ca/watchlist/yia.iplist
    #
    TEMPDIR="$(_ak_make_temp_directory)"
    cd $TEMPDIR
    pubkeys_ips="$TEMPDIR/pi"
    online_ips="$TEMPDIR/oi"
    filtered_online_pubkeys_ips="$TEMPDIR/fopi"
    curl -s \
        --connect-timeout 5 \
        "http://yia.yggdrasil-network.ca/crawler/crawler-yia.pubkey" > $pubkeys_ips
    if [ $? -ne 0 ]
    then
        _ak_log_error "Couldn't fetch DB from YIA"
        exit 1
    fi
    counter=0
    count=0
    max="$(cat $pubkeys_ips|wc -l)"
    printf '[' > walk.aknet
    cat $pubkeys_ips \
        | sort \
        | uniq \
        | while read -r pkey ip || [ -n "$ip" ]
    do
        count="$(( $count + 1 ))"
        _ak_log_debug "Scanning [${count}/${max}] $ip..."
        node_info="$(curl \
            --connect-timeout 3 \
            -A 'akd/0.1.0; https://github.com/arching-kaos' \
            "http://[$ip]:8610/v0/node_info" 2>/dev/null | jq -c -M)"
        if [ $? -eq 0 ] && [ $(echo -n "$node_info" | wc -c) -gt 0 ]
        then
            if [ "$counter" -ne "0" ]
            then
                printf ',' >> walk.aknet
            fi
            if [ ! -n "$node_info" ]
            then
                node_info="null"
            fi
            printf '{"yggdrasil":{"public_key":"%s","ip":"%s"},"node_info":%s}' \
                "$pkey" "$ip" "$node_info" >> walk.aknet
            counter="`expr $counter + 1`"
        fi
    done
    printf ']' >> walk.aknet
    mv walk.aknet $AK_ZPEERSFILE.yggdrasil
    rm -rf $TEMPDIR
}

_ak_network_scan_cjdns(){
    if [ ! -z $1 ] && [ -n "$1" ]
    then
        case $1 in
            -w | --whole) _ak_network_scan_cjdns_full; exit;;
            -d | --dump) _ak_network_scan_cjdns_dump; exit;;
            *) _ak_network_scan_cjdns_dump; exit;;
        esac
    else
        _ak_network_scan_cjdns_dump
    fi
}

_ak_network_scan_yggdrasil(){
    if [ ! -z $1 ] && [ -n "$1" ]
    then
        case $1 in
            *) _ak_network_scan_yggdrasil_full; exit;;
        esac
    else
        _ak_network_scan_yggdrasil_full
    fi
}

_ak_network_scan(){
    if [ ! -z $1 ] && [ -n "$1" ]
    then
        case $1 in
            stellar) _ak_network_scan_stellar; exit;;
            ipfs) _ak_network_scan_ipfs; exit;;
            cjdns) shift; _ak_network_scan_cjdns $1; exit;;
            yggdrasil) shift; _ak_network_scan_yggdrasil $1; exit;;
            * ) _ak_log_error "Unknown network $1";exit 1;;
        esac
    else
        _ak_network_scan_stellar
        _ak_network_scan_ipfs
        _ak_network_scan_cjdns
        _ak_network_scan_yggdrasil
    fi
}
_ak_network_show_peers_stellar(){
    stellarParticipants="$AK_WORKDIR/stellar-network-participants"
    stellarParticipantsOnline="$AK_WORKDIR/stellar-network-participants-online"
    if [ -f "${stellarParticipantsOnline}" ]
    then
    (
        printf '['
        counter=0
        cat $stellarParticipantsOnline | while read stellarAddress akConfig
        do
            if [ $counter -ne 0 ]
            then
                printf ','
            fi
            counter=$(( $counter + 1 ))
            akConfigHash="$(_ak_ipfs_name_resolve /ipns/$akConfig|cut -d '/' -f 3)"
            printf '{"stellar":{"address":"%s","config":"%s"},"node_info":%s}' \
                "$stellarAddress" \
                "$akConfig" \
                "$(_ak_ipfs_cat $akConfigHash)"
        done
        printf ']'
        ) | sed -e 's/\[]//g' #| jq
    else
        _ak_log_debug "No stellar peers found"
    fi
}

_ak_network_show_peers_cjdns(){
    if [ -f $AK_ZPEERSFILE.hyperboria ]
    then
        cat $AK_ZPEERSFILE.hyperboria | jq
    else
        _ak_log_debug "No hyperboria peers found"
    fi
}

_ak_network_show_peers_yggdrasil(){
    if [ -f $AK_ZPEERSFILE.yggdrasil ]
    then
        cat $AK_ZPEERSFILE.yggdrasil | jq
    else
        _ak_log_debug "No yggdrasil peers found"
    fi
}

_ak_network_show_peers_ipfs(){
    _ak_not_implemented _ak_network_show_peers_ipfs
}

_ak_network_show_peers(){
    if [ ! -z $1 ] && [ -n "$1" ]
    then
        case $1 in
            stellar) _ak_network_show_peers_stellar; exit;;
            cjdns) _ak_network_show_peers_cjdns; exit;;
            ipfs) _ak_network_show_peers_ipfs; exit;;
            yggdrasil) _ak_network_show_peers_yggdrasil; exit;;
            * ) _ak_log_error "Unknown network $1";exit 1;;
        esac
    else
        (_ak_network_show_peers_stellar; _ak_network_show_peers_cjdns; _ak_network_show_peers_yggdrasil; _ak_network_show_peers_ipfs) | \
            jq -j | \
            sed -e 's/]\[/,/g' | \
            jq
    fi
}

_ak_network_ipfs_connect_bootstrap(){
    curl -s "https://arching-kaos.net/files/ak_ipfs_bootstrap_peers" | while read peer
    do
        _ak_ipfs_swarm_connect "$peer"
    done
}

_ak_network_cjdns_connect(){
    if [ ! -z $1 ] && [ -n "$1" ] && [ -f $1 ]
    then
        peersfile="$1"
    else
        peersfile="$(_ak_make_temp_file)"
        tmpon="yes"
        curl -s "https://arching-kaos.net/files/ak_cjdns_bootstrap_peers" > $peersfile
    fi
    totalpeers="$(jq '. | length' < $peersfile)"
    number="0"
    interface="0"
    while [ $number -lt $totalpeers ]
    do
        address="$(jq -r '.['$number'].address' < $peersfile)"
        login="$(jq -r '.['$number'].login' < $peersfile)"
        password="$(jq -r '.['$number'].password' < $peersfile)"
        publicKey="$(jq -r '.['$number'].publicKey' < $peersfile)"
        peerName="$(jq -r '.['$number'].peerName' < $peersfile)"
        if [ $(echo $address | grep '\[') ]
        then
            interface="1"
        else
            interface="0"
        fi
        $cjdnstoolspath/cexec 'UDPInterface_beginConnection("'$publicKey'", "'$address'", "'$peerName'", "'$password'", "'$login'", '$interface')'
        number="$(( $number + 1 ))"
    done
    if [ "$tmpon" == "yes" ]
    then
        rm $peersfile
    fi
}

_ak_network_cjdns_connect_bootstrap(){
    _ak_network_cjdns_connect
}

_ak_network_connect(){
    if [ ! -z $1 ] && [ -n "$1" ]
    then
        case $1 in
            cjdns) _ak_network_cjdns_connect_bootstrap; exit;;
            ipfs) _ak_network_ipfs_connect_bootstrap; exit;;
            * ) _ak_log_error "Unknown network $1";exit 1;;
        esac
    else
        _ak_network_cjdns_connect_bootstrap
        _ak_network_ipfs_connect_bootstrap
    fi
}