#!/bin/bash

# This script runs on every start called by boostrap.service
# It makes sure that the system is configured like the
# default values or as in the config.
# For more details see background_raspiblitzSettings.md

# use to detect multiple starts of service
#uid=$(date +%s)
#echo "started" > /home/admin/${uid}.boot

# load codeVersion
source /home/admin/_version.info

################################
# FILES TO WORK WITH
################################

# CONFIGFILE - configuration of RaspiBlitz
# used by fresh SD image to recover configuration
# and delivers basic config info for scripts 
# make raspiblitz.conf if not there
sudo touch /mnt/hdd/raspiblitz.conf
configFile="/mnt/hdd/raspiblitz.conf"

# LOGFILE - store debug logs of bootstrap
# resets on every start
logFile="/home/admin/raspiblitz.log"

# INFOFILE - state data from bootstrap
# used by display and later setup steps
infoFile="/home/admin/raspiblitz.info"

echo "Writing logs to: ${logFile}"
echo "" > $logFile
echo "***********************************************" >> $logFile
echo "Running RaspiBlitz Bootstrap ${codeVersion}" >> $logFile
date >> $logFile
echo "***********************************************" >> $logFile

# display 3 secs logo - try to kickstart LCD
# see https://github.com/rootzoll/raspiblitz/issues/195#issuecomment-469918692
# see https://github.com/rootzoll/raspiblitz/issues/647
randnum=$(shuf -i 0-7 -n 1)
sudo fbi -a -T 1 -d /dev/fb1 --noverbose /home/admin/raspiblitz/pictures/startlogo${randnum}.png
sleep 5
sudo killall -3 fbi

# set default values for raspiblitz.info
network=""
chain=""
setupStep=0

# try to load old values if available (overwrites defaults)
source ${infoFile} 2>/dev/null

# resetting info file
echo "Resetting the InfoFile: ${infoFile}"
echo "state=starting" > $infoFile
echo "message=" >> $infoFile
echo "network=${network}" >> $infoFile
echo "chain=${chain}" >> $infoFile
echo "setupStep=${setupStep}" >> $infoFile
if [ "${setupStep}" != "100" ]; then
  echo "hostname=${hostname}" >> $infoFile
fi
sudo chmod 777 ${infoFile}

# resetting start count files
echo "SYSTEMD RESTART LOG: blockchain (bitcoind/litecoind)" > /home/admin/systemd.blockchain.log
echo "SYSTEMD RESTART LOG: lightning (LND)" > /home/admin/systemd.lightning.log
sudo chmod 777 /home/admin/systemd.blockchain.log
sudo chmod 777 /home/admin/systemd.lightning.log

