#!/usr/bin/env bash
#
# deploy-arcgis-platform.sh
#
# Linux equivalent of Deploy-ArcGISPlatform.ps1.
#
# Deploys the 'arcgis#platform' web application (packaged in arcgis_mcp.zip)
# into the Tomcat container that ships with ArcGIS Server, then restarts the
# ArcGIS Server service so the new context comes online.
#
# It reads the installed version and install directory from the ESRI properties
# file (instead of the Windows registry), validates them, picks the matching
# build automatically from the detected version (v9_11 for 11.1, v9_17 for 11.3,
# v10_17 for 11.5/12.0/12.1), copies the web app and the shared context descriptor into
# place, and restarts ArcGIS Server.
#
# The '#' in 'arcgis#platform' is Tomcat's path separator, so it maps to the
# URL context /arcgis/platform.
#
# Run with sudo privileges: the ArcGIS Server install is owned by the arcgis
# account, so reading its properties and writing under framework/ need sudo.

# --------------------------- helper functions ------------------------------

# Exit with a message if the previous command's exit code ($1) is non-zero.
function signal_if_failed() {
  if [ "$1" -ne 0 ]; then
    echo "$2"
    exit "$1"
  fi
}

# Print a message and exit with a failure code.
function throw_error() {
  echo "$1"
  exit 1
}

# Locate the web.xml of the ArcGIS Web Adaptor's 'server' web app. This lives in
# a SEPARATE Tomcat (the one the Web Adaptor is deployed to, typically owned by a
# 'tomcat' account) - NOT the Tomcat bundled with ArcGIS Server. Its location is
# site-specific (e.g. /data/tomcat, /opt/tomcat_arcgis), so we discover it from
# running Tomcat processes, then the 'tomcat' account's home, then a bounded
# filesystem search - accepting a candidate only when webapps/server/WEB-INF/
# web.xml both exists AND contains the 'agswebadaptor' servlet (which uniquely
# identifies the Web Adaptor app).
function find_webadaptor_webxml() {
  local rel="webapps/server/WEB-INF/web.xml"
  local bundled="$agsserverinstalldir/framework/runtime/tomcat"
  local -a candidates=()
  local d

  # Catalina base/home dirs from any running Tomcat (Catalina) java processes.
  while IFS= read -r d; do
    [ -n "$d" ] && candidates+=("$d")
  done < <(ps -ww -eo args 2>/dev/null \
            | tr ' ' '\n' \
            | grep -E '^-Dcatalina\.(base|home)=' \
            | sed -E 's/^-Dcatalina\.(base|home)=//' \
            | sort -u)

  # Home directory of a dedicated 'tomcat' service account, if one exists.
  local tchome
  tchome=$(getent passwd tomcat 2>/dev/null | cut -d: -f6)
  [ -n "$tchome" ] && candidates+=("$tchome")

  # Return the first candidate (other than the bundled Tomcat) that holds a
  # 'server' web app whose web.xml references the agswebadaptor servlet.
  for d in "${candidates[@]}"; do
    case "$d" in "$bundled"|"$bundled"/*) continue ;; esac
    if sudo test -f "$d/$rel" && sudo grep -q 'agswebadaptor' "$d/$rel" 2>/dev/null; then
      echo "$d/$rel"
      return 0
    fi
  done

  # Last resort: a bounded filesystem search for the marker file.
  local hit
  while IFS= read -r hit; do
    [ -z "$hit" ] && continue
    case "$hit" in "$bundled"/*) continue ;; esac
    if sudo grep -q 'agswebadaptor' "$hit" 2>/dev/null; then
      echo "$hit"
      return 0
    fi
  done < <(sudo find /opt /data /usr/share /usr/local /var/lib -maxdepth 7 \
             -path "*/webapps/server/WEB-INF/web.xml" 2>/dev/null)

  return 1
}

# ------------------------------ parameters ---------------------------------

username='arcgis'        # ArcGIS Server account that owns the install.

usage() {
  cat <<USAGE
Usage: $0 [-u <username>]
  -u  ArcGIS Server account name (default: arcgis)
USAGE
}

