#!/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
source $AK_LIBDIR/_ak_zblock
source $AK_LIBDIR/_ak_script

_ak_zchain_reset(){
    _ak_log_info "Reseting AK_ZLATEST to AK_ZGENESIS"
    cp $AK_ZGENESIS $AK_ZLATEST
    if [ $? -ne 0 ]
    then
        _ak_log_error "Failed to reset AK_ZLATEST to AK_ZGENESIS"
        exit 1
    fi

    _ak_log_info "Checking if /zarchive directory exists"
    _ak_ipfs_files_stat /zarchive > /dev/null
    if [ $? -ne 0 ]
    then
        _ak_ipfs_files_mkdir /zarchive
        if [ $? -ne 0 ]
        then
            _ak_log_error "Could not create directory /zarchive"
            exit 1
        fi
    else
        _ak_log_info "/zarchive directory exists"
    fi

    _ak_log_info "Checking if /zlatest file exists"
    _ak_ipfs_files_stat /zlatest > /dev/null 2>&1
    if [ $? -eq 0 ]
    then
        _ak_log_info "Archive the previous AK_ZLATEST"
        _ak_ipfs_files_cp /zlatest /zarchive/$(date -u +%s)-$(_ak_ipfs_files_stat /zlatest | head -n 1)
        if [ $? -ne 0 ]
        then
            _ak_log_error "Failed to copy /zlatest to /zarchive"
            exit 1
        fi
        _ak_log_info "Removing previous /zlatest entry"
        _ak_ipfs_files_rm /zlatest
        if [ $? -ne 0 ]
        then
            _ak_log_error "Failed to remove /zlatest"
            exit 1
        fi
    else
        _ak_log_info "/zlatest not found, skipping backup"
    fi

    _ak_log_info "Copying reset AK_ZLATEST"
    CZLATEST="$(cat $AK_ZLATEST)"
    _ak_ipfs_files_cp /ipfs/$CZLATEST /zlatest
    if [ $? -ne 0 ]
    then
        _ak_log_error "Failed to copy AK_ZLATEST to /zlatest"
        exit 1
    fi

    _ak_log_info "Publishing new (reset) AK_ZLATEST"
    _ak_ipfs_name_publish --key=zchain /ipfs/$(cat $AK_ZLATEST)
    if [ $? -ne 0 ]
    then
        _ak_log_error "Failed to publish updated zchain"
        exit 1
    fi

    _ak_config_publish
    if [ $? -ne 0 ]
    then
        _ak_log_error "Could not publish new configuration"
        exit 1
    fi

    _ak_log_info "Reset was successful"
    exit 0
}

_ak_zchain_rebase(){
    if [ ! -n "$1" ]; then exit 1; fi
    ZTARGET="$1"
    echo "Reseting ZLATEST to ZTARGET"
    echo $ZTARGET > $AK_ZLATEST
    if [ $? != 0 ]; then exit 1; fi

    echo "Make sure /zarchive folder exists within IPFS FS"
    _ak_ipfs_files_mkdir /zarchive
    if [ $? != 0 ]; then echo "Folder already there"; fi

    echo "Archive the previous ZLATEST"
    _ak_ipfs_files_cp /zlatest /zarchive/$(date -u +%s)-$(_ak_ipfs_files_stat /zlatest | head -n 1)
    if [ $? != 0 ]; then exit 1; fi

    echo "Removing previous /zlatest entry"
    _ak_ipfs_files_rm /zlatest
    if [ $? != 0 ]; then exit 1; fi

    echo "Copying rebased ZLATEST"
    CZLATEST="$(cat $AK_ZLATEST)"
    _ak_ipfs_files_cp /ipfs/$CZLATEST /zlatest
    if [ $? != 0 ]; then exit 1; fi

    echo "Publishing new (rebased) ZLATEST"
    _ak_ipfs_name_publish --key=zchain /ipfs/$(cat $AK_ZLATEST)
    if [ $? != 0 ]; then exit 1; fi

    _ak_config_publish
    if [ $? -ne 0 ]
    then
        _ak_log_error "Could not publish new configuration"
        exit 1
    fi
    echo "Rebase was successful"
    exit 0
}

