#!/bin/bash

set +e

# duplicate log to SD card
log_path='/boot/firmware'
log_file="${1:-${log_path}/dotpi_prepare_system-$(date +"%Y%m%d-%H%M%S").log}"
# Self log
exec &> >(sudo tee -a "$log_file")

error_exit() {
  error_code="${1:-255}"
  error_context="${2:-dotpi_prepare_system}"
  exit_code="${3:-255}"
  exit_message="${4:-System is NOT READY. Please flash SD card again.}"

  dotpi echo_error "dotpi_prepare_system: ${error_context}: error ${error_code}"
  dotpi echo_error "${exit_message}"

  exit "$exit_code"
}

############### bootstrap
error_exit_code_init=1

# This script MUST be executed by `pi` user
FIRSTUSER="$(getent passwd 1000 | cut -d: -f1)"
if [[ $(whoami) != "${FIRSTUSER}" ]] ; then
   error_exit 255 "This script MUST be executed by pi user (or first user)" $error_exit_code_init
fi

DOTPI_ROOT="${DOTPI_ROOT:-/opt/dotpi}"
source "${DOTPI_ROOT}/share/dotpi_init.bash" || {
  error_exit $? "source dotpi_init.bash" $error_exit_code_init
}

####### wifi
dotpi echo_info "Configure wifi"

# set wifi country code
#
# This is also done earlier by `dotpi_firstrun` but doing this
# only in one place fail for some unknown reason
dotpi echo_info "raspi-config nonint do_wifi_country ${dotpi_wifi_country_code}"
sudo raspi-config nonint do_wifi_country "${dotpi_wifi_country_code}" || {
  error_exit $? "set wifi country code" $error_exit_code_init
}

dotpi echo_info "Update network configurations"
sudo dotpi connections_update
nmcli connection show

# unblock wifi in any case
sudo nmcli radio wifi on
sudo rfkill unblock wifi

# wait for system time update to be able to verify certificates needed by apt
dotpi echo_info "Wait for the system clock to update from network"
while [[ $(timedatectl --property NTPSynchronized --value | grep yes | wc -l) == 0 ]] ; do
  date
  sleep 5
done
timedatectl status
date

############### system packages via apt
error_exit_code_package=2

# in case dpkg was interrupted
sudo dpkg --configure -a || {
  error_exit $? dpkg $error_exit_code_package
}

declare -A apt_uninstall_packages
for package in "${dotpi_apt_uninstall_default[@]}" "${dotpi_apt_uninstall[@]}" ; do
  apt_uninstall_packages["$package"]=1
done

dotpi echo_info "Uninstall apt packages:" "${!apt_uninstall_packages[@]}"
sudo dotpi apt_get purge "${!apt_uninstall_package[@]}" || {
  error_exit $? 'apt_get purge' $error_exit_code_package
}

sudo dotpi apt_get clean  || {
  error_exit $? 'apt_get clean' $error_exit_code_package
}
sudo dotpi apt_get autoremove -y || {
  error_exit $? 'apt_get autoremove' $error_exit_code_package
}

dotpi echo_info "Update and dist-upgrade"
sudo dotpi apt_get update || {
  error_exit $? 'apt_get update' $error_exit_code_package
}
sudo dotpi apt_get dist-upgrade || {
  dotpi echo_error "Error while doing apt_get dist-upgrade"
  dotpi echo_warning "Try to fix broken configuration and retry dist-upgrade"
  sudo dpkg --configure -a --force-confdef --force-confnew

  sudo dotpi apt_get dist-upgrade || {
    error_exit $? 'apt_get dist-upgrade' $error_exit_code_package
  }
}

declare -A apt_install_packages
for package in "${dotpi_apt_install_default[@]}" "${dotpi_apt_install[@]}" ; do
  # install only packages not required to be uninstalled
  if [[ "${apt_uninstall_packages["$package"]}" == '' ]] ; then
    apt_install_packages["$package"]=1
  fi
done

dotpi echo_info "Install apt packages:" "${!apt_install_packages[@]}"
sudo dotpi apt_get install "${!apt_install_packages[@]}" || {
  error_exit $? 'apt_get install' $error_exit_code_package
}


############### node.js
error_exit_code_node=3

dotpi echo_info "Install Node.js"

word_size="$(dotpi file_word_size "$(which file)" )"
if [[ $word_size == "32" ]] ; then
   if [[ "$(uname -m)" == "armv6l" ]] ; then
      node_arch=armv6l
   else
      node_arch=armv7l
   fi
else
   node_arch=arm64
fi

# install n for sudo usage
N_PREFIX="${DOTPI_ROOT}/share/n"
sudo rm -rf -- "$N_PREFIX" || {
  error_exit $? "rm ${N_PREFIX}" $error_exit_code_node
}

{
  sudo mkdir -p -- "$N_PREFIX"
  sudo chmod a+rw "$N_PREFIX"
} || {
  error_exit $? "mkdir ${N_PREFIX}" $error_exit_code_node
}

while ! curl -L https://bit.ly/n-install -o /tmp/n-install ; do
  dotpi echo_warning "Error while retrieving n-install"
  sleep 1
  echo "trying again"
done

environment_definitions=(
  # n-install checks for N_PREFIX to not be set in .bashrc
  # "N_PREFIX='${N_PREFIX}'"

  "N_ARCH='${node_arch}'"
)

if [[ $node_arch == "armv6l" ]] ; then
  # armv6 is not supported any more in offical node.js builds
  environment_definitions+=(
    "N_NODE_MIRROR='https://unofficial-builds.nodejs.org/download/release'"
  )
fi

echo "

# dotpi: environment variables for n (node version manager)
" >> "${HOME}/.bashrc" || {
  error_exit $? "n environment in ${HOME}/.bashrc" $error_exit_code_node
}

