#!/bin/bash
#
# shellcheck disable=SC1091,SC2086
#
# Copyright (c) 2023-2026 openmediavault plugin developers
#
# This file is licensed under the terms of the GNU General Public
# License version 2. This program is licensed "as is" without any
# warranty of any kind, whether express or implied.
#
# version: 1.1.0

. /usr/share/openmediavault/scripts/helper-functions

declare -i skip=0

# logging location
logDir="/var/log/"
logFile="${logDir}/omv-compose-restore.log"

_log()
{
  msg=${1}
  echo "[$(date +'%Y-%m-%d %H:%M:%S%z')] [composerestore] ${msg}" | tee -a ${logFile} >&2
}

compose="${1}"
if [ -z "${compose}" ]; then
  _log "No compose name set.  Exiting..."
  exit 10
fi
_log "compose :: ${compose}"

# Optional: restore borg archives from a specific snapshot timestamp (YYYY-MM-DD_HH-MM-SS)
snapshot_at=""
shift || true
while [ $# -gt 0 ]; do
  case "${1}" in
    --at)
      snapshot_at="${2}"
      shift 2 || true
      ;;
    *)
      shift || true
      ;;
  esac
done

# Get docker storage path
dockerStorage=$(omv_config_get "/config/services/compose/dockerStorage")
_log "Docker storage :: ${dockerStorage}"

# get podman setting
podman=$(omv_config_get "/config/services/compose/podman")
_log "podman :: ${podman}"
if [[ "${podman}" == "1" ]]; then
  export DOCKER_HOST="unix:///run/podman/podman.sock"
else
  unset DOCKER_HOST
fi

# Borg settings
export BORG_RELOCATED_REPO_ACCESS_IS_OK=yes
borg_encryption="$(omv_config_get "/config/services/compose/borgencryption")"
borg_passphrase="$(omv_config_get "/config/services/compose/borgpassphrase")"
if [ "${borg_encryption}" != "none" ] && [ -n "${borg_passphrase}" ]; then
  export BORG_PASSPHRASE="${borg_passphrase}"
fi

# Get the shared folder reference and path
sfref=$(omv_config_get "/config/services/compose/sharedfolderref")
if ! omv_isuuid "${sfref}"; then
  _log "No compose sharedfolder set."
  exit 11
fi
sfpath="$(omv_get_sharedfolder_path "${sfref}")"
if [ ! -d "${sfpath}" ]; then
  _log "Shared folder directory does not exist.  Exiting..."
  exit 12
fi
sfpath="${sfpath/%\/}"
sfpath="${sfpath//\/\//\/}"
_log "Compose file path :: ${sfpath}"

# Get the backup shared folder reference and path
sfref=$(omv_config_get "/config/services/compose/backupsharedfolderref")
if ! omv_isuuid "${sfref}"; then
  _log "No backup sharedfolder set."
  exit 13
fi
backuppath="$(omv_get_sharedfolder_path "${sfref}")"
if [ ! -d "${backuppath}" ]; then
  _log "Backup shared folder directory does not exist.  Exiting..."
  exit 14
fi
backuppath="${backuppath/%\/}"
_log "Backup path :: ${backuppath}"

if [ -n "${snapshot_at}" ]; then
  _log "Snapshot timestamp :: ${snapshot_at}"
fi
# set path for yml and env files
composepath="${sfpath}/${compose}"
yml="${composepath}/${compose}.yml"
if [ ! -f "${yml}" ]; then
  _log "Compose file '${yml}' does not exist."
  skip=1
fi
_log "Compose file :: ${yml}"

if [ ${skip} -ne 1 ]; then
  # save status
  status="$(mktemp)"
  docker compose ls --all --format json | jq -r ".[] | select(.ConfigFiles | contains(\"${yml}\")) | .Status" | tee "${status}"
  _log "status :: $(cat ${status})"

  # stop compose if running
  if grep -q "running" "${status}"; then
    omv-compose-run "${compose}" stop
  else
    _log "${compose} is not running"
  fi
fi

path="${backuppath}/${compose}"
_log "path :: ${path}"

