#!/usr/bin/env bash

# A script containing many useful functions and common operations. 
# This file is suitable for sourcing into other scripts.

setup_colors() {
  if [[ -t 2 ]] && [[ -z "${NO_COLOR-}" ]] && [[ "${TERM-}" != "dumb" ]]; then
    NOFORMAT='\033[0m' RED='\033[0;31m' GREEN='\033[0;32m' ORANGE='\033[0;33m' BLUE='\033[0;34m' PURPLE='\033[0;35m' CYAN='\033[0;36m' YELLOW='\033[1;33m'
  else
    NOFORMAT='' RED='' GREEN='' ORANGE='' BLUE='' PURPLE='' CYAN='' YELLOW=''
  fi
}

# setup colors to enable using `msg_*` functions immediately
setup_colors

msg() {
  echo >&2 -e "${1-}"
}

msg_success() {
  msg "${GREEN}${1-}${NOFORMAT}"
}

msg_warn() {
  msg "${ORANGE}${1-}${NOFORMAT}"
}

msg_error() {
  msg "${RED}${1-}${NOFORMAT}"
}

msg_highlight() {
  msg "${YELLOW}${1-}${NOFORMAT}"
}

die() {
  local code=${2-1} # default exit status 1
  msg_error "${1-}"
  exit "$code"
}

### Check if dependencies are installed.
################################################################################
if ! command -v curl &> /dev/null; then
    die "This script needs 'curl' to run. Consider installing it."
fi
if ! command -v jq &> /dev/null; then
    die "This script needs 'jq' to run. Consider installing it."
fi
if ! command -v docker &> /dev/null; then
    die "This script needs 'docker' to run. Consider installing it."
fi

is_docker_container_running() {
  [ -z "${1-}" ] && die "is_docker_container_running: Missing required parameter: name"
  local result; result=$(docker ps --filter "name=^/${1-}$" --filter status=running -q)
  echo "$result"
}

# --------------------------
# Unlock existing wallet
# --------------------------
## API: POST /api/v1/wallet/$wallet_name/unlock
## 
## Response: 
## 200 OK
## {
##    "walletname": "Satoshi.jmdat",
##    "token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9..."
## }
unlock_wallet() {
    local base_url; base_url=${1:-}
    local wallet_name; wallet_name=${2:-}
    local wallet_password; wallet_password=${3:-}
    local unlock_request_payload; unlock_request_payload="{\"password\":\"$wallet_password\"}"

    # param "--insecure": Is needed because a self-signed certificate is used in joinmarket regtest container
    # param "--silent": Don't show progress meter or error messages (errors are reactivated with "--show-error").
    # param "--show-error": When used with -s, --silent, it makes curl show an error message if it fails.
    local unlock_result; unlock_result="$(curl "$base_url/api/v1/wallet/$wallet_name/unlock" --silent --show-error --insecure --data "$unlock_request_payload" | jq ".")"

    local unlock_result_error_msg; unlock_result_error_msg=$(jq -r '. | select(.message != null) | .message' <<< "$unlock_result")
    if [ "$unlock_result_error_msg" != "" ]; then
        die "$unlock_result_error_msg"
    fi

    echo "$unlock_result"
}

# --------------------------
# Lock wallet
# --------------------------
## API: GET /api/v1/wallet/$wallet_name/lock
## 
## Response: 
## 200 OK
## {
##  "walletname": "Satoshi.jmdat",
##  "already_locked": false
## }
lock_wallet() {
    local base_url; base_url=${1:-}
    local auth_header; auth_header=${2:-}
    local wallet_name; wallet_name=${3:-}
    
    local lock_result; lock_result=$(curl "$base_url/api/v1/wallet/$wallet_name/lock" --silent --show-error --insecure -H "$auth_header" | jq ".")

    local locked_wallet_name; locked_wallet_name=$(jq -r '.walletname' <<< "$lock_result")
    [ "$locked_wallet_name" != "$wallet_name" ] && die "Problem while locking wallet $wallet_name"

    echo "$lock_result"
}

