#!/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
AK_IPFS_REPO="$AK_WORKDIR/ipfsrepo"
AK_IPFS_ARTIFACTS="$AK_WORKDIR/ipfs_artifacts"
if [ ! -d $AK_IPFS_ARTIFACTS ]
then
    mkdir -p $AK_IPFS_ARTIFACTS
fi

function _ak_ipfs(){
    export IPFS_PATH=$AK_IPFS_REPO; kubo $*
    if [ $? -ne 0 ]
    then
        _ak_log_error "Some error occured when running: kubo $*"
        #exit 1
    fi
}

function _ak_ipfs_daemon(){
    _ak_ipfs daemon --routing=dht --migrate &
    printf '%s' "$!" > $AK_WORKDIR/akipfsd.pid
}

function _ak_ipfs_swarm_connect(){
    if [ ! -z $1 ] && [ -n "$1" ]
    then
        _ak_ipfs swarm connect "$1"
    else
        ak_log_error "No peer address provided"
    fi
}

function _ak_ipfs_get_peers(){
    _ak_ipfs swarm peers 1> /dev/null 2>&1
    if [ $? -eq 0 ]
    then
        _ak_ipfs swarm peers > $AK_WORKDIR/peers.ipfs
    fi
}

function _ak_ipfs_scanner(){
    peersIPFSfile="$AK_WORKDIR/peers.ipfs"
    ak_peersIPFSfile="$AK_WORKDIR/ipfs.peers.akn"
    if [ ! -f $peersIPFSfile ]
    then
        _ak_ipfs_get_peers
        if [ $? -ne 0 ]
        then
            _ak_log_error "Failed to get IPFS peers"
            exit 1
        fi
    fi
    counter=0
    printf '[' > walk.aknet
    cat $peersIPFSfile \
        | cut -d '/' -f 2,3,7 \
        | sort \
        | uniq \
        | while read -r line || [ -n "$line" ]
    do
        protocol="`printf '%s' "$line" | cut -d '/' -f 1`"
        ip="`printf '%s' "$line" | cut -d '/' -f 2`"
        ipfsId="`printf '%s' "$line" | cut -d '/' -f 3`"
        if [ "$protocol" == "ip6" ]
        then
            url="http://[$ip]:8610/v0/node_info"
        else
            url="http://$ip:8610/v0/node_info"
        fi
        node_info="`curl \
            --connect-timeout 3 \
            -A 'akd/0.1.0; https://github.com/arching-kaos' \
            "$url" 2>/dev/null`"
        if [ $? -eq 0 ]
        then
            if [ "$counter" -ne "0" ]
            then
                printf ',' >> walk.aknet
            fi
            printf '{"ipfsPublicKey":"%s","ip":"%s","node_info":%s}' \
                "$ipfsId" "$ip" "$node_info" >> walk.aknet
            counter="`expr $counter + 1`"
        fi
    done
    printf ']' >> walk.aknet
    mv walk.aknet $ak_peersIPFSfile
}

function _ak_ipfs_add(){
    if [ -z $1 ] || [ ! -n "$1" ]
    then
        _ak_log_error "no argument given"
        exit 1
    fi
    # Receives a file
    # Returns a hash
    _ak_ipfs add -Qr "$1"
    if [ $? -ne 0 ]
    then
        _ak_log_error "Failed to add $1"
        exit 1
    fi
}

function _ak_ipfs_block_stat(){
    if [ -z $1 ] || [ ! -n "$1" ]
    then
        _ak_log_error "no argument given"
        exit 1
    fi
    _ak_ipfs block stat "$1"
    if [ $? -ne 0 ]
    then
        _ak_log_error "Failed to retrieve stat of block $1"
        exit 1
    fi
}

function _ak_ipfs_files_cp(){
    if [ -z $1 ] || [ ! -n "$1" ]
    then
        _ak_log_error "Source: No argument given"
        exit 1
    fi
    if [ -z $2 ] || [ ! -n "$2" ]
    then
        _ak_log_error "Destination: No argument given"
        exit 1
    fi
    _ak_ipfs files cp "$1" "$2"
    if [ $? -ne 0 ]
    then
        _ak_log_error "Failed to copy $1 to $2"
        exit 1
    fi
}

function _ak_ipfs_files_ls(){
    _ak_ipfs files ls "$1"
    if [ $? -ne 0 ]
    then
        _ak_log_error "Failed to list $1"
        exit 1
    fi
}

function _ak_ipfs_files_mkdir(){
    if [ -z $1 ] || [ ! -n "$1" ]
    then
        _ak_log_error "No argument given"
        exit 1
    fi
    _ak_ipfs files mkdir "$1"
    if [ $? -ne 0 ]
    then
        _ak_log_error "Failed to mkdir $1"
        exit 1
    fi
}

function _ak_ipfs_files_mv(){
    if [ -z $1 ] || [ ! -n "$1" ]
    then
        _ak_log_error "No argument given"
        exit 1
    fi
    if [ -z $2 ] || [ ! -n "$2" ]
    then
        _ak_log_error "No argument given"
        exit 1
    fi
    _ak_ipfs files mv "$1" "$2"
    if [ $? -ne 0 ]
    then
        _ak_log_error "Failed to move $1 to $2"
        exit 1
    fi
}