_ak_zchain_extract_cids(){
    if [ ! -z $1 ] && [ -n "$1" ]
    then
        _ak_zchain_crawl $1 | jq  -M | grep Qm | sed -e 's/".*"://g; s/ //g; s/[{,"]//g' | sort | uniq
    else
        _ak_zchain_crawl | jq -M | grep Qm | sed -e 's/".*"://g; s/ //g; s/[{,"]//g' | sort | uniq
    fi
}

_ak_zchain_extract_cids_limit(){
    if [ ! -z $1 ] && [ -n "$1" ] && [ ! -z $2 ] && [ -n "$2" ]

    then
        _ak_zchain_crawl -l $2 $1 | jq  -M | grep Qm | sed -e 's/".*"://g; s/ //g; s/[{,"]//g' | sort | uniq
    else
        _ak_zchain_crawl | jq -M | grep Qm | sed -e 's/".*"://g; s/ //g; s/[{,"]//g' | sort | uniq
    fi
}

_ak_zchain_extract_data_cids(){
    if [ ! -z $1 ]
    then
        _ak_zchain_crawl $1 | jq | grep ipfs | awk '{print $2}' | sed -e 's/"//g;s/,//g'
    else
        _ak_zchain_crawl | jq | grep ipfs | awk '{print $2}' | sed -e 's/"//g;s/,//g'
    fi
}

_ak_zchain_calculate_size(){
    temp="$(_ak_make_temp_directory)"
    cd $temp
    if [ ! -z $1 ] && [ -n "$1" ]
    then
        _ak_zchain_extract_cids $1 > to_stats
    else
        _ak_zchain_extract_cids > to_stats
    fi
    sum=0 ; while IFS="" read -r p || [ -n "$p" ]
    do
        if [ "$p" != "" ]
        then
            _ak_ipfs_get $p
            num="$( (du -b $p || du -A $p)2>/dev/null | cut -d $'\t' -f 1)"
        else
            num=0
        fi
        sum=$(expr $sum + $num )
    done < to_stats
    echo "Chain is : $sum bytes"
    cd ~
    rm -rf $temp
}

_ak_zchain_crawl(){
    entrance="$(cat $AK_ZLATEST)"
    verify=1
    limit=0
    fromIpns=0
    while [ "$#" ]; do
        case "$1" in
            -h | --help)
                printf "Zchain crawler
==============

ak zchain --crawl [-N | --no-verify] [-l | --limit <number>] [zblock]

ak zchain --crawl [-N | --no-verify] [-l | --limit <number>] -n <zchain>

Usage:

    --help, -h                             Print this help and exit

    --chain <ipns-link>, -n <ipns-link>    Crawl specified chain

    --no-verify, -N                        Don't verify signatures

    <ipfs-link>                            Specify IPFS CID for entrance

Note that combined flags don't work for now
Running with no flags crawls your chain based on AK_ZLATEST environment
variable
"
                exit 1
                ;;
            -l | --limit)
                limit=$2
                shift 2
                ;;
            -N | --no-verify)
                verify=0
                shift
                ;;
            -n | --chain | --ipns)
                fromIpns=1
                ipns=$1
                shift
                ol=$1
                entrance="$(_ak_ipns_resolve $1)"
                if [ $? -ne 0 ]
                then
                    _ak_log_error "Could not resolve IPNS name"
                    exit 1
                fi
                shift
                ;;
            *)
                test="$1"
                if [ ! -z "$test" ] && [ $fromIpns -eq 0 ]
                then
                    _ak_ipfs_cid_v0_check "$test"
                    entrance="$test"
                elif [ -z "$entrance" ] && [ $fromIpns -eq 1 ]
                then
                    entrance="$(cat $AK_ZLATEST)"
                fi
                break
        esac
    done
    # We assign the IPFS CIDv0 of an empty file as this is used
    # as our GENESIS block, hence the "seed" that the tree grows
    # from.
    seed="$(cat $AK_ZGENESIS)"
    # We assume that we found the entrance inside a block, hence
    # ZBLOCK is labeled as previous
    zblock="$entrance"
    # Enter temp folder
    TEMPASSIN="$(_ak_make_temp_directory)"
    cd $TEMPASSIN
    counter=0
    # The loop
    # We break the loop from inside the loop
    while true
    do
        if [ $counter -eq 0 ]
        then
            echo -n '['
        fi
        counter=$(($counter + 1))
        _ak_zblock_show "$zblock"
        if [ $limit -ne 0 ] && [ $limit -eq $counter ]
        then
            echo -n ']'
            exit 0
        else
            echo -n ','
        fi
    done
}