# --------------------------
# Create new wallet
# --------------------------
## API: POST /api/v1/wallet/create
## 
## Response: 
## 200 OK
## {
##    "walletname": "Satoshi.jmdat",
##    "token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9...", 
##    "seedphrase": "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about"
## }
## or (if wallet already exists):
## 409 Conflict
## {
##  "message": "Wallet file cannot be overwritten."
## }
create_wallet() {
    local base_url; base_url=${1:-}
    local wallet_name; wallet_name=${2:-}
    local wallet_password; wallet_password=${3:-}
    local create_request_payload; create_request_payload="{\"password\":\"$wallet_password\",\"walletname\":\"$wallet_name\",\"wallettype\":\"sw-fb\"}"

    local create_result; create_result="$(curl "$base_url/api/v1/wallet/create" --silent --show-error --insecure --data "$create_request_payload" | jq ".")"

    local create_result_error_msg; create_result_error_msg=$(jq -r '. | select(.message != null) | .message' <<< "$create_result")
    if [ "$create_result_error_msg" != "" ]; then
        die "$create_result_error_msg"
    fi

    echo "$create_result"
}

# --------------------------
# Fetch new address
# --------------------------
## API: GET /api/v1/wallet/$wallet_name/address/new/$mixdepth
## 
## Response:
## 200 OK
## {
##  "address": "1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa"
## }
fetch_new_address() {
    local base_url; base_url=${1:-}
    local auth_header; auth_header=${2:-}
    local wallet_name; wallet_name=${3:-}
    local mixdepth; mixdepth=${4:-}

    local address_result; address_result=$(curl "$base_url/api/v1/wallet/$wallet_name/address/new/$mixdepth" --silent --show-error --insecure -H "$auth_header" | jq ".")

    local address; address=$(jq -r '.address' <<< "$address_result")

    echo "$address"
}

# --------------------------
# Fetch available wallets
# --------------------------
## API: GET /api/v1/wallet/all
## 
## Response:
## 200 OK
## {
##    "wallets": ["Satoshi.jmdat", "test0.jmdat", "test1.jmdat", "test2.jmdat"]
## }
##
fetch_available_wallets() {
    local base_url; base_url=${1:-}

    local wallet_all_result; wallet_all_result="$(curl "$base_url/api/v1/wallet/all" --silent --show-error --insecure | jq ".")"
    local available_wallets; available_wallets="$(jq -r '.wallets' <<< "$wallet_all_result")"

    echo "$available_wallets"
}

# --------------------------
# Fetch Session Information
# --------------------------
## API: GET /api/v1/session
## 
## Response:
## 200 OK
## { 
##    "session": false, 
##    "maker_running": false, 
##    "coinjoin_in_process": false, 
##    "wallet_name": "None"
## }
##
fetch_session() {
    local base_url; base_url=${1:-}
    local session_result; session_result="$(curl "$base_url/api/v1/session" --silent --show-error --insecure | jq ".")"

    echo "$session_result"
}

# --------------------------
# Verify no open session
# --------------------------
verify_no_open_session_or_throw() {
    local base_url; base_url=${1:-}

    if session_result=$(fetch_session "$base_url"); then
      msg_success "Successfully established connection to jmwalletd"
    else rc=$?
      die "Could not connect to joinmarket. Please make sure jmwalletd is running inside container."
    fi

    [ "$(jq -r '.session' <<< "$session_result")" != "false" ] && die "Please make sure no session is active."
    [ "$(jq -r '.wallet_name' <<< "$session_result")" != "None" ] && die "Please make sure no wallet is active."

    return 0
}

is_maker_running() {
  local base_url; base_url=${1:-}

  local session_result; session_result=$(fetch_session "$base_url")
  local maker_running; maker_running=$(jq -r '.maker_running' <<< "$session_result")

  echo "$maker_running"
}