echo
vollist="${path}/vol.list"
if [ -f "${vollist}" ]; then
  borgrepo="${path}/borgrepo"
  # If --at is set, load per-volnum archive selection from vol.list.history
  declare -A hist_archive=()
  if [ -n "${snapshot_at}" ] && [ -f "${path}/vol.list.history" ]; then
    while IFS=',' read -r ts hvol backend_h harch; do
      [ -z "${ts}" ] && continue
      [ "${ts}" = "${snapshot_at}" ] || continue
      [ "${backend_h}" = "borg" ] || continue
      hist_archive["${hvol}"]="${harch}"
    done < "${path}/vol.list.history"
    _log "Snapshot requested :: ${snapshot_at}"
  fi
  while IFS=',' read -r volnum volpath backend archive; do
    # skip empty lines
    [ -z "${volnum}" ] && continue

    # compose files (directory 0) keep original behavior
    if [ ${volnum} -eq 0 ]; then
      if [[ ! "${volpath}" == */ ]]; then
        volpath="$(dirname "${volpath}")"
      fi
      src="${path}/${volnum}/"
      volpath="${volpath}/${compose}/"
      volpath="${volpath//\/\//\/}"
      _log "Source :: ${src}"
      _log "Dest :: ${volpath}"
      cp -v "${src}/${compose}.yml" "${src}/compose.override.yml" "${src}/${compose}.env" "${volpath}"
      continue
    fi

    # If vol.list includes extended borg fields: volnum,path,borg,archive
    if [ "${backend}" = "borg" ]; then
      if [ ! -d "${borgrepo}" ]; then
        _log "Borg repo not found :: ${borgrepo}. Exiting..."
        exit 16
      fi

      # Restore destination path behavior mirrors original rsync branch:
      # - for files (no trailing / in vol.list), restore into dirname()
      # - for dirs (trailing /), restore the directory itself.
      dest_raw="${volpath}"
      if [[ ! "${dest_raw}" == */ ]]; then
        dest_dir="$(dirname "${dest_raw}")"
        item="$(basename "${dest_raw}")"
        # mimic rsync overwriting a single file by removing it first
        rm -f -- "${dest_dir}/${item}"
      else
        dest_target="${dest_raw%/}"
        dest_dir="$(dirname "${dest_target}")"
        item="$(basename "${dest_target}")"
        # Ensure delete behavior similar to rsync --delete by removing the target first
        rm -rf -- "${dest_target}"
      fi
      mkdir -p -- "${dest_dir}"

      _log "Borg repo :: ${borgrepo}"
      if [ -n "${snapshot_at}" ] && [ -n "${hist_archive[${volnum}]}" ]; then
        archive="${hist_archive[${volnum}]}"
      fi
      _log "Borg archive :: ${archive}"
      _log "Dest :: ${dest_dir}"
      (
        cd -- "${dest_dir}" || exit 1
        borg extract "${borgrepo}::${archive}"
      )
    else
      # rsync restore
      args=(-avr)
      if [[ ! "${volpath}" == */ ]]; then
        volpath="$(dirname "${volpath}")"
      else
        args+=(--delete)
      fi
      src="${path}/${volnum}/"
      _log "Source :: ${src}"
      _log "Dest :: ${volpath}"
      rsync "${args[@]}" "${src}" "${volpath}"
    fi
  done < "${vollist}"
else
  _log "No volume list found."
fi

if [ ${skip} -ne 1 ]; then
  # start compose if running before backup
  if grep -q "running" "${status}"; then
    omv-compose-run "${compose}" start
  fi

  rm -f "${status}"
else
  # add compose file to database if it doesn't exist already
  xpath="//services/compose/files"
  if ! omv_config_exists "${xpath}/file[name='${compose}']"; then
    _log "Adding '${compose}' to the database..."
    composepath="${path}/0/${compose}"
    envfile="${composepath}.env"
    ymlfile="${composepath}.yml"
    ovrfile="${path}/0/compose.override.yml"
    descfile="${path}/omv_file_desc.txt"
    envtxt=""
    ymltxt=""
    ovrtxt=""
    desctxt=""
    # escape for xml
    if [ -f "${envfile}" ]; then
      envtxt=$(sed -e 's/&/\&amp;/g' -e 's/</\&lt;/g' -e 's/>/\&gt;/g' "${envfile}")
    fi
    if [ -f "${ymlfile}" ]; then
      ymltxt=$(sed -e 's/&/\&amp;/g' -e 's/</\&lt;/g' -e 's/>/\&gt;/g' "${ymlfile}")
    fi
    if [ -f "${ovrfile}" ]; then
      ovrtxt=$(sed -e 's/&/\&amp;/g' -e 's/</\&lt;/g' -e 's/>/\&gt;/g' "${ovrfile}")
    fi
    if [ -f "${descfile}" ]; then
      desctxt=$(sed -e 's/&/\&amp;/g' -e 's/</\&lt;/g' -e 's/>/\&gt;/g' "${descfile}")
    fi
    if [ -n "${ymltxt}" ]; then
      # create backup of omv database
      date="$(date +'%Y-%m-%d_%H-%M-%S')"
      cp -fv "${OMV_CONFIG_FILE}" "/root/config_${date}.xml"
      # create database entry
      object="<uuid>$(uuid)</uuid>"
      object="${object}<name>${compose}</name>"
      object="${object}<description>@@DESC_DATA@@</description>"
      object="${object}<body>@@YAML_DATA@@</body>"
      object="${object}<showenv>0</showenv>"
      object="${object}<env>@@ENV_DATA@@</env>"
      object="${object}<showoverride>0</showoverride>"
      object="${object}<override>@@OVR_DATA@@</override>"
      # add new entry to database
      omv_config_add_node_data "${xpath}" "file" "${object}"
      # repalce temp variables with compose yaml and environment file
      omvdb=$(<"${OMV_CONFIG_FILE}")
      omvdbmod=$(
        awk \
          -v env="${envtxt}" \
          -v yml="${ymltxt}" \
          -v ovr="${ovrtxt}" \
          -v desc="${desctxt}" \
          '{
             gsub(/@@DESC_DATA@@/, desc)
             gsub(/@@ENV_DATA@@/, env)
             gsub(/@@YAML_DATA@@/, yml)
             gsub(/@@OVR_DATA@@/, ovr)
           }
           1
          ' <<< "${omvdb}"
      )
      echo "${omvdbmod}" > "${OMV_CONFIG_FILE}"
      # mark compose module dirty
      omv_module_set_dirty compose
    fi
  else
    _log "File exists in database."
  fi
fi

_log "Done."

exit 0
