#!/usr/bin/env bash
set -Eeuo pipefail

log() {
  printf '[vsmecp-installer] %s\n' "$*"
}

die() {
  printf '[vsmecp-installer] ERROR: %s\n' "$*" >&2
  exit 1
}

require_root() {
  [ "$(id -u)" -eq 0 ] || die "Run as root."
}

check_os() {
  [ -r /etc/os-release ] || die "/etc/os-release not found."
  # shellcheck disable=SC1091
  . /etc/os-release
  case "${ID:-}" in
    almalinux|rocky|rhel|centos) ;;
    *) die "Only AlmaLinux/Rocky/RHEL/CentOS 8 are supported. Detected ${ID:-unknown}." ;;
  esac
  case "${VERSION_ID:-}" in
    8|8.*) ;;
    *) die "Only EL8 is supported. Detected VERSION_ID=${VERSION_ID:-unknown}." ;;
  esac
}

configure_dnf_resilience() {
  [ -d /etc/dnf ] || return 0
  local conf="/etc/dnf/dnf.conf"
  local marker="# VSMECP installer network resilience"
  touch "${conf}"
  if ! grep -qF "${marker}" "${conf}"; then
    if ! grep -q '^\[main\]' "${conf}"; then
      printf '[main]\n' >>"${conf}"
    fi
    cat >>"${conf}" <<DNF
${marker}
timeout=30
minrate=65536
retries=5
max_parallel_downloads=10
fastestmirror=True
DNF
  fi
}

ensure_tools() {
  local missing="" cmd
  for cmd in curl tar gzip python3 openssl sha256sum find awk sed; do
    if ! command -v "${cmd}" >/dev/null 2>&1; then
      missing="${missing} ${cmd}"
    fi
  done
  if [ -n "${missing}" ]; then
    command -v dnf >/dev/null 2>&1 || die "Missing tools:${missing}; dnf is not available."
    log "Installing bootstrap tools:${missing}"
    dnf -y install curl tar gzip python3 openssl coreutils findutils gawk sed
  fi
}

curl_fetch() {
  local url="$1" output="$2"
  curl -fsSL \
    --retry "${VSMECP_CURL_RETRIES:-3}" \
    --retry-delay "${VSMECP_CURL_RETRY_DELAY:-2}" \
    --connect-timeout "${VSMECP_CURL_CONNECT_TIMEOUT:-20}" \
    --max-time "${VSMECP_CURL_MAX_TIME:-300}" \
    -A "${VSMECP_INSTALLER_USER_AGENT:-VSMECP-Installer/1.0 (+https://cp.vsme.vn)}" \
    "${url}" -o "${output}"
}

fetch_manifest_env() {
  local manifest_url="$1" output="$2"
  local manifest_json
  manifest_json="${output}.json"
  curl_fetch "${manifest_url}" "${manifest_json}"
  python3 - "${manifest_json}" "$output" <<'PY'
import json
import shlex
import sys

manifest_json, output = sys.argv[1], sys.argv[2]
with open(manifest_json, "r", encoding="utf-8") as response:
    manifest = json.load(response)

if manifest.get("product") != "vsmecp":
    raise SystemExit("Manifest product is not vsmecp")

required = ["version", "artifact_url", "sha256", "signature_url", "public_key_sha256"]
missing = [key for key in required if not manifest.get(key)]
if missing:
    raise SystemExit("Manifest missing required fields: " + ", ".join(missing))

public_key_url = manifest.get("public_key_url") or "https://cp.vsme.vn/updates/vsmecp/update-public.pem"
values = {
    "VERSION": str(manifest["version"]),
    "ARTIFACT_URL": str(manifest["artifact_url"]),
    "SHA256": str(manifest["sha256"]).lower(),
    "SIGNATURE_URL": str(manifest["signature_url"]),
    "PUBLIC_KEY_URL": str(public_key_url),
    "PUBLIC_KEY_SHA256": str(manifest["public_key_sha256"]).lower(),
}
with open(output, "w") as handle:
    for key, value in values.items():
        handle.write("%s=%s\n" % (key, shlex.quote(value)))
PY
}