while getopts ":u:h" opt; do
  case "$opt" in
    u) username="$OPTARG" ;;
    h) usage; exit 0 ;;
    *) usage; exit 1 ;;
  esac
done

# ------------- read ArcGIS Server details from ESRI properties -------------

# Resolve the account's home directory from /etc/passwd.
homedir=$(awk -F: -v v="$username" '{if ($1==v) print $6}' /etc/passwd)
if [ -z "$homedir" ]; then
  throw_error "Unable to fetch home directory location for user $username."
fi

# The ESRI properties file is named .ESRI.properties.<hostname>.*; take the most
# recently modified one.
propertiesfilepath=$(sudo ls -tr "$homedir"/.ESRI.properties."$(hostname)".* 2>/dev/null | tail -n 1)
sudo test -f "$propertiesfilepath"
signal_if_failed $? "ESRI properties file does not exist at path $propertiesfilepath"

# Installed ArcGIS Server version.
line=$(sudo grep -m1 '^Z_REAL_VERSION=' "$propertiesfilepath")
curragsserverversion=${line#Z_REAL_VERSION=}
if [ -z "$curragsserverversion" ]; then
  throw_error "Unable to retrieve ArcGIS Server version from ESRI properties file."
fi

# Reduce the detected version (which may be e.g. 11.3.0.12345) to Major.Minor,
# which is what the supported set and the build mapping are keyed on.
detectedversion=$(awk -F. '{print $1"."$2}' <<< "$curragsserverversion")

# The package only ships builds for these versions; anything else (older or
# newer than supported) stops here.
case "$detectedversion" in
  11.1|11.3|11.5|12.0|12.1) ;;
  *) throw_error "ArcGIS Server version $detectedversion is not supported. Supported versions: 11.1 11.3 11.5 12.0 12.1" ;;
esac
echo "Detected ArcGIS Server version: $detectedversion."

# Installed ArcGIS Server directory.
line=$(sudo grep -m1 '^Z_ArcGISServer_INSTALL_DIR=' "$propertiesfilepath")
agsserverinstalldir=${line#Z_ArcGISServer_INSTALL_DIR=}
if [ -z "$agsserverinstalldir" ]; then
  throw_error "Unable to retrieve ArcGIS Server installation directory from ESRI properties file."
fi
sudo test -d "$agsserverinstalldir"
signal_if_failed $? "Unable to locate ArcGIS Server installation directory. Expected path is $agsserverinstalldir."

echo "ArcGIS Server $curragsserverversion installed at $agsserverinstalldir"

# ----------------------- locate & extract the package ----------------------

# Folder this script lives in; the package is expected to sit beside it.
scriptdir="$(cd "$(dirname "${BASH_SOURCE[0]}")" >/dev/null 2>&1 && pwd)"
zipfilepath="$scriptdir/arcgis_mcp.zip"
if [ ! -f "$zipfilepath" ]; then
  throw_error "Unable to locate the deployment package. Expected path is $zipfilepath"
fi

command -v unzip >/dev/null 2>&1 || throw_error "'unzip' is required but is not installed."

# Extract into a dedicated scratch folder under temp, cleared first so the
# extraction is clean and the script stays idempotent.
tempfolder="${TMPDIR:-/tmp}"
extractfolder="$tempfolder/arcgis_mcp"
rm -rf "$extractfolder"
mkdir -p "$extractfolder"
unzip -q -o "$zipfilepath" -d "$extractfolder"
signal_if_failed $? "Failed to extract $zipfilepath"
echo "Extracted '$zipfilepath' to '$extractfolder'"

# Pick the package sub-folder whose 'arcgis#platform' build matches the detected
# version. v9_11 -> 11.1; v9_17 -> 11.3; v10_17 -> 11.5/12.0/12.1.
case "$detectedversion" in
  11.1)           versionfolder="v9_11"  ;;
  11.3)           versionfolder="v9_17"  ;;
  11.5|12.0|12.1) versionfolder="v10_17" ;;
esac
echo "Targeting ArcGIS Server $detectedversion -> using '$versionfolder' build."

