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

BASE_DIR="${BASE_DIR:-/home/app/lobster-ai}"
SOURCE_DIR="${SOURCE_DIR:-${BASE_DIR}/source-code}"
DOCKER_DIR="${DOCKER_DIR:-${BASE_DIR}/deploy/docker}"
WEBAPP_DIR="${WEBAPP_DIR:-${BASE_DIR}/zm-ai-server}"
FRONTEND_RUNTIME_DIR="${FRONTEND_RUNTIME_DIR:-${BASE_DIR}/zm-ai-frontend}"

SERVER_REPO_URL="${SERVER_REPO_URL:-http://192.168.1.204:3000/gzzm/zm-ai-server.git}"
SERVER_BRANCH="${SERVER_BRANCH:-master}"
FRONTEND_REPO_URL="${FRONTEND_REPO_URL:-http://192.168.1.204:3000/gzzm-frontend/ai-deepseek.git}"
FRONTEND_BRANCH="${FRONTEND_BRANCH:-next}"

SERVER_SRC="${SOURCE_DIR}/zm-ai-server"
FRONTEND_SRC="${SOURCE_DIR}/zm-ai-frontend"
ZMEG_ARTIFACT_DIR="${SOURCE_DIR}/zmeg_new-artifacts"
STATE_DIR="${SOURCE_DIR}/.build-state"
MAVEN_IMAGE="${MAVEN_IMAGE:-maven:3.9.11-eclipse-temurin-8}"

mkdir -p "${SOURCE_DIR}" "${STATE_DIR}"

log() {
  printf '[%s] %s\n' "$(date '+%F %T')" "$*"
}

sha_file() {
  sha256sum "$1" | awk '{print $1}'
}

read_state() {
  local name="$1"
  if [ -f "${STATE_DIR}/${name}" ]; then
    cat "${STATE_DIR}/${name}"
  fi
}

write_state() {
  local name="$1"
  local value="$2"
  printf '%s\n' "${value}" > "${STATE_DIR}/${name}"
}

ensure_repo() {
  local dir="$1"
  local repo_url="$2"
  local branch="$3"

  if [ ! -d "${dir}/.git" ]; then
    log "Clone ${repo_url} (${branch}) -> ${dir}"
    if [ -e "${dir}" ] && [ -n "$(find "${dir}" -mindepth 1 -maxdepth 1 -print -quit 2>/dev/null)" ]; then
      log "ERROR: ${dir} exists but is not a Git repository; refusing to delete existing files."
      exit 9
    fi
    git clone --branch "${branch}" "${repo_url}" "${dir}" || {
      log "ERROR: failed to clone ${repo_url}"
      exit 5
    }
    return 0
  fi

  if ! git -C "${dir}" diff --quiet || ! git -C "${dir}" diff --cached --quiet; then
    log "ERROR: ${dir} has uncommitted changes; refusing to overwrite deployment source."
    exit 2
  fi

  local before
  local after
  before="$(git -C "${dir}" rev-parse HEAD)"
  log "Fetch ${dir}"
  git -C "${dir}" fetch origin "${branch}" || {
    log "ERROR: failed to fetch ${dir}"
    exit 6
  }
  git -C "${dir}" checkout "${branch}" || {
    log "ERROR: failed to checkout ${branch} in ${dir}"
    exit 7
  }
  git -C "${dir}" reset --hard "origin/${branch}" || {
    log "ERROR: failed to reset ${dir} to origin/${branch}"
    exit 8
  }
  after="$(git -C "${dir}" rev-parse HEAD)"

  if [ "${before}" != "${after}" ]; then
    return 0
  fi
  return 1
}

artifact_digest() {
  if [ -f "${ZMEG_ARTIFACT_DIR}/artifact.sha256" ]; then
    cat "${ZMEG_ARTIFACT_DIR}/artifact.sha256"
    return 0
  fi
  if [ -f "${WEBAPP_DIR}/WEB-INF/lib/zmeg_new-classes.jar" ]; then
    sha_file "${WEBAPP_DIR}/WEB-INF/lib/zmeg_new-classes.jar"
    return 0
  fi
  printf 'none\n'
}

sync_zmeg_artifacts_to_backend_source() {
  mkdir -p "${SERVER_SRC}/web/WEB-INF/lib"

  if [ -f "${ZMEG_ARTIFACT_DIR}/zmeg_new-classes.jar" ]; then
    log "Use published zmeg_new initialization artifacts"
    if [ -d "${ZMEG_ARTIFACT_DIR}/lib" ]; then
      rsync -a "${ZMEG_ARTIFACT_DIR}/lib/" "${SERVER_SRC}/web/WEB-INF/lib/"
    fi
    cp -f "${ZMEG_ARTIFACT_DIR}/zmeg_new-classes.jar" "${SERVER_SRC}/web/WEB-INF/lib/zmeg_new-classes.jar"
    return 0
  fi

  if [ -f "${WEBAPP_DIR}/WEB-INF/lib/zmeg_new-classes.jar" ]; then
    log "Use existing runtime WEB-INF/lib dependencies"
    rsync -a "${WEBAPP_DIR}/WEB-INF/lib/" "${SERVER_SRC}/web/WEB-INF/lib/"
    return 0
  fi

  if [ -d "${ZMEG_ARTIFACT_DIR}/lib" ]; then
    rsync -a "${ZMEG_ARTIFACT_DIR}/lib/" "${SERVER_SRC}/web/WEB-INF/lib/"
  fi

  log "ERROR: missing zmeg_new-classes.jar. Run publish-zmeg-artifacts.ps1 once from the local workstation."
  exit 3
}

