#!/bin/bash

# do not run this as root
if [ "$(id -u)" = "0" ]; then
  echo "Error: Do not run this as root!"
  exit 1
fi

# check if argument is not missing
if [ -z "$1" ]; then
  echo "Error: Missing tag argument. Please provide a tag argument."
  exit 2
fi

# Check if the 'debian' user exists
if id "debian" &>/dev/null; then
    # Check if 'debian' user is in the 'sudo' group
    if groups "debian" | grep -qw "sudo"; then
        # Check if the sudoers file already exists
        if [ ! -f /etc/sudoers.d/debian_nopass ]; then
            echo "Creating no-password sudo entry for debian user..."
            # Use echo piped to sudo tee to safely create a sudoers file without using a text editor
            echo 'debian' | sudo -S sh -c "echo '%sudo ALL=(ALL) NOPASSWD: ALL' | tee /etc/sudoers.d/debian_nopass > /dev/null"
            # Set correct permissions for the sudoers file
            echo 'debian' | sudo -S chmod 0440 /etc/sudoers.d/debian_nopass
            echo "No-password sudo entry created successfully."
        fi
    fi
fi

# set options
set -e
set -o nounset
set -o errtrace
set -o pipefail

# variables and configuration
NAME="whonix"
DM="derivative-maker"
DB="derivative-binary"
BL="build-log"
VERSION="$1"
VERSION_MASTER="master"
VERSION_NUMBER="${VERSION%%-*}"
REPOSITORY_URL="https://github.com/Whonix/derivative-maker.git"
FLAVOR_GATEWAY="whonix-gateway-xfce"
FLAVOR_WORKSTATION="whonix-workstation-xfce"
TARGET="utm"
ARCH="arm64"
TB="open"
REPO="true"
SIZE_GATEWAY="15G"
SIZE_WORKSTATION="25G"
WB="whonix-binary-$VERSION"
APT="/usr/bin/apt"
GIT="/usr/bin/git"
GPG="/usr/bin/gpg"

# catching errors as they occur
function catch() {
  echo "Error Report"
  if [ "$1" != "0" ]; then
    echo "Error $1 occurred on line $2, command: '$3'"
    exit 11  # Exit on any error caught by this function
  fi
}

# SIGINT or SIGTERM
function handle_signal() {
  echo "Error: received SIGINT or SIGTERM exiting"
  exit 3
}

# start text
function beginning() {
  echo "Starting $NAME build for version $VERSION"
}

# finishing text
function bye() {
  echo "Exiting without errors."
  echo "Build function finished, please check log"
  echo "Its recommended that you verify the signing key on the repository and build, more info here:"
  echo "https://www.whonix.org/wiki/Signing_Key"
}

# safe exit of the script
function safe_exit() {
  trap bye EXIT
  exit 0
}

function check_apt_sources() {
  echo "Checking APT sources availability..."
  if ! sudo "$APT" update &> /dev/null; then
    echo "Error: APT sources are not reachable. Please check your sources.list and internet connection."
    exit 4
  fi
}

function check_connectivity() {
  local HOST="github.com"
  echo "Checking connectivity to $HOST..."
  if ! ping -c 1 -W 2 "$HOST" &> /dev/null; then
    echo "Error: Unable to reach $HOST. Please check your internet connection."
    exit 5
  fi
}

function repository_not_found() {
  echo "Error: ${HOME}/${DM} not found"
  exit 6
}

# Set up trap
trap 'catch $? $LINENO "$BASH_COMMAND"' EXIT
trap handle_signal SIGINT SIGTERM

function update() {
  # updates/upgrades and autoremove packages
  check_apt_sources
  sudo "$APT" update -y
  sudo "$APT" upgrade -y
  sudo "$APT" autoremove -y
}