# The web-app folder lives under the version sub-folder; the context descriptor
# sits at the package root and is shared across all versions.
sourcewebapp="$extractfolder/$versionfolder/arcgis#platform"
sourcexml="$extractfolder/arcgis#platform.xml"
if [ ! -d "$sourcewebapp" ]; then
  throw_error "Expected folder '$versionfolder/arcgis#platform' was not found in the extracted package."
fi
if [ ! -f "$sourcexml" ]; then
  throw_error "Expected file 'arcgis#platform.xml' was not found in the extracted package."
fi

# ------------------------------- deploy ------------------------------------

# Deployed files should be owned by the ArcGIS Server account so Tomcat (running
# as that account) can read and deploy them.
ownergroup="$(id -gn "$username" 2>/dev/null)"
[ -z "$ownergroup" ] && ownergroup="$username"

# Deploy the web application into ArcGIS Server's Tomcat webapps folder.
webappsfolder="$agsserverinstalldir/framework/webapps"
sudo test -d "$webappsfolder"
signal_if_failed $? "Unable to locate Tomcat webapps folder. Expected path is $webappsfolder"

destwebapp="$webappsfolder/arcgis#platform"
if sudo test -e "$destwebapp"; then
  sudo rm -rf "$destwebapp"   # remove any previous deployment for a clean copy
fi
sudo cp -r "$sourcewebapp" "$webappsfolder/"
signal_if_failed $? "Failed to copy web application into $webappsfolder"
sudo chown -R "$username":"$ownergroup" "$destwebapp"
echo "Deployed 'arcgis#platform' to '$webappsfolder'"

# Deploy the context descriptor into Tomcat's Catalina/localhost folder.
catalinafolder="$agsserverinstalldir/framework/runtime/tomcat/conf/Catalina/localhost"
sudo test -d "$catalinafolder"
signal_if_failed $? "Unable to locate Tomcat Catalina/localhost folder. Expected path is $catalinafolder"

sudo cp -f "$sourcexml" "$catalinafolder/arcgis#platform.xml"
signal_if_failed $? "Failed to copy context descriptor into $catalinafolder"
sudo chown "$username":"$ownergroup" "$catalinafolder/arcgis#platform.xml"
echo "Deployed 'arcgis#platform.xml' to '$catalinafolder'"

# ------------ add the /platform/* web adaptor mapping to web.xml ------------