_ak_zchain_crawl_self(){
    _ak_zchain_crawl
}

_ak_zchain_crawl_remote_ipfs(){
    _ak_zchain_crawl $1
}

_ak_zchain_crawl_remote_ipns(){
    _ak_zchain_crawl -n $1
}

_ak_zchain_get_latest(){
    _ak_ipfs_files_stat /zlatest | head -n 1| tr -d '\n'
}

# _ak_zchain_announce(){}
#     PROGRAM="$(basename $0)"
#     printf '[%s]\n' "$PROGRAM"
#     printf "TEST\t/v0/announce/zchain\n"
#     # curl http://127.0.0.1:8610/v0/announce/zchain
#     printf "\t01:\tendpoint with valid data"
#     curl \
#         --connect-timeout 3 \
#         -POST http://localhost:8610/v0/announce/zchain \
#         --header 'Content-Type: application/json' \
#         --data-raw '{"zchain":"k51qzi5uqu5dgapvk7bhxmchuqya9immqdpbz0f1r91ckpdqzub63afn3d5apr"}' 2>/dev/null | jq -M -c > /dev/null
#     if [ $? -eq 0 ]
#     then
#         printf "\t\t\033[0;32mPASSED\033[0;0m"
#     else
#         printf "\t\033[0;31mFAILED\033[0;0m"
#     fi
#     printf "\n"
# 
#     printf "\t02:\tendpoint with invalid data"
#     curl \
#         --connect-timeout 3 \
#         -POST http://localhost:8610/v0/announce/zchain \
#         --header 'Content-Type: application/json' \
#         --data-raw '{"zchain":"k51qzi5uqu5dgapvk7bhxmchuqya9immqdpbz0f1r91ckpdqzub63afn3d5aar"}' 2>/dev/null | jq -M -c > /dev/null
#     if [ $? -eq 0 ]
#     then
#         printf "\t\t\033[0;32mPASSED\033[0;0m"
#     else
#         printf "\t\033[0;31mFAILED\033[0;0m"
#     fi
#     printf "\n"
# 
#     printf "\t03:\tendpoint no data"
#     curl \
#         --connect-timeout 3 \
#         -POST http://localhost:8610/v0/announce/zchain \
#         --header 'Content-Type: application/json' \
#         --data-raw '{"zchain":""}' 2>/dev/null | jq -M -c > /dev/null
#     if [ $? -eq 0 ]
#     then
#         printf "\t\t\t\033[0;32mPASSED\033[0;0m"
#     else
#         printf "\t\t\t\033[0;31mFAILED\033[0;0m"
#     fi
#     printf "\n"
# }

