#!/bin/bash

set -e

print_help() {
    cat <<HELP
Usage: oxen-multi-sn-create NN 'https://l2.provider/url' [backups...]
Usage: oxen-multi-sn-create NN l2-oxend IP:PORT/PUBKEY [backups...]
Usage: oxen-multi-sn-create NN

NN is a two-digit number.

Remaining arguments specify Arbitrum One L2 RPC providers:

- if HTTP(S) URLs are provided, they will be listed as l2-provider= lines in
  the node's config file.  They will be checked to make sure they respond and
  are returning the correct Arbitrum One chain ID.  (To skip this check, add
  the argument "nocheck" before the first URL).

- if the providers begin with the argument "l2-oxend" then subsequent
  parameters are the IP:PORT/PUBKEY values of another oxend to use as a proxy
  for L2 chain data, or the "ipc:///path/to/oxend.sock" on the current machine.
  In either case, the target must be set up in l2-proxy mode.  See online
  documentation for more details.

- if no arguments are provided after the NN at all then this script will look
  for existing l2-oxend= or l2-provider= lines in existing
  /etc/oxen/node-NN.conf files and use those values.  This is generally what
  you want if you are adding new instances to a server with working instances.

Backups can be specified by listing multiple URLs or l2-oxend addresses.

Example using a primary and backup L2-provider:

    oxen-multi-sn-create 00 'https://example.com/arb1rpc' 'https://backup.example.com/arb1'

Example using oxend L2 proxies, where one proxy is node-00 on the same server,
and the other is a remote node at the given IP/port/pubkey:

    oxen-multi-sn-create 01 l2-oxend \\
        ipc:///var/lib/oxen/node-00/oxend.sock \\
        1.2.3.4:9876/d511bcbbf01f739187df45a59501aafaa02cedda678d05cb9aee27f9b91f0caf

(Note that if the proxy is a very old node (installed before Oxen v8), it may
have different primary pubkey and Ed25519 pubkeys: in such a case you must use
Ed25519 pubkey rather than the primary SN pubkey here).
HELP
}

if [ "$#" -eq 0 ] || [[ ! $1 =~ ^[0-9][0-9]$ ]]; then
    print_help
    exit 1
fi

if [ "$UID" -ne 0 ]; then
    echo "This script must be run as root (e.g. via sudo)."
    exit 1
fi

PT="$1"
shift

l2_setting=
bad=

if [ "$#" -eq 0 ]; then
    # No args given: go see if we can find any existing node-NN.conf with
    # l2-provider= or l2-oxend= in it.  If we find both, we prefer l2-oxend, as
    # it is probably quite common to have the proxy itself listed with
    # l2-provider and the others pointing at it via l2-oxend.
    from=
    for x in /etc/oxen/node-[0-9][0-9].conf; do
        if [ -z "$l2_setting" ]; then
            # Only look for this if we haven't found anything yet:
            l2prov="$(grep '^l2-provider=.' $x || true)"
            l2_setting="$l2prov"
            from="$x"
        fi
        # Always look for l2-oxend, because with the 'break' we can only get
        # here if we either haven't found anything, or have only found
        # a l2-provider= so far.
        l2oxend="$(grep '^l2-oxend=.' $x || true)"
        if [ -n "$l2oxend" ]; then
            l2_setting="$l2oxend"
            from="$x"
            break
        fi
    done

    if [ -n "$l2_setting" ]; then
        echo "Auto-detected the following L2 provider settings from $from:"
        echo "$l2_setting" | sed -e 's/^/    /'
        read -p "Press enter to confirm these L2 settings, Ctrl-C to abort."
    else
        echo "No l2-oxend= or l2-provider= found in any existing /etc/oxen/node-NN.conf; you need to specify a provider or proxy to use."
        bad=1
    fi
