#!/bin/bash

## wiki-vbox-version-update - Find new versions of VirtualBox and update the
##   links in the wiki accordingly.

set -o errexit
set -o nounset
set -o pipefail
set -o errtrace
shopt -s inherit_errexit
shopt -s shift_verbose

true "INFO: Currently running script: ${BASH_SOURCE[0]} $*"

MYDIR="$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" && pwd )"

if [ "$MYDIR" = "/usr/bin" ]; then
   true "INFO: Run from: /usr/bin"
   ## XXX: hardcoded path
   derivative_maker_source_code_dir="$HOME/derivative-maker"
else
   true "INFO: Run from: source code folder"
   derivative_maker_source_code_dir="$(cd -- "$MYDIR" && cd -- "../../../../../" && pwd)"
fi

## required by: make-helper-one.bsh
cd -- "$derivative_maker_source_code_dir"

# shellcheck source=../../../helper-scripts/usr/libexec/helper-scripts/has.sh
source "${HELPER_SCRIPTS_PATH:-}"/usr/libexec/helper-scripts/has.sh
# shellcheck source=../../../helper-scripts/usr/libexec/helper-scripts/log_run_die.sh
source "${HELPER_SCRIPTS_PATH:-}"/usr/libexec/helper-scripts/log_run_die.sh

for dep_tool in mw-fetch mw-edit stecho stsponge overwrite str_replace; do
  has "${dep_tool}" || die 1 "'${dep_tool}' not on PATH"
done

declare -A url_cache
declare -A wiki_cache

get_url_cached () {
   local url

   url="${1:-}"
   [ -z "${url}" ] && return 1

   if [ -z "${url_cache["${url}"]:-}" ]; then
      url_cache["${url}"]="$(2>/dev/null scurl "${url}")"
   fi

   url_content="${url_cache["${url}"]}"
}

get_wiki_page_cached () {
   local wiki_page cache_id tmpfile

   wiki_page="${1:-}"
   [ -z "${wiki_page}" ] && return 1

   cache_id="${WIKI_URL} - ${wiki_page}"

   ## 'mktemp' inside the cache-miss branch only: on a cache hit (every repeat
   ## lookup) we never fetch, so creating a temp file then would just leak it.
   if [ -z "${wiki_cache["${cache_id}"]:-}" ]; then
      tmpfile="$(mktemp)"
      mw-fetch "${WIKI_URL}" "${wiki_page}" "${tmpfile}"
      wiki_cache["${cache_id}"]="$(cat -- "${tmpfile}")"
      ## Leftover temp file is by design - kept on disk so a
      ## developer can inspect it after a failure.
      printf '%s\n' "INFO: get_wiki_page_cached tmp file kept for review: ${tmpfile}"
   fi

   wiki_content="${wiki_cache["${cache_id}"]}"
}

# TODO: It might be more lightweight to use release-monitoring.org to detect
#   the latest VirtualBox release.
# get_latest_version-virtualbox () {
#    get_url_cached 'https://release-monitoring.org/api/v2/packages/?name=Virtualbox'
#    jq '.items[] | select ( contains({distribution: "Windows"}) ) | .version' <<< "${url_content}"
# }

extract_url () {
   local content regex line match

   content="${1:-}"
   regex="${2:-}"
   [ -z "${content}" ] && return 1
   [ -z "${regex}" ] && return 1

   ## Emit EVERY matching URL, one per line, so callers can tell apart no
   ## match, exactly one, and several (ambiguous).
   while read -r line; do
      if [[ "${line}" =~ ${regex} ]]; then
         stecho "$(perl -p -e "s/.*(${regex}).*/\1/" <<< "${line}")"
      fi
   done <<< "${content}"
}

get_url_from_page () {
   local dl_page release_regex line result

   dl_page="${1:-}"
   release_regex="${2:-}"
   [ -z "${dl_page}" ] && return 1
   [ -z "${release_regex}" ] && return 1

   get_url_cached "${dl_page}"
   readarray -t result < <(extract_url "${url_content}" "${release_regex}")

   if [ "${#result[@]}" -eq 0 ]; then
      1>&2 stecho 'WARNING - no URL found!'
   elif [ "${#result[@]}" -gt 1 ]; then
      1>&2 stecho 'WARNING - more than one URL found for download!'
   fi

   func_result="${result[0]:-}"
}