main() {
  require_root
  check_os
  configure_dnf_resilience
  ensure_tools

  : "${VSMECP_CONTROL_PLANE_URL:=https://cp.vsme.vn}"
  VSMECP_CONTROL_PLANE_URL="${VSMECP_CONTROL_PLANE_URL%/}"
  : "${VSMECP_MANIFEST_URL:=${VSMECP_CONTROL_PLANE_URL}/updates/vsmecp/latest.json}"
  : "${VSMECP_UPDATE_MANIFEST_URL:=${VSMECP_MANIFEST_URL}}"
  : "${VSMECP_APPSTORE_CATALOG_URL:=${VSMECP_CONTROL_PLANE_URL}/appstore/catalog.json}"
  : "${VSMECP_LICENSE_SERVER_URL:=${VSMECP_CONTROL_PLANE_URL}/api/license.php}"
  : "${VSMECP_NODE_ENDPOINT:=${VSMECP_CONTROL_PLANE_URL}/api/nodes.php}"
  : "${VSMECP_UPDATE_PUBLIC_KEY_PATH:=/etc/vsmecp/update-public.pem}"

  local work manifest_env archive signature public_key installer release_root
  work="$(mktemp -d /tmp/vsmecp-install.XXXXXX)"
  trap 'rm -rf "${work:-}"' EXIT

  log "Fetching manifest: ${VSMECP_MANIFEST_URL}"
  manifest_env="${work}/manifest.env"
  fetch_manifest_env "${VSMECP_MANIFEST_URL}" "${manifest_env}"
  # shellcheck disable=SC1090
  . "${manifest_env}"

  archive="${work}/vsmecp-${VERSION}.tar.gz"
  signature="${work}/vsmecp-${VERSION}.tar.gz.sig"
  public_key="${work}/update-public.pem"

  log "Downloading release ${VERSION}"
  curl_fetch "${ARTIFACT_URL}" "${archive}"
  curl_fetch "${SIGNATURE_URL}" "${signature}"
  curl_fetch "${PUBLIC_KEY_URL}" "${public_key}"

  log "Verifying update public key SHA256"
  printf '%s  %s\n' "${PUBLIC_KEY_SHA256}" "${public_key}" | sha256sum -c -

  log "Verifying SHA256"
  printf '%s  %s\n' "${SHA256}" "${archive}" | sha256sum -c -

  log "Verifying OpenSSL signature"
  openssl dgst -sha256 -verify "${public_key}" -signature "${signature}" "${archive}" >/dev/null

  log "Extracting release"
  tar -xzf "${archive}" -C "${work}"
  installer="$(find "${work}" -path '*/install/el8/install.sh' -type f | head -n 1)"
  [ -n "${installer}" ] || die "install/el8/install.sh not found in release archive."
  release_root="$(cd "$(dirname "${installer}")/../.." && pwd)"

  if [ "${VSMECP_INSTALLER_VERIFY_ONLY:-0}" = "1" ]; then
    log "Verify-only mode completed for release ${VERSION}."
    exit 0
  fi

  export VSMECP_CONTROL_PLANE_URL
  export VSMECP_UPDATE_MANIFEST_URL
  export VSMECP_UPDATE_PUBLIC_KEY_URL="${PUBLIC_KEY_URL}"
  export VSMECP_UPDATE_PUBLIC_KEY_PATH
  export VSMECP_APPSTORE_CATALOG_URL
  export VSMECP_LICENSE_SERVER_URL
  export VSMECP_NODE_ENDPOINT
  export VSMECP_LICENSE_KEY="${VSMECP_LICENSE_KEY:-}"
  export VSMECP_CUSTOMER_NAME="${VSMECP_CUSTOMER_NAME:-}"

  log "Running EL8 installer from ${release_root}"
  bash "${installer}"

  log "Install finished."
  log "Admin panel: https://$(hostname -I | awk '{print $1}'):2087/"
  log "Credentials: /root/vsmecp-root.txt and /root/vsmecp-admin.txt"
}

main "$@"
