#!/usr/bin/env bash

set -euo pipefail

export LANG=C.UTF-8
export LC_ALL=C.UTF-8

usage() {
  cat <<'EOF'
Usage:
  bash install-ezyplatform.sh [mysql_root_password] [ezyplatform_download_url]
  sudo bash install-ezyplatform.sh [mysql_root_password] [ezyplatform_download_url]
  sudo MYSQL_ROOT_PASSWORD='RootPassword123!' bash install-ezyplatform.sh
  sudo DB_PASSWORD='AppPassword123!' bash install-ezyplatform.sh

Example:
  bash install-ezyplatform.sh
  sudo bash install-ezyplatform.sh
  sudo bash install-ezyplatform.sh 'RootPassword123!'
  sudo bash install-ezyplatform.sh 'RootPassword123!' 'https://ezyplatform.com/api/v1/platforms/latest/download'
  sudo bash install-ezyplatform.sh 'https://ezyplatform.com/api/v1/platforms/latest/download'

Environment variables:
  DB_NAME             Default: ezyplatform
  DB_USER             Default: ezyplatform
  DB_PASSWORD         Default: random 24-character password
  MYSQL_ROOT_PASSWORD Default: argument 1 if provided, otherwise unchanged
  EZYPLATFORM_DIR     Default: current directory + /ezyplatform
  EZYPLATFORM_ZIP     Default: current directory + /ezyplatform.zip
  EZYPLATFORM_URL     Default: positional URL argument if provided, otherwise
                      https://ezyplatform.com/api/v1/platforms/latest/download
  EZYPLATFORM_ADMIN_PORT Default: 9090
  EZYPLATFORM_WEB_PORT   Default: 8080
  ADMIN_DOMAIN           Optional. Public domain for the admin service, e.g.
                         admin.example.com. Point its DNS A record at this
                         server before running the script. When set, an
                         Nginx site and TLS certificate are configured for
                         it, and 'admin_url' is stored in the database.
  WEB_DOMAIN             Optional. Public domain for the web service, e.g.
                         www.example.com. Point its DNS A record at this
                         server before running the script. When set, an
                         Nginx site and TLS certificate are configured for
                         it, and 'web_url' / 'websocket_url' are stored in
                         the database.
  CERTBOT_EMAIL          Optional. Contact email passed to Certbot when
                         requesting certificates for ADMIN_DOMAIN/WEB_DOMAIN.

What this script does:
  - Installs OpenJDK 8 (or OpenJDK 11 if 8 is unavailable), MySQL, Nginx, Certbot, unzip, wget
  - Optionally configures MySQL root password if provided
  - Creates MySQL database/user for EzyPlatform with a DB password from DB_PASSWORD or random_password()
  - Downloads and extracts EzyPlatform
  - Writes settings/setup.properties for MySQL
  - Starts EzyPlatform admin and web
  - Installs a systemd watchdog service that auto-restarts EzyPlatform admin
    if it stops responding, using the same check-script + systemd-unit
    mechanism as EzySupport's auto-restart feature
  - When ADMIN_DOMAIN/WEB_DOMAIN are set: configures an Nginx site and
    requests a Certbot TLS certificate for each domain, then writes
    admin_url/web_url/websocket_url into the ezy_settings table

What this script does NOT do:
  - Configure Nginx or request a TLS certificate for domains that are not
    provided via ADMIN_DOMAIN/WEB_DOMAIN (IP:port access is used instead)
  - Complete the web-based EzyPlatform admin/web setup
EOF
}

if [[ "${1:-}" == "-h" || "${1:-}" == "--help" ]]; then
  usage
  exit 0
fi

if [[ "${EUID}" -ne 0 ]]; then
  if ! command -v sudo >/dev/null 2>&1; then
    echo "This script needs root privileges and could not find sudo."
    exit 1
  fi

  echo "Root privileges are required. Re-running with sudo..."
  exec sudo -E bash "$0" "$@"
fi

random_password() {
  local length="${1:-24}"
  local password=""

  while [[ "${#password}" -lt "${length}" ]]; do
    password+="$(
      tr -dc 'A-Za-z0-9' </dev/urandom | head -c "${length}" || true
    )"
  done

  printf '%s' "${password:0:length}"
}