get_url_from_wiki () {
   local wiki_page release_regex content result

   wiki_page="${1:-}"
   release_regex="${2:-}"
   [ -z "${wiki_page}" ] && return 1
   [ -z "${release_regex}" ] && return 1

   get_wiki_page_cached "${wiki_page}"
   readarray -t result < <(extract_url "${wiki_content}" "${release_regex}")

   if [ "${#result[@]}" -eq 0 ]; then
      1>&2 stecho 'WARNING - no URL found!'
   elif [ "${#result[@]}" -gt 1 ]; then
      1>&2 stecho 'WARNING - more than one URL found for download!'
   fi

   func_result="${result[0]:-}"
}

replace_wiki_string () {
   local wiki_page orig_string new_string cache_id tmpfile

   wiki_page="${1:-}"
   orig_string="${2:-}"
   new_string="${3:-}"
   [ -z "${wiki_page}" ] && return 1
   [ -z "${orig_string}" ] && return 1
   [ -z "${new_string}" ] && return 1

   cache_id="${WIKI_URL} - ${wiki_page}"
   wiki_content="${wiki_cache["${cache_id}"]}"
   [ -z "${wiki_content}" ] && return 1

   tmpfile="$(mktemp)"
   ## 'overwrite', not 'append-once': the tmpfile is a fresh empty mktemp, and
   ## 'append-once' would prepend a newline to it (its "fix a missing
   ## terminating newline" path treats the empty file as needing one), leaving
   ## a blank line at the top of the wiki page. 'overwrite' just writes.
   overwrite "${tmpfile}" "${wiki_content}" >/dev/null
   str_replace "${orig_string}" "${new_string}" "${tmpfile}"
   wiki_content="$(cat -- "${tmpfile}")"
   wiki_cache["${cache_id}"]="${wiki_content}"

   ## Leftover temp file is by design - kept on disk so a developer
   ## can inspect it after a failure.
   printf '%s\n' "INFO: replace_wiki_string tmp file kept for review: ${tmpfile}"
}

# TODO: Templates for future work
# get_latest_version-tor-browser () {
#    true
# }
#
# get_latest_version-tor-browser-alpha () {
#    true
# }
#
# get_current_version-tor-browser () {
#    true
# }
#
# get_current_version-tor-browser-alpha () {
#    true
# }

save_cached_page_to_wiki () {
   local wiki_page tmpfile

   wiki_page="${1:-}"
   [ -z "${wiki_page}" ] && return 1

   tmpfile="$(mktemp)"

   get_wiki_page_cached "${wiki_page}"
   ## 'overwrite', not 'append-once' -- see replace_wiki_string.
   overwrite "${tmpfile}" "${wiki_content}" >/dev/null

   mw-edit "${WIKI_URL}" "${tmpfile}" "${wiki_page}"

   ## Leftover temp file is by design - kept on disk so a developer
   ## can inspect it after a failure.
   printf '%s\n' "INFO: save_cached_page_to_wiki tmp file kept for review: ${tmpfile}"
}

declare wiki_vbox_page vbox_download_page vbox_win_regex vbox_macos_intel64_regex vbox_macos_arm64_regex
wiki_vbox_page='Template:VirtualBox_Host_Software_Installation'
vbox_download_page='https://www.virtualbox.org/wiki/Downloads'
vbox_win_regex='https:\/\/download.virtualbox.org\/virtualbox\/.*\/VirtualBox-.*Win\.exe'
vbox_macos_intel64_regex='https:\/\/download.virtualbox.org\/virtualbox\/.*\/VirtualBox-.*OSX.dmg'
vbox_macos_arm64_regex='https:\/\/download.virtualbox.org\/virtualbox\/.*\/VirtualBox-.*macOSArm64.dmg'

## The VirtualBox host software installation template is mirrored on both
## wikis. The page title is identical; only the domain differs. Each wiki is
## fetched, compared and updated independently.
declare -a wiki_url_list=(
   'https://www.kicksecure.com/w'
   'https://www.whonix.org/w'
)