# Best-effort step: if the Web Adaptor's Tomcat / web.xml can't be found, or any
# problem occurs, it logs a warning and returns so the deployment still proceeds
# to the restart. It never aborts the script.
add_platform_mapping() {
  local webxml status orig_owner orig_group orig_mode backup tmpxml

  # Auto-discover the Web Adaptor's web.xml (in its own Tomcat, not the bundled one).
  webxml="$(find_webadaptor_webxml)"
  if [ -z "$webxml" ] || ! sudo test -f "$webxml"; then
    echo "WARNING: Web Adaptor web.xml (webapps/server/WEB-INF/web.xml) not found or Tomcat not configured; skipping /platform/* mapping."
    return 0
  fi
  echo "Using Web Adaptor web.xml at $webxml"

  # Inspect the 'agswebadaptor' <servlet-mapping> block and decide what to do:
  #   NEEDS    - block exists but has no /platform/* mapping yet
  #   ALREADY  - /platform/* mapping is already present (nothing to do)
  #   NOTFOUND - no agswebadaptor servlet-mapping in the file
  status=$(sudo awk '
    /<servlet-mapping>/ { inblock=1; hasadaptor=0; hasplatform=0 }
    inblock {
      if ($0 ~ /<servlet-name>[[:space:]]*agswebadaptor[[:space:]]*<\/servlet-name>/) hasadaptor=1
      if ($0 ~ /<url-pattern>\/platform\/\*<\/url-pattern>/) hasplatform=1
    }
    /<\/servlet-mapping>/ {
      if (inblock && hasadaptor && !found) {
        print (hasplatform ? "ALREADY" : "NEEDS")
        found=1
      }
      inblock=0
    }
    END { if (!found) print "NOTFOUND" }
  ' "$webxml" | head -n 1)

  case "$status" in
    ALREADY)
      echo "web.xml already contains the /platform/* mapping; no change needed."
      ;;
    NOTFOUND)
      echo "WARNING: no 'agswebadaptor' <servlet-mapping> found in $webxml; skipping /platform/* mapping."
      ;;
    NEEDS)
      echo "Adding /platform/* mapping to the agswebadaptor servlet-mapping in web.xml..."

      # Record the original ownership and mode so they can be restored after the
      # sudo edit (the file is normally owned by the ArcGIS Server account).
      orig_owner=$(sudo stat -c '%U' "$webxml")
      orig_group=$(sudo stat -c '%G' "$webxml")
      orig_mode=$(sudo stat -c '%a' "$webxml")

      # Keep a timestamped backup beside the original for easy rollback.
      backup="$webxml.bak.$(date +%Y%m%d%H%M%S)"
      if ! sudo cp -p "$webxml" "$backup"; then
        echo "WARNING: failed to back up web.xml to $backup; skipping /platform/* mapping."
        return 0
      fi

      # Insert <url-pattern>/platform/*</url-pattern> just before the closing
      # </servlet-mapping> of the agswebadaptor block, matching sibling indent.
      tmpxml="$(mktemp)"
      if ! sudo awk '
        /<servlet-mapping>/ { inblock=1; hasadaptor=0; hasplatform=0; indent="\t\t" }
        inblock {
          if ($0 ~ /<servlet-name>[[:space:]]*agswebadaptor[[:space:]]*<\/servlet-name>/) hasadaptor=1
          if ($0 ~ /<url-pattern>\/platform\/\*<\/url-pattern>/) hasplatform=1
          if ($0 ~ /<url-pattern>/) { match($0, /^[ \t]*/); indent=substr($0, 1, RLENGTH) }
        }
        /<\/servlet-mapping>/ {
          if (inblock && hasadaptor && !hasplatform && !done) {
            print indent "<url-pattern>/platform/*</url-pattern>"
            done=1
          }
          inblock=0
        }
        { print }
      ' "$webxml" > "$tmpxml"; then
        echo "WARNING: failed to generate updated web.xml; leaving original unchanged."
        rm -f "$tmpxml"
        return 0
      fi

      # Sanity-check the result before overwriting the live file.
      if ! grep -q '<url-pattern>/platform/\*</url-pattern>' "$tmpxml"; then
        echo "WARNING: updated web.xml is missing the /platform/* mapping; leaving original unchanged."
        rm -f "$tmpxml"
        return 0
      fi

      # Overwrite in place, then restore the original ownership and permissions.
      if ! sudo cp "$tmpxml" "$webxml"; then
        echo "WARNING: failed to write updated web.xml; original left in place (backup at $backup)."
        rm -f "$tmpxml"
        return 0
      fi
      sudo chown "$orig_owner":"$orig_group" "$webxml"
      sudo chmod "$orig_mode" "$webxml"
      rm -f "$tmpxml"

      echo "Updated $webxml (backup at $backup)."
      ;;
    *)
      echo "WARNING: unexpected state while inspecting web.xml ('$status'); skipping /platform/* mapping."
      ;;
  esac
  return 0
}

# Run it as a best-effort step - it never aborts the deployment.
add_platform_mapping

# ------------------------------ restart ------------------------------------

# Restart ArcGIS Server so Tomcat deploys the new context. Prefer the install's
# stop/start scripts (run as the ArcGIS Server account); fall back to systemd.
stopscript="$agsserverinstalldir/stopserver.sh"
startscript="$agsserverinstalldir/startserver.sh"

if sudo test -f "$stopscript" && sudo test -f "$startscript"; then
  echo "Restarting ArcGIS Server via stop/start scripts..."
  sudo -u "$username" "$stopscript"
  signal_if_failed $? "Failed to stop ArcGIS Server."
  sudo -u "$username" "$startscript"
  signal_if_failed $? "Failed to start ArcGIS Server."
elif systemctl list-unit-files 2>/dev/null | grep -q '^arcgisserver'; then
  echo "Restarting ArcGIS Server via systemd..."
  sudo systemctl restart arcgisserver
  signal_if_failed $? "Failed to restart the arcgisserver service."
else
  throw_error "Unable to restart ArcGIS Server: no stop/start scripts or systemd unit found."
fi

echo "ArcGIS Server restarted successfully."