function install() {
  # install dependencies
  local PACKAGES=("git" "time" "curl" "apt-cacher-ng" "lsb-release" "dpkg-dev" "fakeroot" "fasttrack-archive-keyring" "safe-rm" "extrepo-offline-data")
  # Loop through each package
  for PACKAGE in "${PACKAGES[@]}"; do
    if ! dpkg-query -W -f='${Status}' "$PACKAGE" 2>/dev/null | grep -q "install ok installed"; then
      echo "$PACKAGE is not installed. Installing..."
      sudo DEBIAN_FRONTEND=noninteractive "$APT" install -y "$PACKAGE"
    else
      echo "$PACKAGE is already installed."
    fi
  done

    # GPG Key Import Check
  local KEY_FINGERPRINT="916B8D99C38EAF5E8ADC7A2A8D66066A2EEACCDA"
  if ! gpg --list-keys "$KEY_FINGERPRINT" > /dev/null 2>&1; then
    echo "GPG key not found. Importing..."
    "$GPG" --import /usr/share/extrepo/offline-data/debian/bullseye/whonix.asc
  else
    echo "GPG key already imported."
  fi
}

function repo_down() {
  if [ ! -d "${HOME}/${DM}" ]; then
    # download git repository
    check_connectivity
    echo "Git clone $NAME repository"
    "$GIT" clone --depth=1 --branch "$VERSION_MASTER" --jobs=4 --recurse-submodules --shallow-submodules "$REPOSITORY_URL"
    cd "${HOME}/${DM}" || repository_not_found
  fi
}

function fetch_tag() {
  cd "${HOME}/${DM}" || repository_not_found

  # Verify if the current directory is a git repository
  if ! "$GIT" rev-parse --git-dir > /dev/null 2>&1; then
    echo "Error: Not a git repository. Please run this script within a git repository."
    exit 7
  fi

  if [ ! "$($GIT status --porcelain)" = "" ]; then
    echo "Error: Command git status --porcelain failed at the beginning!"
    exit 8
  fi

  # Before fetching and checking out:
  "$GIT" reset --hard
  "$GIT" clean -fdx

  # Fetch only the specific tag
  "$GIT" fetch --depth=1 origin tag "$VERSION"

  # Checkout the fetched tag
  "$GIT" checkout "$VERSION"
  "$GIT" verify-tag "$VERSION"
  "$GIT" verify-commit "${VERSION}^{commit}"
  "$GIT" verify-commit HEAD

  # Reset and clean each submodule
  "$GIT" submodule foreach --recursive 'git reset --hard && git clean -fdx'

  # Submodule operations
  "$GIT" submodule sync --recursive
  "$GIT" submodule update --init --recursive --jobs=200

  # Double-check everything is clean
  "$GIT" submodule foreach --recursive 'git reset --hard && git clean -fdx'
  
  if [ ! "$($GIT status --porcelain)" = "" ]; then
    echo "Error: Command git status --porcelain failed at the end!"
    exit 9
  fi

  # verifies correct version is downloaded
  GIT_VERSION="$($GIT describe --tags --abbrev=0)"
  if [ "$GIT_VERSION" == "$VERSION" ]; then
    "$GIT" describe
    echo "Git version matches the desired version ($VERSION)."
  else
    "$GIT" describe
    echo "Error: Git version does not match the desired version ($VERSION)."
    exit 10
  fi
}

function build() {
  cd "${HOME}/${DM}" || repository_not_found

  echo "Building gateway"
  "${HOME}/${DM}/${DM}" --flavor "$FLAVOR_GATEWAY" --target "$TARGET" --arch "$ARCH" --tb "$TB" --repo "$REPO" --vmsize "$SIZE_GATEWAY"
  
  echo "Building workstation"
  "${HOME}/${DM}/${DM}" --flavor "$FLAVOR_WORKSTATION" --target "$TARGET" --arch "$ARCH" --tb "$TB" --repo "$REPO" --vmsize "$SIZE_WORKSTATION"
}

function pack() {
  mkdir -p "${HOME}/${WB}"
  cp -vr "${HOME}/${DB}/${VERSION_NUMBER}"/*.utm.tar.gz "${HOME}/${WB}"
  echo "Build of $NAME version $VERSION is finished"
}

function main() {
  beginning
  update
  install
  repo_down
  fetch_tag
  build
  pack
} 

# Script execution starts here
echo "Starting $NAME build function"
cd "$HOME" || { echo "Error: Failed to change directory to $HOME"; exit 12; }

main 2>&1 | tee -a "${HOME}/${BL}"

# Copy the build log to the binary easy access folder and complete the script
mv "${HOME}/${BL}" "${HOME}/${WB}/" || { echo "Error: Failed to move build log"; exit 13; }

cd "$HOME" || { echo "Error: Failed to change directory to $HOME at script end"; exit 14; }

# Exit the script safely
safe_exit