# _ak_zchain_chk(){
#     ##
#     ##    -h, --help                  Prints this help message
#     ##
#     fullprogrampath="$(realpath $0)"
#     PROGRAM=$(basename $0)
#     descriptionString="Quick description"
#     source $AK_LIBDIR/_ak_log
#     source $AK_LIBDIR/_ak_script
#     source $AK_LIBDIR/_ak_ipfs
# 
#     fix="0"
#     usage(){
#         echo "zchain-chk - Check and fix zchain"
#         echo "---------------------------------"
#         echo "Usage:"
#         echo "    --help, -h                             Print this help and exit"
#         echo "    --chain <ipns-link>, -n <ipns-link>    Crawl specified chain"
#         echo "    --fix                                  #TODO Fix your chain"
#         echo ""
#         echo "Note that combined flags don't work for now"
#         echo "Running with no flags crawls your chain"
#     }
# 
#     if [ ! -z "$1" ] && [ "$1" == "-h" ] || [ "$1" == "--help" ]
#     then
#         usage
#         exit
#     elif [ ! -z "$1" ] && [ "$1" == "-f" ] || [ "$1" == "--fix" ]
#     then
#         fix="1"
#         entrance="$(cat $AK_ZLATEST)"
#     elif [ ! -z "$1" ] && [ "$1" == "-n" ] || [ "$1" == "--chain" ]
#     then
#         entrance="$(_ak_ipns_resolve $2)"
#     elif [ ! -z "$1" ]
#     then
#         entrance="$1"
#     else
#         # By default we ak-enter from the latest block
#         # We can alter this by changing this value
#         entrance="$(cat $AK_ZLATEST)"
#     fi
# 
#     # We assign the IPFS CIDv0 of an empty file as this is used
#     # as our GENESIS block, hence the "seed" that the tree grows
#     # from.
#     seed="$(cat $AK_ZGENESIS)"
# 
#     # We assume that we found the entrance inside a block, hence
#     # ZBLOCK is labeled as previous
#     zblock="$entrance"
#     declare -A blocks_found
# 
#     # Enter temp folder
#     TEMPASSIN="$(_ak_make_temp_directory)"
#     cd $TEMPASSIN
#     counter=0
# 
#     # The loop
#     # We break the loop from inside the loop
#     while true
#     do
#         if [ $counter == 0 ]
#         then
#             echo 'Start checking'
#         fi
#         counter=$(expr $counter + 1)
# 
#         # Check if $zblock exists as variable
#         if [ ! -v $zblock ]
#         then
#             # Check if it is not our seed cause if it is we skip this part
#             if [ "$zblock" != "$seed" ]
#             then
#                 # Reset timestamp since it's introduced later
#                 timestamp=''
#                 # Announce to stdout which ZBLOCK is being read at the moment
#                 _ak_log_info "Examining $zblock"
# 
#                 # We create files named after each ZBLOCK IPFS CID for later
#                 # reference. Files are empty.
#                 touch $AK_ZBLOCKDIR/$zblock
#                 _ak_log_info "Created reference"
# 
#                 # We concatenate the zblock's contents, pipe them through filter
#                 # ak-json2bash and output them to tmp-file
#                 _ak_ipfs_cat $zblock | ak-json2bash > tmp-zblock
#                 _ak_log_info "ZBLOCK $zblock READ"
# 
#                 # Supposingly you are on a safe environment and you only have
#                 # access to your chain, I would consider mild secure to source
#                 # the files into your bash.
#                 # File an issue/pull request if you think it can be done better!!
#                 source tmp-zblock
#                 _ak_log_info "ZBLOCK SOURCED"
# 
#                 # Same as above applies to BLOCK and DATA subparts of each ZBLOCK
#                 # BLOCKS
#                 _ak_ipfs_cat $block | ak-json2bash > tmp-block
#                 source tmp-block
#                 _ak_log_info "BLOCK $block SOURCED"
#                 touch $AK_BLOCKDIR/$block
#                 _ak_log_info "BLOCK REFERENCED"
#                 module="$(echo $action | sed -e 's/\// /g' | awk '{ print $1 }')"
#                 _ak_log_info "DATA is $module module."
#                 command="$(echo $action | sed -e 's/\// /g' | awk '{ print $2 }')"
#                 _ak_log_info "COMMAND is $command"
#                 if [ ! -v $timestamp ]
#                 then
#                     echo "$timestamp : $zblock -> $block -> $previous"
#                     blocks_found[$counter]="$block"
#                 fi
#                 touch $AK_DATADIR/$data
# 
#                 # Now, since we sourced the BLOCK to our terminal, we can search
#                 # for $previous variable. In case we don't find one, we exit with
#                 # code 0
#                 if [ -v $previous ]
#                 then
#                     _ak_log_error "Block $block has no previous zblock"
#                     echo "Chain with no genesis"
#                     if [ "$fix" == "1" ]
#                     then
#                         echo "LOL"
#                     else
#                         echo "Blocks found and need repacking..."
#                         for value in ${blocks_found[@]}
#                         do
#                             echo $value
#                             _ak_ipfs_cat $value | jq -M
#                         done
#                     fi
#                     exit 0
#                 # Otherwise, we inform of the sequence
#                 else
#                     zblock=$previous
#                 fi
#             # Now check if it is equal to the seed
#             # which apparently means we reached the seed.
#             elif [ "$zblock" == "$seed" ]
#             then
#                 echo "Chain is OK with GENESIS block = $seed"
#                 _ak_log_info "Counter $counter"
#                 exit 0
#             fi
#         # And finally, if nothing is there exit with error
#         else
#             _ak_log_error "Check not passed... No previous IPFS CID"
#             exit 1
#         fi
#     done
# }