# Emergency cleaning logs when over 1GB (to prevent SD card filling up)
# see https://github.com/rootzoll/raspiblitz/issues/418#issuecomment-472180944
echo "*** Checking Log Size ***"
logsMegaByte=$(sudo du -c -m /var/log | grep "total" | awk '{print $1;}')
if [ ${logsMegaByte} -gt 1000 ]; then
  echo "WARN !! Logs /var/log in are bigger then 1GB"
  echo "ACTION --> DELETED ALL LOGS"
  sudo rm -r /var/log/*
  sleep 3
  echo "WARN !! Logs in /var/log in were bigger then 1GB and got emergency delete to prevent fillup."
  echo "If you see this in the logs please report to the GitHub issues, so LOG config needs to hbe optimized."
else
  echo "OK - logs are at ${logsMegaByte} MB - within safety limit"
fi
echo ""

################################
# GENERATE UNIQUE SSH PUB KEYS
# on first boot up
################################

numberOfPubKeys=$(sudo ls /etc/ssh/ | grep -c 'ssh_host_')
if [ ${numberOfPubKeys} -eq 0 ]; then
  echo "*** Generating new SSH PubKeys" >> $logFile
  sudo dpkg-reconfigure openssh-server
  echo "OK" >> $logFile
fi

################################
# AFTER BOOT SCRIPT
# when a process needs to 
# execute stuff after a reboot
# it should in file
# /home/admin/setup.sh
################################

# check for after boot script
afterSetupScriptExists=$(ls /home/admin/setup.sh 2>/dev/null | grep -c setup.sh)
if [ ${afterSetupScriptExists} -eq 1 ]; then
  echo "*** SETUP SCRIPT DETECTED ***"
  # echo out script to journal logs
  sudo cat /home/admin/setup.sh
  # execute the after boot script
  sudo /home/admin/setup.sh
  # delete the after boot script
  sudo rm /home/admin/setup.sh
  # reboot again
  echo "DONE wait 6 secs ... one more reboot needed ... "
  sudo shutdown -r now
  sleep 100
fi

################################
# HDD CHECK & PRE-INIT
################################
 
# waiting for HDD to connect
hddExists=$(lsblk | grep -c sda1)
while [ ${hddExists} -eq 0 ]
  do
    # display will ask user to connect a HDD
    sed -i "s/^state=.*/state=nohdd/g" ${infoFile}
    sed -i "s/^message=.*/message='Connect the Hard Drive'/g" ${infoFile}
    sleep 5
    # retry to find HDD
    hddExists=$(lsblk | grep -c sda1)
  done

