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

catalog_root=${OPENRESTY_GUARD_CATALOG_ROOT:-/opt/1panel/resource/apps/remote/openresty}
app_dir=${OPENRESTY_GUARD_APP_DIR:-/opt/1panel/apps/openresty/openresty}
state_dir=${OPENRESTY_GUARD_STATE_DIR:-/var/lib/openresty-upgrade-guard}
lock_file=${OPENRESTY_GUARD_LOCK_FILE:-/run/openresty-upgrade-guard.lock}
image_suffix=${OPENRESTY_GUARD_IMAGE_SUFFIX:--noble}
last_known_good=${OPENRESTY_GUARD_LAST_KNOWN_GOOD:-1panel/openresty:local-last-known-good}
minimum_healthy_seconds=${OPENRESTY_GUARD_MIN_HEALTHY_SECONDS:-1800}
tags_url=${OPENRESTY_GUARD_TAGS_URL:-https://docker.1panel.live/v2/1panel/openresty/tags/list}
docker_bin=${OPENRESTY_GUARD_DOCKER_BIN:-$(command -v docker || true)}

if [[ ! $minimum_healthy_seconds =~ ^[0-9]+$ ]]; then
  echo "OPENRESTY_GUARD_MIN_HEALTHY_SECONDS must be a non-negative integer" >&2
  exit 2
fi

if [[ ! -d $catalog_root || ! -f $app_dir/.env ]]; then
  echo "openresty-upgrade-guard status=skipped reason=1panel-openresty-not-installed"
  exit 0
fi
if [[ -z $docker_bin || ! -x $docker_bin ]]; then
  echo "docker executable not found" >&2
  exit 1
fi

install -d -m 0755 "$state_dir" "$(dirname "$lock_file")"
exec 9>"$lock_file"
if ! flock -n 9; then
  echo "openresty-upgrade-guard status=skipped reason=already-running"
  exit 0
fi

container_name=$(
  sed -nE 's/^CONTAINER_NAME=["'"'"']?([^"'"'"']+)["'"'"']?$/\1/p' "$app_dir/.env" |
    head -n 1
)
if [[ ! $container_name =~ ^[a-zA-Z0-9][a-zA-Z0-9_.-]*$ ]]; then
  echo "cannot determine a safe OpenResty container name from $app_dir/.env" >&2
  exit 1
fi

current_image_id=
rollback_status=unavailable
if [[ $("$docker_bin" inspect --format '{{.State.Running}}' "$container_name" 2>/dev/null || true) == true ]]; then
  "$docker_bin" exec "$container_name" openresty -t
  current_image_id=$("$docker_bin" inspect --format '{{.Image}}' "$container_name")
  last_known_good_id=$(
    "$docker_bin" image inspect --format '{{.Id}}' "$last_known_good" 2>/dev/null || true
  )
  started_at=$("$docker_bin" inspect --format '{{.State.StartedAt}}' "$container_name")
  started_epoch=$(date -d "$started_at" +%s 2>/dev/null || printf '0')
  now_epoch=$(date +%s)
  if (( started_epoch > 0 && now_epoch >= started_epoch )); then
    healthy_seconds=$((now_epoch - started_epoch))
  else
    healthy_seconds=0
  fi
  if [[ -z $last_known_good_id || $last_known_good_id == "$current_image_id" || $healthy_seconds -ge $minimum_healthy_seconds ]]; then
    "$docker_bin" image tag "$current_image_id" "$last_known_good"
    rollback_status=updated
  else
    rollback_status=retained
  fi
fi

catalog_version=$(
  find "$catalog_root" -mindepth 1 -maxdepth 1 -type d -name "*${image_suffix}" \
    -exec basename {} \; |
    sort -V |
    tail -n 1
)
remote_version=
curl_bin=$(command -v curl || true)
jq_bin=$(command -v jq || true)
if [[ -n $tags_url && -n $curl_bin && -n $jq_bin ]]; then
  remote_version=$(
    "$curl_bin" -fsSL --connect-timeout 5 --max-time 20 "$tags_url" 2>/dev/null |
      "$jq_bin" -r '.tags[]?' 2>/dev/null |
      grep -E "^[0-9]+([.][0-9]+)+(-[0-9]+)*${image_suffix}$" |
      sort -V |
      tail -n 1 || true
  )
fi
latest_version=$(printf '%s\n%s\n' "$catalog_version" "$remote_version" | sed '/^$/d' | sort -V | tail -n 1)
if [[ -z $latest_version ]]; then
  echo "no OpenResty ${image_suffix} release found below $catalog_root" >&2
  exit 1
fi
if [[ ! $latest_version =~ ^[0-9]+([.][0-9]+)+(-[0-9]+)*${image_suffix}$ ]]; then
  echo "refusing unexpected OpenResty version: $latest_version" >&2
  exit 1
fi
candidate_image="1panel/openresty:$latest_version"

pulled=false
if ! "$docker_bin" image inspect "$candidate_image" >/dev/null 2>&1; then
  "$docker_bin" pull "$candidate_image"
  pulled=true
fi

# Validate the candidate binary against the exact configuration and certificate
# mounts used by the running edge. This catches incompatible directives before
# an operator asks 1Panel to replace the only listener on ports 80 and 443.
if [[ -n $current_image_id ]]; then
  "$docker_bin" run --rm \
    --volumes-from "$container_name" \
    --entrypoint /usr/local/openresty/bin/openresty \
    "$candidate_image" -t
else
  "$docker_bin" run --rm \
    --entrypoint /usr/local/openresty/bin/openresty \
    "$candidate_image" -t
fi

candidate_image_id=$("$docker_bin" image inspect --format '{{.Id}}' "$candidate_image")
temporary_ready=$(mktemp "$state_dir/ready.XXXXXX")
trap 'rm -f "$temporary_ready"' EXIT
printf 'image=%s\nimage_id=%s\nvalidated_at=%s\n' \
  "$candidate_image" "$candidate_image_id" "$(date -u +%Y-%m-%dT%H:%M:%SZ)" \
  >"$temporary_ready"
chmod 0644 "$temporary_ready"
mv -f "$temporary_ready" "$state_dir/ready"
trap - EXIT

echo "openresty-upgrade-guard status=ready candidate=$candidate_image candidate_id=$candidate_image_id pulled=$pulled catalog_version=$catalog_version remote_version=${remote_version:-unavailable} current_id=${current_image_id:-unavailable} rollback=$last_known_good rollback_status=$rollback_status"
