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

readonly stack_root="/srv/gitea-security"
readonly env_file="${ENV_FILE:-$stack_root/.env}"
readonly openresty_root="$stack_root/openresty"
readonly gitea_conf="$openresty_root/conf.d/gitea.conf"
timestamp="$(date -u +%Y%m%dT%H%M%SZ)"
readonly timestamp
readonly backup_dir="$stack_root/backups/cutover-$timestamp"
success=false
changed=false

fail() {
  printf 'error=%s\n' "$1" >&2
  exit 1
}

rollback_on_error() {
  local exit_code=$?
  if [[ "$success" != true && "$changed" == true ]]; then
    install -m 0644 "$backup_dir/gitea.conf" "$gitea_conf"
    openresty -t -c "$openresty_root/nginx.conf" >/dev/null 2>&1 || true
    systemctl reload openresty >/dev/null 2>&1 || true
    printf 'cutover=rolled-back backup=%s\n' "$backup_dir" >&2
  fi
  exit "$exit_code"
}
trap rollback_on_error EXIT

[[ $(id -u) -eq 0 ]] || fail "run as root"
[[ -f "$env_file" ]] || fail "missing environment file: $env_file"
[[ -f "$stack_root/.prepared" ]] || fail "run prepare.sh first"
set -a
# shellcheck source=/dev/null
source "$stack_root/VERSION.env"
# shellcheck source=/dev/null
source "$env_file"
set +a

[[ "$PUBLIC_REPOSITORY" =~ ^[A-Za-z0-9_.-]+/[A-Za-z0-9_.-]+$ && "$PUBLIC_REPOSITORY" != *.git ]] || fail "PUBLIC_REPOSITORY must be org/repository without .git"
[[ "$(grep -c 'server 127.0.0.1:3000;' "$gitea_conf")" -eq 1 ]] || fail "direct Gitea upstream is not present exactly once"
[[ "$(docker inspect --format '{{.State.Health.Status}}' openappsec-agent)" == healthy ]] || fail "OpenAppSec Agent is not healthy"
[[ "$(docker inspect --format '{{.State.Health.Status}}' openappsec-nginx)" == healthy ]] || fail "OpenAppSec NGINX is not healthy"

sidecar_status="$(curl -sS -o /dev/null -w '%{http_code}' -H "Host: $GIT_DOMAIN" "http://127.0.0.1:19080/$PUBLIC_REPOSITORY")"
[[ "$sidecar_status" == 200 ]] || fail "sidecar repository check returned $sidecar_status"
sidecar_git_status="$(curl -sS -o /dev/null -w '%{http_code}' -H "Host: $GIT_DOMAIN" "http://127.0.0.1:19080/$PUBLIC_REPOSITORY.git/info/refs?service=git-upload-pack")"
[[ "$sidecar_git_status" == 200 ]] || fail "sidecar Git Smart HTTP check returned $sidecar_git_status"

direct_git_output="$(GIT_TERMINAL_PROMPT=0 git ls-remote "https://$GIT_DOMAIN/$PUBLIC_REPOSITORY.git" HEAD)"
[[ -n "$direct_git_output" ]] || fail "direct git ls-remote returned no HEAD"

install -d -m 0700 "$backup_dir"
install -m 0644 "$gitea_conf" "$backup_dir/gitea.conf"
sed -i 's/server 127\.0\.0\.1:3000;/server 127.0.0.1:19080;/' "$gitea_conf"
changed=true
openresty -t -c "$openresty_root/nginx.conf"
systemctl reload openresty

origin=(curl --insecure --silent --show-error --resolve "$GIT_DOMAIN:443:127.0.0.1")
repo_status="$("${origin[@]}" -o /dev/null -w '%{http_code}' "https://$GIT_DOMAIN/$PUBLIC_REPOSITORY")"
[[ "$repo_status" == 200 ]] || fail "origin repository check returned $repo_status"
git_output="$(GIT_TERMINAL_PROMPT=0 git ls-remote "https://$GIT_DOMAIN/$PUBLIC_REPOSITORY.git" HEAD)"
[[ -n "$git_output" ]] || fail "post-cutover git ls-remote returned no HEAD"
identity_status="$("${origin[@]}" -o /dev/null -w '%{http_code}' -H 'X-WEBAUTH-USER: forged-admin' "https://$GIT_DOMAIN/user/settings")"
[[ "$identity_status" == 302 || "$identity_status" == 303 ]] || fail "identity-header check returned $identity_status"
bundle_status="$("${origin[@]}" -o /dev/null -w '%{http_code}' "https://$GIT_DOMAIN/$PUBLIC_REPOSITORY/archive/main.bundle")"
[[ "$bundle_status" == 404 ]] || fail "bundle check returned $bundle_status"
curl -fsS http://127.0.0.1:18081/healthz >/dev/null

printf '%s\n' "$backup_dir" > "$stack_root/last-cutover-backup"
success=true
printf 'cutover=pass upstream=127.0.0.1:19080 repo=%s git=pass identity=%s bundle=%s\n' "$repo_status" "$identity_status" "$bundle_status"