function _ak_ipfs_files_rm(){
    if [ -z $1 ] || [ ! -n "$1" ]
    then
        _ak_log_error "No argument given"
        exit 1
    fi
    _ak_ipfs files rm "$1"
    if [ $? -ne 0 ]
    then
        _ak_log_error "Failed to remove $1"
        exit 1
    fi
}

function _ak_ipfs_files_stat(){
    if [ -z $1 ] || [ ! -n "$1" ]
    then
        _ak_log_error "No argument given"
        exit 1
    fi
    _ak_ipfs files stat "$1"
    if [ $? -ne 0 ]
    then
        _ak_log_error "Failed to get file's stats $1"
        exit 1
    fi
}

function _ak_ipfs_get(){
    if [ -z $1 ] || [ ! -n "$1" ]
    then
        _ak_log_error "No argument given"
        exit 1
    fi
    if [ ! -f $AK_IPFS_ARTIFACTS/$1 ]
    then
        wam="$(pwd)"
        cd $AK_IPFS_ARTIFACTS
        _ak_ipfs --timeout=10s get "$1" > /dev/null 2>&1
        if [ $? -ne 0 ]
        then
            _ak_log_error "Failed to get $1"
            exit 1
        fi
        cd $wam
        ln -s $AK_IPFS_ARTIFACTS/$1 $1
    fi
}

function _ak_ipfs_cat(){
    if [ -z $1 ] || [ ! -n "$1" ]
    then
        _ak_log_error "no argument given"
        exit 1
    fi
    if [ ! -f $AK_IPFS_ARTIFACTS/$1 ]
    then
        _ak_ipfs_get $1
    fi
    cat $AK_IPFS_ARTIFACTS/$1
    if [ $? -ne 0 ]
    then
        _ak_log_error "Failed to cat $1"
        exit 1
    fi
}

function _ak_ipfs_key_gen(){
    if [ -z $1 ] || [ ! -n "$1" ]
    then
        _ak_log_error "No argument given"
        exit 1
    fi
    _ak_ipfs key gen "$1"
    if [ $? -ne 0 ]
    then
        _ak_log_error "Failed to generate key $1"
        exit 1
    fi
}

function _ak_ipfs_key_list(){
    _ak_ipfs key list
    if [ $? -ne 0 ]
    then
        _ak_log_error "Failed to get key list"
        exit 1
    fi
}

function _ak_ipfs_key_list_full(){
    _ak_ipfs key list -l
    if [ $? -ne 0 ]
    then
        _ak_log_error "Failed to get key list"
        exit 1
    fi
}

function _ak_ipfs_name_publish(){
    if [ -z $1 ] || [ ! -n "$1" ]
    then
        _ak_log_error "No argument given"
        exit 1
    fi
    if [ -z $2 ] || [ ! -n "$2" ]
    then
        _ak_log_error "No argument given"
        exit 1
    fi
    _ak_ipfs name publish --key="$1" "$2"
    if [ $? -ne 0 ]
    then
        _ak_log_error "Failed to get $1"
        exit 1
    fi
}

function _ak_ipfs_config_publish(){
    if [ -z $1 ] || [ ! -n "$1" ]
    then
        _ak_log_error "No argument given"
        exit 1
    fi
    _ak_ipfs_check_config_key
    _ak_ipfs_name_publish "ak-config" $1
}

function _ak_ipfs_name_resolve(){
    if [ -z $1 ] || [ ! -n "$1" ]
    then
        _ak_log_error "No argument given"
        exit 1
    fi
    _ak_ipfs name resolve "$1"
    if [ $? -ne 0 ]
    then
        _ak_log_error "Failed to resolve $1"
        exit 1
    fi
}

function _ak_ipfs_swarm_peers(){
    _ak_ipfs swarm peers
    if [ $? -ne 0 ]
    then
        _ak_log_error "Failed to get list of peers"
        exit 1
    fi
}

function _ak_ipfs_starter(){
    _ak_ipfs_daemon
    if [ $? -ne 0 ]
    then
        _ak_log_error "Failed to start IPFS daemon"
        exit 1
    fi
    akipfsdpid="$(cat $AK_WORKDIR/akipfsd.pid)"
    ps $akipfsdpid
    if [ $? -ne 0 ]
    then
        _ak_log_error "Failed to start IPFS daemon PID $akipfsdpid"
        exit 1
    fi
}

function _ak_ipns_resolve(){
    if [ ! -z $1 ]
    then
        rsld=$(_ak_ipfs_name_resolve $1)
        if [ $? -ne 0 ]
        then
            _ak_log_error "Failed to resolve $1"
            exit 1
        fi
        echo -n $rsld | sed -e 's/\/ipfs\///'
        _ak_log_info "Resolved $1 to $rsld"
    else
        _ak_log_error "No argument given to resolve"
        exit 1
    fi
}

