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

AGENTTEAM_APP_DIR="${AGENTTEAM_APP_DIR:-/opt/agentteam}"
AGENTTEAM_GIT_REMOTE="${AGENTTEAM_GIT_REMOTE:-origin}"
AGENTTEAM_BRANCH="${AGENTTEAM_BRANCH:-main}"
AGENTTEAM_REPO_URL="${AGENTTEAM_REPO_URL:-git@github.com:winkyao/agentteam.git}"
AGENTTEAM_RUN_AS="${AGENTTEAM_RUN_AS:-agentteam}"
AGENTTEAM_SERVICE="${AGENTTEAM_SERVICE:-agentteam-server}"
AGENTTEAM_ENV_FILE="${AGENTTEAM_ENV_FILE:-/etc/agentteam/agentteam.env}"
AGENTTEAM_SKIP_RESTART="${AGENTTEAM_SKIP_RESTART:-0}"
AGENTTEAM_SERVER_BIN="${AGENTTEAM_SERVER_BIN:-${AGENTTEAM_APP_DIR}/bin/agentteam-server}"
AGENTTEAM_SCHEMA_BIN="${AGENTTEAM_SCHEMA_BIN:-${AGENTTEAM_APP_DIR}/bin/agentteam-schema}"
AGENTTEAM_TIDB_ADMIN_BIN="${AGENTTEAM_TIDB_ADMIN_BIN:-${AGENTTEAM_APP_DIR}/bin/agentteam-tidb-admin}"
AGENTTEAM_WEB_BUILD_DIR="${AGENTTEAM_WEB_BUILD_DIR:-${AGENTTEAM_APP_DIR}/web/dist}"
AGENTTEAM_WEB_PUBLISH_DIR="${AGENTTEAM_WEB_PUBLISH_DIR:-${AGENTTEAM_APP_DIR}/www}"
AGENTTEAM_HEALTHZ_URL="${AGENTTEAM_HEALTHZ_URL:-http://127.0.0.1:3002/healthz}"
AGENTTEAM_READYZ_URL="${AGENTTEAM_READYZ_URL:-http://127.0.0.1:3002/readyz}"
AGENTTEAM_HEALTHCHECK_TIMEOUT_SEC="${AGENTTEAM_HEALTHCHECK_TIMEOUT_SEC:-60}"
AGENTTEAM_HEALTHCHECK_INTERVAL_SEC="${AGENTTEAM_HEALTHCHECK_INTERVAL_SEC:-2}"

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

fail() {
  log "ERROR: $*"
  exit 1
}

on_error() {
  local exit_code="$?"
  local line="$1"
  local cmd="$2"
  fail "line ${line} command '${cmd}' failed (exit=${exit_code})"
}

trap 'on_error ${LINENO} "${BASH_COMMAND}"' ERR

require_cmd() {
  local cmd="$1"
  if ! command -v "${cmd}" >/dev/null 2>&1; then
    fail "missing required command: ${cmd}"
  fi
}

require_file() {
  local path="$1"
  if [[ ! -f "${path}" ]]; then
    fail "required file not found: ${path}"
  fi
}

require_dir() {
  local path="$1"
  if [[ ! -d "${path}" ]]; then
    fail "required directory not found: ${path}"
  fi
}

ensure_go_toolchain() {
  if command -v go >/dev/null 2>&1; then
    return
  fi
  if sudo -u "${AGENTTEAM_RUN_AS}" bash -lc 'command -v go >/dev/null 2>&1'; then
    return
  fi
  fail "go not found in root PATH or ${AGENTTEAM_RUN_AS} PATH"
}

run_git() {
  local git_command="$1"
  sudo -u "${AGENTTEAM_RUN_AS}" bash -lc "cd '${AGENTTEAM_APP_DIR}' && ${git_command}"
}

wait_http_ready() {
  local name="$1"
  local url="$2"
  local timeout_sec="${3}"
  local interval_sec="${4}"
  local deadline=$((SECONDS + timeout_sec))

  while (( SECONDS < deadline )); do
    if curl -fsS "${url}" >/dev/null 2>&1; then
      return 0
    fi
    sleep "${interval_sec}"
  done

  fail "${name} check failed after ${timeout_sec}s: ${url}"
}

if [[ "${EUID}" -ne 0 ]]; then
  fail "must run as root (current uid=${EUID})"
fi

for cmd in git npm systemctl curl rsync sudo; do
  require_cmd "${cmd}"
done

id "${AGENTTEAM_RUN_AS}" >/dev/null 2>&1 || fail "linux user not found: ${AGENTTEAM_RUN_AS}"
ensure_go_toolchain
require_dir "${AGENTTEAM_APP_DIR}"
require_file "${AGENTTEAM_ENV_FILE}"

if [[ -d "${AGENTTEAM_APP_DIR}/.git" ]]; then
  chown -R "${AGENTTEAM_RUN_AS}:${AGENTTEAM_RUN_AS}" "${AGENTTEAM_APP_DIR}/.git"
fi

cd "${AGENTTEAM_APP_DIR}"
bootstrapped_repo="false"
if [[ ! -d .git ]]; then
  log "git repository not found in ${AGENTTEAM_APP_DIR}, bootstrapping from ${AGENTTEAM_REPO_URL}"
  run_git "git init"
  bootstrapped_repo="true"
fi