main () {
   local vbox_win_latest vbox_macos_intel64_latest vbox_macos_arm64_latest \
      vbox_win_current vbox_macos_intel64_current vbox_macos_arm64_current \
      update_done sha_link_root sha_link_full WIKI_URL

#    if [ ! -v WIKI_API_USER_NAME ]; then
#       stecho "ERROR: No username provided! Please ensure WIKI_API_USER_NAME is set in the environment."
#       exit 1
#    elif [ ! -v WIKI_API_USER_PASS ]; then
#       stecho "ERROR: No user password provided! Please ensure WIKI_API_USER_PASS is set in the environment."
#       exit 1
#    fi

   get_url_from_page "${vbox_download_page}" "${vbox_win_regex}"; vbox_win_latest="${func_result}"
   get_url_from_page "${vbox_download_page}" "${vbox_macos_intel64_regex}"; vbox_macos_intel64_latest="${func_result}"
   get_url_from_page "${vbox_download_page}" "${vbox_macos_arm64_regex}"; vbox_macos_arm64_latest="${func_result}"

   ## Fail fast if the download page scrape came up empty (get_url_from_page
   ## only warns on a miss). Without this, an empty 'latest' URL flows into the
   ## local-file writes at the end of main -- 'dirname ""' -> '.', download.txt
   ## truncated to empty, SHA256SUMS clobbered from a bogus fetch -- corrupting
   ## the tracked files. A broken scrape must stop here, before any write.
   [ -n "${vbox_win_latest}" ]           || die 1 "no latest Windows VirtualBox URL scraped from '${vbox_download_page}'"
   [ -n "${vbox_macos_intel64_latest}" ] || die 1 "no latest macOS/Intel VirtualBox URL scraped from '${vbox_download_page}'"
   [ -n "${vbox_macos_arm64_latest}" ]   || die 1 "no latest macOS/ARM VirtualBox URL scraped from '${vbox_download_page}'"

   ## The same links are mirrored on every wiki in 'wiki_url_list'. Each wiki
   ## is fetched, compared and (if changed) saved independently, because its
   ## current page content is cached per wiki (the cache key includes
   ## WIKI_URL).
   for WIKI_URL in "${wiki_url_list[@]}"; do
      update_done='false'

      get_url_from_wiki "${wiki_vbox_page}" "${vbox_win_regex}"; vbox_win_current="${func_result}"
      get_url_from_wiki "${wiki_vbox_page}" "${vbox_macos_intel64_regex}"; vbox_macos_intel64_current="${func_result}"
      get_url_from_wiki "${wiki_vbox_page}" "${vbox_macos_arm64_regex}"; vbox_macos_arm64_current="${func_result}"

      ## Each replace is guarded: replace_wiki_string returns non-zero when the
      ## current URL is not present on the page (nothing to replace). If called
      ## as a bare command, it would abort the whole run under 'errexit' -- and
      ## kill the other links, the other wiki, and the local file writes below.
      ## Warn and carry on instead, and only mark the page dirty on success.
      if [ "${vbox_win_latest}" != "${vbox_win_current}" ]; then
         if replace_wiki_string "${wiki_vbox_page}" "${vbox_win_current}" "${vbox_win_latest}"; then
            update_done='true'
         else
            1>&2 stecho "WARNING: could not replace the Windows link on ${WIKI_URL} (current URL not found on the page); skipping it."
         fi
      fi
      if [ "${vbox_macos_intel64_latest}" != "${vbox_macos_intel64_current}" ]; then
         if replace_wiki_string "${wiki_vbox_page}" "${vbox_macos_intel64_current}" "${vbox_macos_intel64_latest}"; then
            update_done='true'
         else
            1>&2 stecho "WARNING: could not replace the macOS/Intel link on ${WIKI_URL} (current URL not found on the page); skipping it."
         fi
      fi
      if [ "${vbox_macos_arm64_latest}" != "${vbox_macos_arm64_current}" ]; then
         if replace_wiki_string "${wiki_vbox_page}" "${vbox_macos_arm64_current}" "${vbox_macos_arm64_latest}"; then
            update_done='true'
         else
            1>&2 stecho "WARNING: could not replace the macOS/ARM link on ${WIKI_URL} (current URL not found on the page); skipping it."
         fi
      fi

      if [ "${update_done}" = 'true' ]; then
         save_cached_page_to_wiki "${wiki_vbox_page}"
      fi
   done

   sha_link_root="$(dirname -- "${vbox_win_latest}")"
   ## example sha_link_full
   ## https://download.virtualbox.org/virtualbox/7.2.2/SHA256SUMS
   sha_link_full="${sha_link_root}/SHA256SUMS"

   ## vbox_win_latest example:
   ## https://download.virtualbox.org/virtualbox/7.2.2/VirtualBox-7.2.2-170484-Win.exe
   overwrite "${derivative_maker_source_code_dir}/windows/virtualbox/download.txt" "${vbox_win_latest}"
   ## Useful as audit trail. Not functionally required.
   overwrite "${derivative_maker_source_code_dir}/windows/virtualbox/weblink_SHA256SUMS" "${sha_link_full}"

   scurl --silent "${sha_link_full}" | stsponge "${derivative_maker_source_code_dir}/windows/virtualbox/SHA256SUMS"

   true "SUCCESS"
}

main "$@"