function _ak_ipfs_check_config_key(){
    _ak_ipfs_key_list | grep ak-config > /dev/null
    if [ $? -ne 0 ]
    then
        _ak_log_warning "ak-config key is missing"
        _ak_ipfs_key_gen ak-config
        if [ $? -ne 0 ]
        then
            _ak_log_error "ak-config fails to create"
        else
            _ak_log_info "ak-config created"
        fi
    else
        _ak_log_info "ak-config is there"
    fi
}

function _ak_ipfs_check_zchain_key(){
    _ak_ipfs_key_list | grep zchain > /dev/null
    if [ $? -ne 0 ]
    then
        _ak_log_warning "zchain key is missing"
        _ak_ipfs_key_gen zchain > $ZCHAIN
        if [ $? -ne 0 ]
        then
            _ak_log_error "zchain fails to create"
        else
            _ak_log_info "zchain created"
        fi
    else
        _ak_log_info "zchain is there"
    fi
}

function _ak_ipfs_check_zarchive_dir(){
    _ak_ipfs_files_ls /zarchive > /dev/null
    if [ $? -ne 0 ]
    then
        _ak_log_error "/zarchive is missing"
    else
        _ak_log_info "/zarchive OK"
    fi
}

function _ak_ipfs_check_zlatest_file(){
    _ak_ipfs_files_ls /zlatest > /dev/null
    if [ $? -ne 0 ]
    then
        _ak_log_error "/zlatest is missing"
    else
        _ak_log_info "/zlatest is OK"
    fi
}

function _ak_ipfs_check(){
    _ak_ipfs_check_zarchive_dir
    _ak_ipfs_check_zlatest_file
    _ak_ipfs_check_config_key
    _ak_ipfs_check_zchain_key
}

function _ak_ipfs_init(){
    if [ ! -d $AK_IPFS_REPO ]
    then
        mkdir $AK_IPFS_REPO
        _ak_ipfs init
    fi
}

function _ak_ipfs_download(){
    _ak_log_info "Attempting to install IPFS..."
    IPFS_VERSION="$(curl \
        --connect-timeout 3 \
        -s https://dist.ipfs.tech/kubo/versions | tail -1)"
    if [ "$(uname -m)" == "amd64" ]
    then
        ARCH="amd64"
    elif [ "$(uname -m)" == "x86_64" ]
    then
        ARCH="amd64"
    elif [ "$(uname -m)" == "armv7l" ]
    then
        ARCH="arm"
    elif [ "$(uname -m)" == "aarch64" ]
    then
        ARCH="arm64"
    else
        echo "ERROR UNKNOWN ARCHITECTURE $(uname -m)"
        exit 1
    fi
    IPFS_TARGET_FILE="kubo_${IPFS_VERSION}_$(uname|tr "[:upper:]" "[:lower:]")-${ARCH}.tar.gz"
    _ak_log_info "Downloading kubo $IPFS_VERSION"
    if [ ! -f $AK_ARCHIVESDIR/$IPFS_TARGET_FILE ]
    then
        curl -s -o $AK_ARCHIVESDIR/$IPFS_TARGET_FILE https://dist.ipfs.tech/kubo/$IPFS_VERSION/$IPFS_TARGET_FILE
    else
        _ak_log_info "Already have the latest version"
        exit 0
    fi
}

function _ak_ipfs_cid_v0_check(){
    if [ -z $1 ] || [ ! -n "$1" ]
    then
        _ak_log_error "_ak_ipfs_cid_v0_check: No argument given"
        exit 1
    fi
    if [ $(echo -n $1 | wc -c) -ne 46 ]
    then
        _ak_log_error "_ak_ipfs_cid_v0_check: $1 IPFS CIDv0 length mismatch"
        exit 1
    fi
    echo $1 | grep -e 'Qm.\{44\}' > /dev/null
    if [ $? -ne 0 ]
    then
        _ak_log_error "_ak_ipfs_cid_v0_check: $1 is not an IPFS CIDv0 string"
        exit 1
    fi
    _ak_log_debug "_ak_ipfs_cid_v0_check: $1 provided is an IPFS CIDv0 string"
}

function _ak_ipfs_swarm_install(){
    SWARMSHA512SUM="7001e37412758c43d372a969e977ca11511e034c8c1e233a58dc3ce1c6f3c1aa7d2da8cba9944a5eabaa8885742bfe6cc6794224c146b7129da8f633b53b9cfc"
    if [ ! -f $AK_IPFS_REPO/swarm.key ]
    then
        _ak_log_info "Downloading swarm key"
        curl -s -o $AK_IPFS_REPO/swarm.key https://arching-kaos.net/files/swarm.key
    elif [ -f $AK_IPFS_REPO/swarm.key ] && [ "$(sha512sum $AK_IPFS_REPO/swarm.key | awk '{ print $1 }')" == "$SWARMSHA512SUM" ]
    then
        _ak_log_info "Congrats! You are already in our swarm"
    else
        _ak_log_error "Found swarm.key but not ours"
        _ak_log_error "Visit https://arching-kaos.net/files/swarm.key and copy it to your ipfs folder"
    fi
}

function _ak_ipfs_get_config_ipns_key(){
    _ak_ipfs_key_list_full | grep 'ak-config' | cut -d ' ' -f 1
}