resolve_db_password() {
  local setup_file="${EZYPLATFORM_DIR}/settings/setup.properties"
  local existing_password=""

  if [[ -f "${setup_file}" ]]; then
    existing_password="$(
      sed -n 's/^datasource\.password=//p' "${setup_file}" | head -n 1
    )"
  fi

  if [[ -n "${existing_password}" && "${existing_password}" != "12345678" ]]; then
    printf '%s' "${existing_password}"
    return 0
  fi

  random_password
}

ARG1="${1:-}"
ARG2="${2:-}"
POSITIONAL_MYSQL_ROOT_PASSWORD=""
POSITIONAL_EZYPLATFORM_URL=""

if [[ -n "${ARG1}" && "${ARG1}" =~ ^https?:// ]]; then
  POSITIONAL_EZYPLATFORM_URL="${ARG1}"
else
  POSITIONAL_MYSQL_ROOT_PASSWORD="${ARG1}"
  POSITIONAL_EZYPLATFORM_URL="${ARG2}"
fi

MYSQL_ROOT_PASSWORD="${MYSQL_ROOT_PASSWORD:-${POSITIONAL_MYSQL_ROOT_PASSWORD}}"
CURRENT_DIR="$(pwd -P)"
EZYPLATFORM_DIR="${EZYPLATFORM_DIR:-${CURRENT_DIR}/ezyplatform}"
EZYPLATFORM_ZIP="${EZYPLATFORM_ZIP:-${CURRENT_DIR}/ezyplatform.zip}"
DB_PASSWORD="${DB_PASSWORD:-$(resolve_db_password)}"
DB_NAME="${DB_NAME:-ezyplatform}"
DB_USER="${DB_USER:-ezyplatform}"
DEFAULT_EZYPLATFORM_URL="https://ezyplatform.com/api/v1/platforms/latest/download"
EZYPLATFORM_URL="${EZYPLATFORM_URL:-${POSITIONAL_EZYPLATFORM_URL:-$DEFAULT_EZYPLATFORM_URL}}"
EZYPLATFORM_ADMIN_PORT="${EZYPLATFORM_ADMIN_PORT:-9090}"
EZYPLATFORM_WEB_PORT="${EZYPLATFORM_WEB_PORT:-8080}"
ADMIN_DOMAIN="${ADMIN_DOMAIN:-}"
WEB_DOMAIN="${WEB_DOMAIN:-}"
CERTBOT_EMAIL="${CERTBOT_EMAIL:-}"
JAVA_HOME_PATH=""
EZYPLATFORM_ALREADY_EXISTS=0

log() {
  printf '\n[%s] %s\n' "$(date '+%Y-%m-%d %H:%M:%S')" "$1"
}

require_command() {
  if ! command -v "$1" >/dev/null 2>&1; then
    echo "Missing required command: $1"
    exit 1
  fi
}

is_package_installed() {
  dpkg -s "$1" >/dev/null 2>&1
}

is_package_available() {
  apt-cache show "$1" >/dev/null 2>&1
}

has_working_java() {
  command -v java >/dev/null 2>&1 && java -version >/dev/null 2>&1
}

is_port_9090_listening() {
  ss -ltn 2>/dev/null | awk '{print $4}' | grep -Eq "(^|:)${EZYPLATFORM_ADMIN_PORT}\$"
}

is_port_listening() {
  local port="$1"
  ss -ltn 2>/dev/null | awk '{print $4}' | grep -Eq "(^|:)${port}\$"
}

wait_for_mysql() {
  for _ in $(seq 1 30); do
    if mysqladmin ping --silent >/dev/null 2>&1 || systemctl is-active --quiet mysql; then
      return 0
    fi
    sleep 2
  done

  echo "MySQL did not become ready in time."
  exit 1
}

mysql_exec_root_socket() {
  mysql --protocol=socket -uroot -e "$1"
}

mysql_exec_root_password() {
  if [[ -z "${MYSQL_ROOT_PASSWORD}" ]]; then
    return 1
  fi

  mysql --protocol=socket -uroot -p"${MYSQL_ROOT_PASSWORD}" -e "$1"
}

mysql_exec_root_default() {
  mysql -e "$1"
}

mysql_exec_root_auto() {
  if mysql_exec_root_password "$1" >/dev/null 2>&1; then
    mysql_exec_root_password "$1"
    return 0
  fi

  if mysql_exec_root_socket "$1" >/dev/null 2>&1; then
    mysql_exec_root_socket "$1"
    return 0
  fi

  if mysql_exec_root_default "$1" >/dev/null 2>&1; then
    mysql_exec_root_default "$1"
    return 0
  fi

  return 1
}

configure_mysql_root_password() {
  if mysql_exec_root_password "SELECT 1;" >/dev/null 2>&1; then
    return 0
  fi

  if [[ -z "${MYSQL_ROOT_PASSWORD}" ]]; then
    log "MySQL root password not provided, keeping current root authentication method"
    return 0
  fi

  log "Configuring MySQL root password"

  if mysql_exec_root_socket "SELECT 1;" >/dev/null 2>&1 || mysql_exec_root_default "SELECT 1;" >/dev/null 2>&1; then
    if mysql_exec_root_auto "ALTER USER 'root'@'localhost' IDENTIFIED BY '${MYSQL_ROOT_PASSWORD}'; FLUSH PRIVILEGES;" >/dev/null 2>&1; then
      return 0
    fi

    log "Could not set MySQL root password automatically, continuing with current root authentication method"
    return 0
  fi

  echo "Unable to connect to MySQL as root by socket or provided password."
  exit 1
}

create_database_and_user() {
  log "Creating MySQL database and user"
  mysql_exec_root_auto "CREATE DATABASE IF NOT EXISTS \`${DB_NAME}\` DEFAULT CHARACTER SET utf8 COLLATE utf8_bin;"
  mysql_exec_root_auto "CREATE USER IF NOT EXISTS '${DB_USER}'@'localhost' IDENTIFIED BY '${DB_PASSWORD}';"
  mysql_exec_root_auto "ALTER USER '${DB_USER}'@'localhost' IDENTIFIED BY '${DB_PASSWORD}';"
  mysql_exec_root_auto "GRANT ALL PRIVILEGES ON \`${DB_NAME}\`.* TO '${DB_USER}'@'localhost'; FLUSH PRIVILEGES;"
}

install_packages() {
  log "Updating apt package index"
  apt-get update

  local packages_to_install=()
  local java_package=""

  if has_working_java; then
    log "Java is already installed, skipping"
  else
    if is_package_installed openjdk-8-jre-headless || is_package_installed openjdk-11-jre-headless; then
      log "Java package appears installed but 'java' is not runnable; reinstalling Java"
    fi

    if is_package_available openjdk-8-jre-headless; then
      java_package="openjdk-8-jre-headless"
    elif is_package_available openjdk-11-jre-headless; then
      java_package="openjdk-11-jre-headless"
    else
      echo "Could not find openjdk-8-jre-headless or openjdk-11-jre-headless in apt repositories."
      exit 1
    fi

    log "Selected Java package: ${java_package}"
    packages_to_install+=("${java_package}")
  fi

  if command -v mysql >/dev/null 2>&1 || is_package_installed mysql-server; then
    log "MySQL is already installed, skipping"
  else
    packages_to_install+=(mysql-server)
  fi

  if command -v nginx >/dev/null 2>&1 || is_package_installed nginx; then
    log "Nginx is already installed, skipping"
  else
    packages_to_install+=(nginx)
  fi

  if command -v certbot >/dev/null 2>&1 || is_package_installed certbot; then
    log "Certbot is already installed, skipping"
  else
    packages_to_install+=(certbot)
  fi

  if is_package_installed python3-certbot-nginx; then
    log "python3-certbot-nginx is already installed, skipping"
  else
    packages_to_install+=(python3-certbot-nginx)
  fi

  if ! command -v unzip >/dev/null 2>&1 && ! is_package_installed unzip; then
    packages_to_install+=(unzip)
  fi

  if ! command -v wget >/dev/null 2>&1 && ! is_package_installed wget; then
    packages_to_install+=(wget)
  fi

  if [[ ${#packages_to_install[@]} -eq 0 ]]; then
    log "All required packages are already installed"
    return 0
  fi

  log "Installing missing packages: ${packages_to_install[*]}"
  DEBIAN_FRONTEND=noninteractive apt-get install -y "${packages_to_install[@]}"
}

configure_java_home() {
  log "Configuring JAVA_HOME"

  local java_bin=""
  local candidate=""

  if [[ -n "${JAVA_HOME:-}" && -x "${JAVA_HOME}/bin/java" ]]; then
    JAVA_HOME_PATH="${JAVA_HOME}"
  fi

  if [[ -z "${JAVA_HOME_PATH}" ]]; then
    java_bin="$(readlink -f "$(command -v java 2>/dev/null)" 2>/dev/null || true)"
    if [[ -n "${java_bin}" ]]; then
      candidate="$(dirname "$(dirname "${java_bin}")")"
      if [[ -x "${candidate}/bin/java" ]]; then
        JAVA_HOME_PATH="${candidate}"
      elif [[ "$(basename "${candidate}")" == "jre" ]]; then
        candidate="$(dirname "${candidate}")"
        if [[ -x "${candidate}/bin/java" ]]; then
          JAVA_HOME_PATH="${candidate}"
        fi
      fi
    fi
  fi

  if [[ -z "${JAVA_HOME_PATH}" ]] && command -v update-alternatives >/dev/null 2>&1; then
    java_bin="$(update-alternatives --list java 2>/dev/null | head -n 1 || true)"
    if [[ -n "${java_bin}" ]]; then
      candidate="$(dirname "$(dirname "${java_bin}")")"
      if [[ -x "${candidate}/bin/java" ]]; then
        JAVA_HOME_PATH="${candidate}"
      elif [[ "$(basename "${candidate}")" == "jre" ]]; then
        candidate="$(dirname "${candidate}")"
        if [[ -x "${candidate}/bin/java" ]]; then
          JAVA_HOME_PATH="${candidate}"
        fi
      fi
    fi
  fi

  if [[ -z "${JAVA_HOME_PATH}" ]]; then
    for candidate in /usr/lib/jvm/*; do
      if [[ -x "${candidate}/bin/java" ]]; then
        JAVA_HOME_PATH="${candidate}"
        break
      fi
    done
  fi

  if [[ -z "${JAVA_HOME_PATH}" || ! -x "${JAVA_HOME_PATH}/bin/java" ]]; then
    echo "Could not determine a valid JAVA_HOME. Please set JAVA_HOME before running this script."
    exit 1
  fi

  cat >/etc/profile.d/ezyplatform-java.sh <<EOF
export JAVA_HOME=${JAVA_HOME_PATH}
export PATH=\$JAVA_HOME/bin:\$PATH
EOF

  chmod 0644 /etc/profile.d/ezyplatform-java.sh
}

download_and_extract_ezyplatform() {
  if [[ -f "${EZYPLATFORM_DIR}/settings/setup.properties" ]]; then
    EZYPLATFORM_ALREADY_EXISTS=1
    log "EzyPlatform directory already exists at ${EZYPLATFORM_DIR}, skipping download and extraction"
    return 0
  fi

  log "Downloading EzyPlatform package"
  rm -f "${EZYPLATFORM_ZIP}"
  wget -O "${EZYPLATFORM_ZIP}" "${EZYPLATFORM_URL}"

  log "Extracting EzyPlatform to ${EZYPLATFORM_DIR}"
  rm -rf "${EZYPLATFORM_DIR}"
  mkdir -p "${EZYPLATFORM_DIR}"
  unzip -oq "${EZYPLATFORM_ZIP}" -d "${EZYPLATFORM_DIR}"

  if [[ -d "${EZYPLATFORM_DIR}/ezyplatform" ]]; then
    local extracted_dir
    extracted_dir="${EZYPLATFORM_DIR}/ezyplatform"
    find "${extracted_dir}" -mindepth 1 -maxdepth 1 -exec mv {} "${EZYPLATFORM_DIR}/" \;
    rmdir "${extracted_dir}"
  fi

  if [[ ! -f "${EZYPLATFORM_DIR}/settings/setup.properties" ]]; then
    echo "Could not find ${EZYPLATFORM_DIR}/settings/setup.properties after extraction."
    exit 1
  fi
}

write_setup_properties() {
  if is_port_9090_listening; then
    log "Port ${EZYPLATFORM_ADMIN_PORT} is already in use, assuming admin is already running"
  fi

  if [[ "${EZYPLATFORM_ALREADY_EXISTS}" -eq 1 ]]; then
    log "Existing EzyPlatform setup detected, skipping setup.properties update"
    return 0
  fi

  log "Writing EzyPlatform setup.properties"
  cat >"${EZYPLATFORM_DIR}/settings/setup.properties" <<EOF
datasource.jdbc_url=jdbc:mysql://localhost:3306/${DB_NAME}
datasource.driver_class_name=com.mysql.cj.jdbc.Driver
datasource.username=${DB_USER}
datasource.password=${DB_PASSWORD}
tables.create_manually=false
EOF
}

enable_services() {
  log "Enabling and starting services"
  systemctl enable mysql || true
  systemctl enable nginx || true
  systemctl start mysql
  systemctl start nginx || true
}

start_ezyplatform_admin() {
  if is_port_9090_listening; then
    log "Port ${EZYPLATFORM_ADMIN_PORT} is already in use, skipping automatic admin startup"
    return 0
  fi

  if [[ ! -f "${EZYPLATFORM_DIR}/cli.sh" ]]; then
    echo "Could not find ${EZYPLATFORM_DIR}/cli.sh to restart admin."
    exit 1
  fi

  log "Starting EzyPlatform admin"
  (
    cd "${EZYPLATFORM_DIR}"
    bash cli.sh "start admin"
  )
}

start_ezyplatform_web() {
  if is_port_listening "${EZYPLATFORM_WEB_PORT}"; then
    log "Port ${EZYPLATFORM_WEB_PORT} is already in use, skipping automatic web startup"
    return 0
  fi

  if [[ ! -f "${EZYPLATFORM_DIR}/cli.sh" ]]; then
    echo "Could not find ${EZYPLATFORM_DIR}/cli.sh to start web."
    exit 1
  fi

  log "Starting EzyPlatform web"
  (
    cd "${EZYPLATFORM_DIR}"
    bash cli.sh "start web"
  )
}

# Mirrors EzySupport's AdminAutoRestartEzyPlatformService: a polling check
# script plus a systemd watchdog unit with Restart=always, so admin recovers
# the same way whether the watchdog was installed by this script or by
# EzySupport from within the running admin UI.
install_admin_watchdog() {
  local target="admin"
  local port="${EZYPLATFORM_ADMIN_PORT}"
  local check_file="${EZYPLATFORM_DIR}/check-${target}-${port}.sh"
  local service_name="ezyplatform-${target}-${port}-watchdog"
  local service_file="${EZYPLATFORM_DIR}/${service_name}.service"

  log "Installing auto-restart watchdog for ${target} (port ${port})"

  if [[ ! -f "${EZYPLATFORM_DIR}/cli.sh" ]]; then
    echo "Could not find ${EZYPLATFORM_DIR}/cli.sh, skipping watchdog installation."
    return 0
  fi

  cat >"${check_file}" <<'CHECK_EOF'
#!/bin/bash

HOST="localhost"
PORT="__PORT__"
URL="http://localhost:__PORT__"
APP_DIR="__APP_DIR__"
LOG_FILE="/var/log/check-ezyplatform-status.log"
INTERVAL=120

check_server_status() {
    if command -v bash >/dev/null 2>&1 && command -v timeout >/dev/null 2>&1; then
        timeout 5 bash -c "</dev/tcp/$HOST/$PORT" >/dev/null 2>&1
        return $?
    fi

    if command -v ss >/dev/null 2>&1; then
        ss -ltn | grep -q ":$PORT "
        return $?
    fi

    if command -v curl >/dev/null 2>&1; then
        curl -sS --max-time 5 "$URL" >/dev/null 2>&1
        return $?
    fi

    return 1
}

while true; do
    if ! check_server_status; then
        echo "$(date '+%Y-%m-%d %H:%M:%S') - __TARGET__ __PORT__ check failed, restarting __TARGET__..." >> "$LOG_FILE"
        cd "$APP_DIR" && bash cli.sh "restart __TARGET__" >> "$LOG_FILE" 2>&1
    fi

    sleep "$INTERVAL"
done
CHECK_EOF

  sed -i \
    -e "s#__PORT__#${port}#g" \
    -e "s#__APP_DIR__#${EZYPLATFORM_DIR}#g" \
    -e "s#__TARGET__#${target}#g" \
    "${check_file}"
  chmod +x "${check_file}"

  cat >"${service_file}" <<EOF
[Unit]
Description=EzyPlatform ${target} ${port} Watchdog
After=network.target

[Service]
Type=simple
ExecStart=${check_file}
Restart=always
RestartSec=10
User=root
KillMode=process

[Install]
WantedBy=multi-user.target
EOF

  cp "${service_file}" "/etc/systemd/system/${service_name}.service"
  systemctl daemon-reload
  systemctl enable "${service_name}"
  systemctl restart "${service_name}"

  if systemctl is-active --quiet "${service_name}"; then
    log "Watchdog ${service_name} is active"
  else
    log "Watchdog ${service_name} failed to start, check: systemctl status ${service_name}"
  fi
}

# Follows the Nginx site block from the official EzyPlatform Ubuntu
# deployment guide (https://youngmonkeys.org/ezyplatform/guides/deploy-ezyplatform-on-ubuntu):
# same proxy headers, client_max_body_size, and, for the admin site, the
# dedicated unbuffered /api/v1/media/add upload location.
configure_nginx_site() {
  local domain="$1"
  local upstream_port="$2"
  local max_body_size="$3"
  local include_media_upload="$4"
  local site_name="ezyplatform-${domain}"
  local site_file="/etc/nginx/sites-available/${site_name}"

  require_command nginx

  if [[ -f "${site_file}" ]]; then
    log "Nginx site for ${domain} already exists, skipping"
  else
    log "Configuring Nginx site for ${domain} -> 127.0.0.1:${upstream_port}"

    {
      cat <<EOF
server {
    listen 80;
    server_name ${domain};

    location / {
        proxy_set_header X-Forwarded-For \$remote_addr;
        proxy_set_header X-Forwarded-Proto \$scheme;
        proxy_set_header Host \$http_host;
        proxy_pass "http://127.0.0.1:${upstream_port}";
        client_max_body_size ${max_body_size};
    }
EOF

      if [[ "${include_media_upload}" == "1" ]]; then
        cat <<EOF

    location /api/v1/media/add {
        proxy_pass http://127.0.0.1:${upstream_port};
        proxy_http_version 1.1;
        proxy_buffering off;
        client_max_body_size ${max_body_size};
    }
EOF
      fi

      echo "}"
    } >"${site_file}"
  fi

  ln -sf "${site_file}" "/etc/nginx/sites-enabled/${site_name}"

  if ! nginx -t >/dev/null 2>&1; then
    echo "Nginx configuration test failed after adding site for ${domain}."
    exit 1
  fi

  systemctl reload nginx
}

# Requests a certificate through Certbot's Nginx plugin, which rewrites the
# site file written by configure_nginx_site to add the 443 server block and
# an HTTP-to-HTTPS redirect. Requires the domain's DNS A record to already
# point at this server.
request_certificate() {
  local domain="$1"

  require_command certbot

  if [[ -d "/etc/letsencrypt/live/${domain}" ]]; then
    log "TLS certificate for ${domain} already exists, skipping Certbot"
    return 0
  fi

  log "Requesting TLS certificate for ${domain}"

  local email_args=()
  if [[ -n "${CERTBOT_EMAIL}" ]]; then
    email_args=(--email "${CERTBOT_EMAIL}")
  else
    email_args=(--register-unsafely-without-email)
  fi

  certbot --nginx \
    -d "${domain}" \
    --non-interactive \
    --agree-tos \
    --redirect \
    "${email_args[@]}"
}

# Runs a SQL statement as the EzyPlatform application DB user via a
# temporary --defaults-extra-file, instead of passing the password on the
# command line, where it would be visible to other local users in `ps`.
run_mysql_as_app_user() {
  local sql="$1"
  local cred_file
  cred_file="$(mktemp)"
  chmod 600 "${cred_file}"

  cat >"${cred_file}" <<EOF
[client]
user=${DB_USER}
password=${DB_PASSWORD}
EOF

  if mysql --defaults-extra-file="${cred_file}" "${DB_NAME}" -e "${sql}"; then
    rm -f "${cred_file}"
    return 0
  fi

  rm -f "${cred_file}"
  return 1
}

upsert_ezy_setting_url() {
  local name="$1"
  local value="$2"

  log "Setting ${name} = ${value}"

  run_mysql_as_app_user "
INSERT INTO ezy_settings (
    setting_name,
    data_type,
    setting_value,
    created_at,
    updated_at
)
VALUES (
    '${name}',
    'URL',
    '${value}',
    NOW(),
    NOW()
)
ON DUPLICATE KEY UPDATE
    data_type = VALUES(data_type),
    setting_value = VALUES(setting_value),
    updated_at = NOW();
"
}

setup_admin_domain() {
  if [[ -z "${ADMIN_DOMAIN}" ]]; then
    return 0
  fi

  configure_nginx_site "${ADMIN_DOMAIN}" "${EZYPLATFORM_ADMIN_PORT}" "100M" "1"
  request_certificate "${ADMIN_DOMAIN}"
  upsert_ezy_setting_url "admin_url" "https://${ADMIN_DOMAIN}"
}

setup_web_domain() {
  if [[ -z "${WEB_DOMAIN}" ]]; then
    return 0
  fi

  configure_nginx_site "${WEB_DOMAIN}" "${EZYPLATFORM_WEB_PORT}" "50M" "0"
  request_certificate "${WEB_DOMAIN}"
  upsert_ezy_setting_url "web_url" "https://${WEB_DOMAIN}"
  upsert_ezy_setting_url "websocket_url" "wss://${WEB_DOMAIN}:2812/ws"
}

print_summary() {
  local admin_setup_url="http://<server-ip>:${EZYPLATFORM_ADMIN_PORT}/setup-admin"
  local admin_url_display="not set, using IP:${EZYPLATFORM_ADMIN_PORT}"
  local web_url_display="not set, using IP:${EZYPLATFORM_WEB_PORT}"
  local websocket_url_display="not set"

  if [[ -n "${ADMIN_DOMAIN}" ]]; then
    admin_setup_url="https://${ADMIN_DOMAIN}/setup-admin"
    admin_url_display="https://${ADMIN_DOMAIN}"
  fi

  if [[ -n "${WEB_DOMAIN}" ]]; then
    web_url_display="https://${WEB_DOMAIN}"
    websocket_url_display="wss://${WEB_DOMAIN}:2812/ws"
  fi

  cat <<EOF

Installation completed.

Database:
  name: ${DB_NAME}
  user: ${DB_USER}
  password: ${DB_PASSWORD}

EzyPlatform:
  directory: ${EZYPLATFORM_DIR}
  setup file: ${EZYPLATFORM_DIR}/settings/setup.properties

MySQL root:
  password: ${MYSQL_ROOT_PASSWORD:-unchanged}

Next steps:
  1. Open:
     ${admin_setup_url}
  2. Complete admin setup in the browser.
  3. If you want to inspect the log:
     cd ${EZYPLATFORM_DIR}
     tail -f logs/admin-server.log

Watchdog:
  service: ezyplatform-admin-${EZYPLATFORM_ADMIN_PORT}-watchdog
  status:  systemctl status ezyplatform-admin-${EZYPLATFORM_ADMIN_PORT}-watchdog
  log:     tail -f /var/log/check-ezyplatform-status.log

Domains:
  admin_url:     ${admin_url_display}
  web_url:       ${web_url_display}
  websocket_url: ${websocket_url_display}

Notes:
  - Nginx and Certbot were installed. A site and TLS certificate were only
    configured for domains passed via ADMIN_DOMAIN/WEB_DOMAIN.
  - Admin and web are started automatically at the end of the installation.
  - If port ${EZYPLATFORM_ADMIN_PORT} or ${EZYPLATFORM_WEB_PORT} is already listening, the script keeps the existing service and does not stop/restart it.
  - A systemd watchdog checks the admin port every 120s and runs
    'bash cli.sh "restart admin"' if it stops responding, same as EzySupport's
    auto-restart feature.
EOF
}

require_command apt-get
require_command systemctl
require_command wget
require_command unzip
require_command ss

install_packages
enable_services
wait_for_mysql
configure_mysql_root_password
create_database_and_user
configure_java_home
download_and_extract_ezyplatform
write_setup_properties
start_ezyplatform_admin
start_ezyplatform_web
install_admin_watchdog
setup_admin_domain
setup_web_domain

log "Java version"
java -version || true

print_summary