build_backend() {
  log "Prepare zmeg_new framework jars for backend build"
  sync_zmeg_artifacts_to_backend_source

  log "Build zm-ai-server"
  if command -v mvn >/dev/null 2>&1; then
    (cd "${SERVER_SRC}" && mvn -DskipTests package)
  else
    docker run --rm \
      -v "${SERVER_SRC}:/workspace" \
      -v "${SOURCE_DIR}/.m2:/root/.m2" \
      -w /workspace \
      "${MAVEN_IMAGE}" \
      mvn -DskipTests package
  fi
}

deploy_zmeg_web_resources() {
  local web_tar="${ZMEG_ARTIFACT_DIR}/zmeg_new-web-no-webinf.tar.gz"
  if [ ! -f "${web_tar}" ]; then
    return 0
  fi

  local zmeg_web_stage="${SOURCE_DIR}/zmeg_new-web"
  rm -rf "${zmeg_web_stage}"
  mkdir -p "${zmeg_web_stage}"
  tar -xzf "${web_tar}" -C "${zmeg_web_stage}"

  log "Deploy zmeg_new web resources, excluding WEB-INF"
  rsync -a --exclude='/WEB-INF/' "${zmeg_web_stage}/" "${WEBAPP_DIR}/"
}

deploy_backend() {
  log "Deploy backend runtime artifacts while preserving remote WEB-INF environment config"
  if [ ! -d "${SERVER_SRC}/web/WEB-INF/classes" ]; then
    log "ERROR: backend build did not create WEB-INF/classes"
    exit 4
  fi

  # Keep this as an exclusion list instead of a whitelist. New Java packages,
  # servlet mappings, static web files, and other runtime artifacts should deploy
  # automatically; only server-local environment files are protected.
  rsync -a \
    --exclude='/WEB-INF/db.properties' \
    --exclude='/WEB-INF/config/' \
    --exclude='/WEB-INF/*.xml' \
    "${SERVER_SRC}/web/" "${WEBAPP_DIR}/"
}

sync_frontend_runtime_source() {
  log "Sync frontend source to compose-mounted runtime directory"
  mkdir -p "${FRONTEND_RUNTIME_DIR}"
  rsync -a \
    --exclude='.git/' \
    --exclude='node_modules/' \
    --exclude='dist/' \
    "${FRONTEND_SRC}/" "${FRONTEND_RUNTIME_DIR}/"
}

build_frontend() {
  log "Build frontend with lobster-ai-frontend-build"
  (cd "${DOCKER_DIR}" && docker compose -f docker-compose.dev.yml --profile build run --rm lobster-ai-frontend-build)
}

restart_tomcat() {
  log "Restart lobster-ai-tomcat"
  (cd "${DOCKER_DIR}" && docker compose -f docker-compose.dev.yml restart lobster-ai-tomcat)
}

server_changed=0
frontend_changed=0

if ensure_repo "${SERVER_SRC}" "${SERVER_REPO_URL}" "${SERVER_BRANCH}"; then
  server_changed=1
fi

if ensure_repo "${FRONTEND_SRC}" "${FRONTEND_REPO_URL}" "${FRONTEND_BRANCH}"; then
  frontend_changed=1
fi

current_artifact_digest="$(artifact_digest)"
previous_artifact_digest="$(read_state zmeg_new-artifact.sha256 || true)"
zmeg_artifact_changed=0
if [ "${current_artifact_digest}" != "${previous_artifact_digest}" ]; then
  log "zmeg_new artifact changed"
  zmeg_artifact_changed=1
  server_changed=1
fi

current_server_head="$(git -C "${SERVER_SRC}" rev-parse HEAD)"
previous_server_head="$(read_state zm-ai-server.head || true)"
if [ "${current_server_head}" != "${previous_server_head}" ]; then
  server_changed=1
fi

current_frontend_head="$(git -C "${FRONTEND_SRC}" rev-parse HEAD)"
previous_frontend_head="$(read_state zm-ai-frontend.head || true)"
if [ "${current_frontend_head}" != "${previous_frontend_head}" ]; then
  frontend_changed=1
fi

did_work=0

if [ "${server_changed}" = "1" ]; then
  build_backend
  if [ "${zmeg_artifact_changed}" = "1" ] && [ -f "${ZMEG_ARTIFACT_DIR}/zmeg_new-web-no-webinf.tar.gz" ]; then
    deploy_zmeg_web_resources
  fi
  deploy_backend
  write_state zmeg_new-artifact.sha256 "${current_artifact_digest}"
  write_state zm-ai-server.head "${current_server_head}"
  did_work=1
else
  log "Backend unchanged; skip backend build"
fi

if [ "${frontend_changed}" = "1" ]; then
  sync_frontend_runtime_source
  build_frontend
  write_state zm-ai-frontend.head "${current_frontend_head}"
  did_work=1
else
  log "Frontend unchanged; skip frontend build"
fi

if [ "${server_changed}" = "1" ]; then
  restart_tomcat
elif [ "${did_work}" = "1" ]; then
  log "Only frontend updated; tomcat restart skipped"
else
  log "No updates detected; tomcat restart skipped"
fi

log "Done"