# check if the HDD is auto-mounted ( auto-mounted = setup-done)
hddIsAutoMounted=$(sudo cat /etc/fstab | grep -c '/mnt/hdd')
if [ ${hddIsAutoMounted} -eq 0 ]; then

  echo "HDD is there but not AutoMounted yet." >> $logFile
  echo "Analysing the situation ..." >> $logFile

  # detect for correct device name (the biggest partition)
  hddDeviceName="sda1"
  hddSecondPartitionExists=$(lsblk | grep -c sda2)
  hddSecondDriveExists=$(lsblk | grep -c sdb)
  if [ ${hddSecondPartitionExists} -eq 1 ] || [ ${hddSecondDriveExists} -eq 1 ] ; then
    echo "HDD has a second partition - choosing the bigger one ..." >> $logFile
    # get both with size
    size1=$(lsblk -o NAME,SIZE -b | grep "sda1" | awk '{ print substr( $0, 12, length($0)-2 ) }' | xargs)
    echo "sda1(${size1})" >> $logFile
    size2=$(lsblk -o NAME,SIZE -b | grep "sda2" | awk '{ print substr( $0, 12, length($0)-2 ) }' | xargs)
    echo "sda2(${size2})" >> $logFile
    size3=$(lsblk -o NAME,SIZE -b | grep "sdb" | awk '{ print substr( $0, 8, length($0)-2 ) }' | xargs)
    echo "sdb(${size3})" >> $logFile
    # choose to run with the bigger one
    if [ ${size2} -gt ${size1} ]; then
      echo "sda2 is BIGGER - run with this one" >> $logFile
      hddDeviceName="sda2"
    elif [ ${size3} -gt ${size1} ]; then
      echo "sdb is BIGGER - run with this one" >> $logFile
      hddDeviceName="sdb"      
    else
      echo "sda1 is BIGGER - run with this one" >> $logFile
      hddDeviceName="sda1"
    fi
  fi

  # check if HDD is formatted EXT4
  hddExt4=$(lsblk -o NAME,FSTYPE -b /dev/${hddDeviceName} | grep -c "ext4")
  if [ ${hddExt4} -eq 0 ]; then
    echo "HDD is NOT formatted in ext4." >> $logFile
    # stop the bootstrap here ...
    # display will ask user to run setup
    sed -i "s/^state=.*/state=waitsetup/g" ${infoFile}
    sed -i "s/^message=.*/message='HDD needs SetUp (1)'/g" ${infoFile}
    exit 0
  fi

  # temp-mount the HDD
  echo "temp-mounting the HDD .." >> $logFile
  sudo mkdir /mnt/hdd
  sudo mount -t ext4 /dev/${hddDeviceName} /mnt/hdd
  mountOK=$(lsblk | grep -c '/mnt/hdd')
  if [ ${mountOK} -eq 0 ]; then
    echo "FAIL - not able to temp-mount HDD" >> $logFile
    sed -i "s/^state=.*/state=waitsetup/g" ${infoFile}
    sed -i "s/^message=.*/message='HDD failed Mounting'/g" ${infoFile}
    # no need to unmount the HDD, it failed mounting
    exit 0
  else 
     echo "OK - HDD available under /mnt/hdd" >> $logFile
  fi

  # UPDATE MIGRATION & CONFIG PROVISIONING 
  # check if HDD contains already a configuration
  echo "Check if HDD contains already a configuration .." >> $logFile
  configExists=$(ls ${configFile} | grep -c '.conf')
  if [ ${configExists} -eq 1 ]; then
    echo "Found existing configuration" >> $logFile
    source ${configFile}
    # check if config files contains basic: version
    if [ ${#raspiBlitzVersion} -eq 0 ]; then
      echo "Invalid Config: missing raspiBlitzVersion in (${configFile})!" >> ${logFile}
      configExists=0
    fi
    # check if config files contains basic: network
    if [ ${#network} -eq 0 ]; then
      echo "Invalid Config: missing network in (${configFile})!" >> ${logFile}
      configExists=0
    fi
    # check if config files contains basic: chain
    if [ ${#chain} -eq 0 ]; then
      echo "Invalid Config: missing chain in (${configFile})!" >> ${logFile}
      configExists=0
    fi
    if [ ${configExists} -eq 0 ]; then
      echo "Moving invalid config to raspiblitz.invalid.conf" >> ${logFile}
      sudo mv ${configFile} /mnt/hdd/raspiblitz.invalid.conf
    fi
  fi
  # if config is still valid ...
  if [ ${configExists} -eq 1 ]; then
    echo "Found valid configuration" >> $logFile
    sed -i "s/^state=.*/state=recovering/g" ${infoFile}
    sed -i "s/^message=.*/message='Starting Recover'/g" ${infoFile}
    echo "Calling Data Migration .." >> $logFile
    sudo /home/admin/_bootstrap.migration.sh
    echo "Calling Provisioning .." >> $logFile
    sudo /home/admin/_bootstrap.provision.sh
    sed -i "s/^state=.*/state=recovered/g" ${infoFile}
    sed -i "s/^message=.*/message='Done Recover'/g" ${infoFile}
    echo "rebooting" >> $logFile
    # set flag that system is freshly recovered and needs setup dialogs
    echo "state=recovered" >> /home/admin/raspiblitz.recover.info
    # save log file for inspection before reboot
    cp $logFile /home/admin/raspiblitz.recover.log
    sudo shutdown -r now
    exit 0
  else 
    echo "OK - No config file found: ${configFile}" >> $logFile
  fi

  # check if HDD contains existing LND data (old RaspiBlitz Version)
  echo "Check if HDD contains existing LND data .." >> $logFile
  lndDataExists=$(ls /mnt/hdd/lnd/lnd.conf | grep -c '.conf')
  if [ ${lndDataExists} -eq 1 ]; then
    echo "Found existing LND data - old RaspiBlitz?" >> $logFile
    sed -i "s/^state=.*/state=olddata/g" ${infoFile}
    sed -i "s/^message=.*/message='No Auto-Update possible'/g" ${infoFile}
    # keep HDD mounted if user wants to copy data
    exit 0
  else 
    echo "OK - No LND data found" >> $logFile
  fi

  # check if HDD contains pre-loaded blockchain data
  echo "Check if HDD contains pre-loaded blockchain data .." >> $logFile
  litecoinDataExists=$(ls /mnt/hdd/litecoin/blocks/blk00000.dat 2>/dev/null | grep -c '.dat')
  bitcoinDataExists=$(ls /mnt/hdd/bitcoin/blocks/blk00000.dat 2>/dev/null | grep -c '.dat')

  # check if node can go into presync (only for bitcoin)
  if [ ${bitcoinDataExists} -eq 1 ]; then

    # update info file
    sed -i "s/^state=.*/state=presync/g" ${infoFile}
    sed -i "s/^message=.*/message='starting presync'/g" ${infoFile}

    # activating presync
    # so that on a hackathon you can just connect a RaspiBlitz
    # to the network and have it up-to-date for setting up
    echo "Found pre-loaded blockchain" >> $logFile

    # check if pre-sync was already activated on last power-on
    #presyncActive=$(systemctl status bitcoind | grep -c 'could not be found')
    echo "starting pre-sync in background" >> $logFile
    # make sure that debug file is clean, so just pre-sync gets analysed on stop
    sudo rm /mnt/hdd/bitcoin/debug.log 2>/dev/null
    # starting in background, because this scripts is part of systemd
    # so to change systemd needs to happen after delay in seperate process
    sudo chown -R bitcoin:bitcoin /mnt/hdd/bitcoin 2>> $logFile
    sudo -u bitcoin /usr/local/bin/bitcoind -daemon -conf=/home/admin/assets/bitcoin.conf -pid=/mnt/hdd/bitcoin/bitcoind.pid 2>> $logFile
    echo "OK Started bitcoind for presync" >> $logFile
    sudo sed -i "s/^message=.*/message='running presync'/g" ${infoFile}
    # after admin login, presync will be stopped and HDD unmounted
    exit 0
  
  else
    echo "OK - No bitcoin blockchain data found" >> $logFile
  fi

  # if it got until here: HDD is empty ext4
  echo "Waiting for SetUp." >> $logFile
  sed -i "s/^state=.*/state=waitsetup/g" ${infoFile}
  sed -i "s/^message=.*/message='HDD needs SetUp (2)'/g" ${infoFile}
  # unmount HDD to be ready for auto-mount during setup
  sudo umount -l /mnt/hdd
  exit 0

fi # END - no automount

#####################################
# UPDATE HDD CONFIG FILE (if exists)
# needs to be done before starting LND
# so that environment info is fresh
#####################################

echo "Check if HDD contains configuration .." >> $logFile
configExists=$(ls ${configFile} | grep -c '.conf')
if [ ${configExists} -eq 1 ]; then

  # make sure lndAddress & lndPort exist
  valueExists=$(cat ${configFile} | grep -c 'lndPort=')
  if [ ${valueExists} -eq 0 ]; then
    lndPort=$(sudo cat /mnt/hdd/lnd/lnd.conf | grep "^listen=*" | cut -f2 -d':')
    if [ ${#lndPort} -eq 0 ]; then
      lndPort="9735"
    fi
    echo "lndPort='${lndPort}'" >> ${configFile}
  fi
  valueExists=$(cat ${configFile} | grep -c 'lndAddress=')
  if [ ${valueExists} -eq 0 ]; then
      echo "lndAddress=''" >> ${configFile}
  fi

  # load values
  echo "load and update publicIP" >> $logFile
  source ${configFile}
  freshPublicIP=""
  
  # determine the publicIP/domain that LND should announce
  if [ ${#lndAddress} -gt 3 ]; then

    # use domain as PUBLICIP 
    freshPublicIP="${lndAddress}"

  else

    # update public IP on boot
    # wait otherwise looking for publicIP fails
    sleep 5
    freshPublicIP=$(curl -s http://v4.ipv6-test.com/api/myip.php)

    # sanity check on IP data
    # see https://github.com/rootzoll/raspiblitz/issues/371#issuecomment-472416349
    echo "-> sanity check of IP data:"
    if [[ $freshPublicIP =~ ^(([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(ffff(:0{1,4}){0,1}:){0,1}((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]))$ ]]; then
      echo "OK IPv6"
    elif [[ $freshPublicIP =~ ^([0-9]{1,2}|1[0-9][0-9]|2[0-4][0-9]|25[0-5])\.([0-9]{1,2}|1[0-9][0-9]|2[0-4][0-9]|25[0-5])\.([0-9]{1,2}|1[0-9][0-9]|2[0-4][0-9]|25[0-5])\.([0-9]{1,2}|1[0-9][0-9]|2[0-4][0-9]|25[0-5])$ ]]; then
      echo "OK IPv4"
    else
      echo "FAIL - not an IPv4 or IPv6 address"
      freshPublicIP=""
    fi

    if [ ${#freshPublicIP} -eq 0 ]; then
      # prevent having no publicIP set at all and LND getting stuck
      # https://github.com/rootzoll/raspiblitz/issues/312#issuecomment-462675101
      if [ ${#publicIP} -eq 0 ]; then
        localIP=$(ip addr | grep 'state UP' -A2 | tail -n1 | awk '{print $2}' | cut -f1 -d'/')
        echo "WARNING: No publicIP information at all - working with placeholder: ${localIP}" >> $logFile
        freshPublicIP="${localIP}"
      fi
    fi

  fi

  # set publicip value in raspiblitz.conf
  if [ ${#freshPublicIP} -eq 0 ]; then
    echo "WARNING: Was not able to determine external IP/domain on startup." >> $logFile
  else
    publicIPValueExists=$( sudo cat ${configFile} | grep -c 'publicIP=' )
    if [ ${publicIPValueExists} -gt 1 ]; then
      # remove one 
      echo "more then one publiIp entry - removing one" >> $logFile
      sed -i "s/^publicIP=.*//g" ${configFile}
      publicIPValueExists=$( sudo cat ${configFile} | grep -c 'publicIP=' )
    fi
    if [ ${publicIPValueExists} -eq 0 ]; then
      echo "create value (${freshPublicIP})" >> $logFile
      echo "publicIP='${freshPublicIP}'" >> $configFile
    else
      echo "update value (${freshPublicIP})" >> $logFile
      sed -i "s/^publicIP=.*/publicIP='${freshPublicIP}'/g" ${configFile}
    fi
  fi

fi

#################################
# FIX BLOCKCHAINDATA OWNER (just in case)
# https://github.com/rootzoll/raspiblitz/issues/239#issuecomment-450887567
#################################
sudo chown bitcoin:bitcoin -R /mnt/hdd/bitcoin 2>/dev/null

#################################
# MAKE SURE ADMIN USER HAS LATEST LND DATA
#################################
source ${configFile}
if [ ${#network} -gt 0 ] && [ ${#chain} -gt 0 ]; then

  echo "making sure LND blockchain RPC password is set correct in lnd.conf" >> $logFile
  source <(sudo cat /mnt/hdd/${network}/${network}.conf 2>/dev/null | grep "rpcpass" | sed 's/^[a-z]*\./lnd/g')
  if [ ${#rpcpassword} -gt 0 ]; then
    sudo sed -i "s/^${network}d.rpcpass=.*/${network}d.rpcpass=${rpcpassword}/g" /mnt/hdd/lnd/lnd.conf 2>/dev/null
  else
    echo "WARN: could not get value 'rpcuser' from blockchain conf" >> $logFile
  fi

  echo "updating admin user LND data" >> $logFile
  sudo cp /mnt/hdd/lnd/lnd.conf /home/admin/.lnd/lnd.conf 2>> $logFile
  sudo chown admin:admin /home/admin/.lnd/lnd.conf 2>> $logFile
  sudo cp /mnt/hdd/lnd/tls.cert /home/admin/.lnd/tls.cert 2>> $logFile
  sudo chown admin:admin /home/admin/.lnd/tls.cert 2>> $logFile
  sudo cp /mnt/hdd/lnd/data/chain/${network}/${chain}net/admin.macaroon /home/admin/.lnd/data/chain/${network}/${chain}net/admin.macaroon 2>/dev/null
  sudo chown admin:admin /home/admin/.lnd/data/chain/${network}/${chain}net/admin.macaroon 2>> $logFile

else 
  echo "skipping admin user LND data update" >> $logFile
fi

################################
# DETECT FRESHLY RECOVERED SD
################################

recoveredInfoExists=$(ls /home/admin/raspiblitz.recover.info | grep -c '.info')
if [ ${recoveredInfoExists} -eq 1 ]; then
  sed -i "s/^state=.*/state=recovered/g" ${infoFile}
  sed -i "s/^message=.*/message='login to finish'/g" ${infoFile}
  exit 0
fi

################################
# SD INFOFILE BASICS
################################

# state info
sed -i "s/^state=.*/state=ready/g" ${infoFile}
sed -i "s/^message=.*/message='waiting login'/g" ${infoFile}

# determine network and chain from system

# check for BITCOIN
loaded=$(sudo systemctl status bitcoind | grep -c 'loaded')
if [ ${loaded} -gt 0 ]; then
  sed -i "s/^network=.*/network=bitcoin/g" ${infoFile}
  source /mnt/hdd/bitcoin/bitcoin.conf
  if [ ${testnet} -gt 0 ]; then
    sed -i "s/^chain=.*/chain=test/g" ${infoFile}
  else
    sed -i "s/^chain=.*/chain=main/g" ${infoFile}
  fi
fi

# check for LITECOIN
loaded=$(sudo systemctl status litecoind | grep -c 'loaded')
if [ ${loaded} -gt 0 ]; then
  sed -i "s/^network=.*/network=litecoin/g" ${infoFile}
  sed -i "s/^chain=.*/chain=main/g" ${infoFile}
fi

################################
# DELETE LOG FILES
################################
# LND and Blockchain Errors will be still in systemd journals

# /mnt/hdd/bitcoin/debug.log
sudo rm /mnt/hdd/${network}/debug.log 2>/dev/null
# /mnt/hdd/lnd/logs/bitcoin/mainnet/lnd.log
sudo rm /mnt/hdd/lnd/logs/${network}/${chain}net/lnd.log 2>/dev/null

################################
# RECORD BASEIMAGE
################################

baseImage="?"
isDietPi=$(uname -n | grep -c 'DietPi')
isRaspbian=$(cat /etc/os-release 2>/dev/null | grep -c 'Raspbian')
isArmbian=$(cat /etc/os-release 2>/dev/null | grep -c 'Debian')
isUbuntu=$(cat /etc/os-release 2>/dev/null | grep -c 'Ubuntu')
if [ ${isRaspbian} -gt 0 ]; then
  baseImage="raspbian"
fi
if [ ${isArmbian} -gt 0 ]; then
  baseImage="armbian"
fi 
if [ ${isUbuntu} -gt 0 ]; then
baseImage="ubuntu"
fi
if [ ${isDietPi} -gt 0 ]; then
  baseImage="dietpi"
fi
echo "baseimage=${baseImage}" >> $infoFile

################################
# STRESSTEST RASPBERRY PI
################################

if [ "${baseImage}" = "raspbian" ] ; then
  # generate stresstest report on every startup (in case hardware has changed)
  sed -i "s/^state=.*/state=stresstest/g" ${infoFile}
  sed -i "s/^message=.*/message='Testing Hardware 60s'/g" ${infoFile}
  sudo /home/admin/config.scripts/blitz.stresstest.sh /home/admin/stresstest.report
  source /home/admin/stresstest.report
  if [ "${powerWARN}" = "0" ]; then
    # https://github.com/rootzoll/raspiblitz/issues/576
    echo "" > /var/log/syslog
  fi
fi

echo "DONE BOOTSTRAP" >> $logFile
exit 0