for definition in "${environment_definitions[@]}" ; do
  cmd="export ${definition}"
  eval "$cmd"
  echo "$cmd" >> "${HOME}/.bashrc" || {
  error_exit $? "n environment in ${HOME}/.bashrc" $error_exit_code_node
}
done

# n-install checks for N_PREFIX to not be set in bashrc
# however, it must be set for the installation
while ! N_PREFIX="$N_PREFIX" bash /tmp/n-install -y -a "$node_arch" "$dotpi_node_version" ; do
  dotpi echo_warning "Error with n-install"
  sleep 1
  echo "trying again"
  sudo rm -rf -- "$N_PREFIX"
  sudo mkdir -p -- "$N_PREFIX"
done

rm -f /tmp/n-install


source_path="${N_PREFIX}/bin"
destination_path="/usr/bin"

n_path_to_symlinks() (
  cd "${N_PREFIX}/bin" || {
    error_exit $? "cd ${N_PREFIX}/bin" $error_exit_code_node
  }

  for executable in * ; do
      sudo ln -s -f "${source_path}/${executable}" "${destination_path}/${executable}" || {
        error_exit $? "symlink to ${destination_path}/${executable}" $error_exit_code_node
      }
  done
)

# bootstrap: node, npm
n_path_to_symlinks

# pnpm is mandatory for node modules
for package in "${dotpi_node_install_default[@]}" "${dotpi_node_install[@]}" ; do
  npm -g install "$package" || {
  error_exit $? "npm install ${package}" $error_exit_code_node
}
done

# update: pnpm
n_path_to_symlinks

############## services and modules
error_exit_code_services=4

dotpi echo_info "Enable linger to run user services without login"

sudo loginctl enable-linger "$FIRSTUSER" || {
  error_exit $? "loginctl enable-linger" $error_exit_code_services
}

############# dotpi module manager

# # bootstrap-install
module_name="@dotpi/module-module"
source_path="${DOTPI_ROOT}/dist/${module_name}"
destination_prefix="${DOTPI_ROOT}/lib"
destination_path="${destination_prefix}/node_modules/${module_name}"

{
  cd "$destination_prefix" || {
    error_exit $? "cd ${destination_prefix}" $error_exit_code_services
  }

  sudo npx pnpm install --dangerously-allow-all-builds -- \
    "$module_name" || {
    error_exit $? "dotpi_module: error when installing '${module_name}'" $error_exit_code_services
  }
}

############## audio
error_exit_code_audio=6

# @TODO: @dotpi/audio

dotpi echo_info "Configure audio"

# set alsa default output volume and unmute
sudo dotpi audio_device_init || {
    dotpi echo_warning "Error while setting default output volume and unmute for ${dotpi_audio_device} device"
    $error_exit_code_audio
  }

# over-ride calls to jack, to use pipewire instead
# copy with prefix 000- to ensure it is loaded before default aarch64-linux-gnu.conf
for f in /usr/share/doc/pipewire/examples/ld.so.conf.d/pipewire-jack-*.conf ; do
  source_basename="$(basename "$f")"
  destination_basename="000-${source_basename}"
  sudo cp "$f" "/etc/ld.so.conf.d/${destination_basename}" || {
    error_exit $? "install ${f}" $error_exit_code_audio
  }
done
sudo ldconfig || {
  error_exit $? "ldconfig" $error_exit_code_audio
}

# settings over-rides
source_path="${DOTPI_ROOT}/share/wireplumber"
destination_path='/usr/share/wireplumber'
if [ -r "$source_path" ] ; then
   sudo mkdir -p "$destination_path"
   tar c -C "$source_path" '.' | sudo tar x -C "$destination_path"
   exit_code=$?
   pipe_status="${PIPESTATUS[0]}"
   if (( exit_code || pipe_status )) ; then
      if (( exit_code )) ; then
         dotpi echo_error "Error code: $exit_code"
      fi

      if (( pipe_status )) ; then
         dotpi echo_error "Pipe status: $pipe_status"
      fi

      error_exit "$((exit_code ? exit_code : pipe_status))"  "Error while copying ${source_path} to ${destination_path}" $error_exit_code_audio
   fi
fi

systemctl --user enable --now wireplumber.service || {
  dotpi echo_warning "error while enabling wireplumber.service now: ${?}"
  dotpi echo_warning "wireplumber.service will be available after a reboot"
  # do not exit
}

"${DOTPI_ROOT}/share/dotpi_pipewire/dotpi_install" || {
  dotpi echo_error "error while installing dotpi_pipewire: ${?}"
  # do not exit
}

############## dotpi modules
error_exit_code_dotpi_modules=10


declare -A dotpi_module_uninstall_modules
for module in "${dotpi_module_uninstall_default[@]}" "${dotpi_module_uninstall[@]}" ; do
  dotpi_module_uninstall_modules["$module"]=1
done

dotpi echo_info "Uninstall dotpi modules packages:" "${!dotpi_module_uninstall_modules[@]}"
sudo dotpi module uninstall --verbose full "${!dotpi_module_uninstall_modules[@]}"

declare -A dotpi_module_install_modules
for module in "${dotpi_module_install_default[@]}" "${dotpi_module_install[@]}" ; do
  # install only modules not required to be uninstalled
  if [[ "${dotpi_module_uninstall_modules["$module"]}" == '' ]] ; then
    dotpi_module_install_modules["$module"]=1
  fi
done

dotpi echo_info "Install dotpi modules packages:" "${!dotpi_module_install_modules[@]}"
sudo dotpi module install --verbose full "${!dotpi_module_install_modules[@]}"

############## finalisation

dotpi echo_info "System prepared."
dotpi echo_info "Rebooting..."

sudo reboot