elif [ "$1" = "l2-oxend" ]; then
    shift
    if [ "$#" -eq 0 ]; then
        bad=1
        echo "No L2 oxend proxies listed after 'l2-oxend', unable to proceed!"
    fi

    for proxy in "$@"; do
        if [[ "$proxy" =~ ^([1-9][0-9]*\.){3}[1-9][0-9]*:[1-9][0-9]{0,4}/[a-fA-F0-9]{64}$ ]] || [[ "$proxy" =~ ^ipc://. ]]; then
            l2_setting="$l2_setting
l2-oxend=$proxy"
        else
            bad=1
            echo "Invalid l2-oxend proxy address: '$proxy'; expected IP:PORT/PUBKEY"
        fi
    done
else
    if [ "$1" = "nocheck" ]; then
        shift
        if [ "$#" -eq 0 ]; then
            bad=1
            echo "No L2 providers listed after 'nocheck', unable to proceed!"
        fi
    else
        for url in "$@"; do
            outi=$(mktemp)
            erri=$(mktemp)
            echo -en "\nChecking '$url'..."
            if curl -sSX POST "$url" --connect-timeout 10 -H "Content-Type: application/json" -d '{"jsonrpc":"2.0","method":"eth_chainId","params":[],"id":1}' -o "$outi" 2>"$erri"; then
                if grep '"0xa4b1"' "$outi" >/dev/null; then
                    echo -e " Success!"
                else
                    bad=1
                    echo -e " Did not find expected Arbitrum One network id \"0xa4b1\" in the L2 provider 'eth_chainId' response:\n-----"
                    cat "$outi"
                    echo -e "-----\nThis RPC provider may be on the wrong network\n"
                fi
            else
                bad=1
                echo -e " Request failed:\n-----"
                cat "$erri"
                echo -e "-----\n"
            fi
        done
    fi

    for proxy in "$@"; do
        l2_setting="$l2_setting
l2-provider=$proxy"
    done
fi

ip=$(curl -sS -4 http://api.ipify.org)
if [ -z "$ip" ]; then
    echo "Failed to figure out your public IPv4 address via ipify.org"
    bad=1
fi


if [ -n "$bad" ]; then
    echo -e "\n\n\e[31;1mCorrect the errors above and then re-run this script\e[0m\n"
    exit 2
fi


oxend_conf=/etc/oxen/node-$PT.conf
ss_conf=/etc/oxen/storage-$PT.conf
lokinet_conf=/etc/loki/lokinet-router-$PT.ini
srouter_conf=/etc/session-router/relay-$PT.ini
if ! [ -d /etc/loki ]; then mkdir /etc/loki; fi
if ! [ -d /etc/session-router ]; then mkdir /etc/session-router; fi
if [ -z "$OVERWRITE" ]; then
    for x in $oxend_conf $ss_conf $lokinet_conf $srouter_conf /var/lib/oxen/node-$PT /var/lib/lokinet/router-$PT /var/lib/oxen/storage-$PT /var/lib/session-router/relay-$PT; do
        if [ -e $x ]; then
            echo "$x already exists, refusing to overwrite."
            exit 1
        fi
    done
fi

echo "Creating $oxend_conf"
cat <<EOF >$oxend_conf
service-node=1
service-node-public-ip=$ip
p2p-bind-port=222$PT
rpc-admin=127.0.0.1:223$PT
quorumnet-port=225$PT
data-dir=/var/lib/oxen/node-$PT
$l2_setting
EOF
mkdir -p /var/lib/oxen/node-$PT
chown _loki:_loki /var/lib/oxen/node-$PT

echo "Creating $ss_conf"
cat <<EOF >$ss_conf
https-port=221$PT
omq-port=202$PT
oxend-rpc=ipc:///var/lib/oxen/node-$PT/oxend.sock
data-dir=/var/lib/oxen/storage-$PT
EOF
mkdir -p /var/lib/oxen/storage-$PT
chown _loki:_loki /var/lib/oxen/storage-$PT

echo "Creating $lokinet_conf"
tmpdir=$(mktemp --tmpdir -d lokinet.XXXXXXXXXX)
/usr/bin/lokinet -r -g $tmpdir/lokinet.ini
perl -pi -e "
    s#$tmpdir#/var/lib/lokinet/router-$PT#;
    if (/^\[lokid/ ... /^\[/) {
        s{^#?rpc=.*}{rpc=ipc:///var/lib/oxen/node-$PT/oxend.sock};
    }
    if (/^\[router/ ... /^\[/) {
        s{#?public-ip=.*}{public-ip=$ip};
        s{#?public-port=.*}{public-port=109$PT};
    }
    if (/^\[bind/ ... /^\[/) {
        \$_ = qq{0.0.0.0=109$PT\n\n} if /^$/ and not \$added_bind++;
    }
    if (/^\[dns/ ... /^\[/) {
        s{^#?bind=(?:.*):53}{bind=127.1$PT.0.0:53};
    }
    if (/^\[api/ ... /^\[/) {
        s{^#?bind=127.0.0.1:1190}{bind=127.1$PT.0.0:1190};
    }
    if (/^\[network\]/) {
        s{^#?ifname=.*}{ifname=lokitun$PT};
        s{^#?ifaddr=.*}{ifaddr=10.1$PT.0.1/16}
    }
" $tmpdir/lokinet.ini
chmod 640 $tmpdir/lokinet.ini
chgrp _loki $tmpdir/lokinet.ini
mv $tmpdir/lokinet.ini $lokinet_conf
mkdir -p /var/lib/lokinet/router-$PT
ln -snf $lokinet_conf /var/lib/lokinet/router-$PT/lokinet.ini
chown -R _lokinet:_loki /var/lib/lokinet/router-$PT

echo "Creating $srouter_conf"
f=/etc/session-router/relay-$PT.ini
/usr/bin/session-router-config --relay --data-dir=/var/lib/session-router/relay-$PT \
    --public-ip=$ip --public-port=119$PT --listen=:119$PT \
    --oxend-rpc=ipc:///var/lib/oxen/node-$PT/oxend.sock \
    $f
chmod 644 $f
mkdir -p /var/lib/session-router/relay-$PT
chown -R _session:_session /var/lib/session-router/relay-$PT

echo -e "\n\n\nDone with configuration setup.  Enabling services..."
systemctl enable {oxen-node,oxen-storage-server,lokinet-router,session-router-relay}@$PT.service

echo -e "\n\nServices configured to auto-start.  Press enter to actually start services now,"
echo -e     "or Ctrl-C if you want to look around first before launching them.  Note that if"
echo -e     "you hit Ctrl-C here you will have to start them yourself using:\n"
echo -e     "    systemctl start oxen-node@$PT oxen-storage-server@$PT lokinet-router@$PT session-router-relay@$PT\n"
read

echo "Starting services..."

systemctl start {oxen-node,oxen-storage-server,lokinet-router,session-router-relay}@$PT.service

echo "All done!\n"