if run_git "git remote get-url '${AGENTTEAM_GIT_REMOTE}' >/dev/null 2>&1"; then
  if [[ -n "${AGENTTEAM_REPO_URL}" ]]; then
    run_git "git remote set-url '${AGENTTEAM_GIT_REMOTE}' '${AGENTTEAM_REPO_URL}'"
  fi
else
  [[ -n "${AGENTTEAM_REPO_URL}" ]] || fail "AGENTTEAM_REPO_URL is required when remote ${AGENTTEAM_GIT_REMOTE} is missing"
  run_git "git remote add '${AGENTTEAM_GIT_REMOTE}' '${AGENTTEAM_REPO_URL}'"
fi

if [[ "${bootstrapped_repo}" != "true" ]] && [[ -n "$(run_git "git status --porcelain --untracked-files=no")" ]]; then
  fail "git working tree is dirty in ${AGENTTEAM_APP_DIR}; commit/stash/revert changes before deploy"
fi

log "fetching ${AGENTTEAM_GIT_REMOTE}/${AGENTTEAM_BRANCH}"
run_git "git fetch --prune '${AGENTTEAM_GIT_REMOTE}'"

run_git "git show-ref --verify --quiet 'refs/remotes/${AGENTTEAM_GIT_REMOTE}/${AGENTTEAM_BRANCH}'" || \
  fail "remote branch not found: ${AGENTTEAM_GIT_REMOTE}/${AGENTTEAM_BRANCH}"

if run_git "git show-ref --verify --quiet 'refs/heads/${AGENTTEAM_BRANCH}'"; then
  run_git "git checkout -f '${AGENTTEAM_BRANCH}'"
else
  run_git "git checkout -f -B '${AGENTTEAM_BRANCH}' --track '${AGENTTEAM_GIT_REMOTE}/${AGENTTEAM_BRANCH}'"
fi

log "pulling latest ${AGENTTEAM_GIT_REMOTE}/${AGENTTEAM_BRANCH} with --ff-only"
run_git "git pull --ff-only '${AGENTTEAM_GIT_REMOTE}' '${AGENTTEAM_BRANCH}'"

log "installing web dependencies"
npm --prefix web ci

log "building web assets"
npm --prefix web run build

install -d -m 0755 -o "${AGENTTEAM_RUN_AS}" -g "${AGENTTEAM_RUN_AS}" "${AGENTTEAM_APP_DIR}/bin"
install -d -m 0755 "${AGENTTEAM_WEB_PUBLISH_DIR}"

log "building agentteam binaries"
sudo -u "${AGENTTEAM_RUN_AS}" bash -lc "cd '${AGENTTEAM_APP_DIR}' && agentteam_go_ldflags=\"\$(./scripts/agentteam-go-buildinfo-ldflags.sh)\" && go build -ldflags \"\${agentteam_go_ldflags}\" -o '${AGENTTEAM_SERVER_BIN}' ./cmd/agentteam-server && go build -o '${AGENTTEAM_SCHEMA_BIN}' ./cmd/agentteam-schema && go build -o '${AGENTTEAM_APP_DIR}/bin/agentteam-tidb-admin' ./cmd/agentteam-tidb-admin"

require_dir "${AGENTTEAM_WEB_BUILD_DIR}"

log "syncing web assets to ${AGENTTEAM_WEB_PUBLISH_DIR}"
rsync -a --delete "${AGENTTEAM_WEB_BUILD_DIR}/" "${AGENTTEAM_WEB_PUBLISH_DIR}/"

# shellcheck disable=SC1090
set -a
source "${AGENTTEAM_ENV_FILE}"
set +a

log "stopping service ${AGENTTEAM_SERVICE} before schema cutover"
systemctl stop "${AGENTTEAM_SERVICE}"

log "initializing / verifying schema"
"${AGENTTEAM_SCHEMA_BIN}" init

log "backfilling and verifying workspace placements"
"${AGENTTEAM_TIDB_ADMIN_BIN}" workspace-placement-backfill --workspace-group-id workspace-default
"${AGENTTEAM_TIDB_ADMIN_BIN}" workspace-placement-verify --workspace-group-id workspace-default

if [[ "${AGENTTEAM_SKIP_RESTART}" == "1" ]]; then
  log "starting service ${AGENTTEAM_SERVICE} after exclusive schema cutover (AGENTTEAM_SKIP_RESTART cannot leave it stopped)"
  systemctl start "${AGENTTEAM_SERVICE}"
  systemctl is-active --quiet "${AGENTTEAM_SERVICE}" || fail "service not active after start: ${AGENTTEAM_SERVICE}"
else
  log "restarting service ${AGENTTEAM_SERVICE}"
  systemctl restart "${AGENTTEAM_SERVICE}"
  systemctl is-active --quiet "${AGENTTEAM_SERVICE}" || fail "service not active after restart: ${AGENTTEAM_SERVICE}"
fi

log "checking health endpoint ${AGENTTEAM_HEALTHZ_URL}"
wait_http_ready "health" "${AGENTTEAM_HEALTHZ_URL}" "${AGENTTEAM_HEALTHCHECK_TIMEOUT_SEC}" "${AGENTTEAM_HEALTHCHECK_INTERVAL_SEC}"

log "checking ready endpoint ${AGENTTEAM_READYZ_URL}"
wait_http_ready "ready" "${AGENTTEAM_READYZ_URL}" "${AGENTTEAM_HEALTHCHECK_TIMEOUT_SEC}" "${AGENTTEAM_HEALTHCHECK_INTERVAL_SEC}"

log "deploy succeeded: $(run_git "git rev-parse --short HEAD")"
