#!/bin/sh

set -eu

SCRIPT_DIR=$(CDPATH= cd -- "$(dirname -- "$0")" && pwd)
REPO_ROOT=$(CDPATH= cd -- "$SCRIPT_DIR/../../.." && pwd)
COMPOSE_FILE="$REPO_ROOT/etc/servers/work-server/docker-compose.yml"
PROXY_SERVICE="${WORK_SERVER_PROXY_SERVICE:-work-server}"
PROXY_CONTAINER="${WORK_SERVER_PROXY_CONTAINER:-work-server}"
BLUE_SERVICE="${WORK_SERVER_BLUE_SERVICE:-work-server-blue}"
GREEN_SERVICE="${WORK_SERVER_GREEN_SERVICE:-work-server-green}"
BLUE_CONTAINER="${WORK_SERVER_BLUE_CONTAINER:-work-server-blue}"
GREEN_CONTAINER="${WORK_SERVER_GREEN_CONTAINER:-work-server-green}"
ACTIVE_SLOT_FILE="${WORK_SERVER_ACTIVE_SLOT_FILE:-$REPO_ROOT/etc/servers/work-server/.docker/runtime/active-slot}"
PROXY_CONFIG_FILE="${WORK_SERVER_PROXY_CONFIG_FILE:-$REPO_ROOT/etc/servers/work-server/.docker/proxy/default.conf}"
HEALTH_ENDPOINT="${WORK_SERVER_HEALTH_ENDPOINT:-http://127.0.0.1:3100/health}"
RUNTIME_ENDPOINT="${WORK_SERVER_RUNTIME_ENDPOINT:-http://127.0.0.1:3100/api/runtime}"
PREVIOUS_SLOT_DRAIN_TIMEOUT_SECONDS="${WORK_SERVER_PREVIOUS_SLOT_DRAIN_TIMEOUT_SECONDS:-900}"

cd "$REPO_ROOT"

mkdir -p "$(dirname "$ACTIVE_SLOT_FILE")" "$(dirname "$PROXY_CONFIG_FILE")"

read_active_slot() {
  if [ -f "$ACTIVE_SLOT_FILE" ]; then
    SLOT=$(tr -d '[:space:]' <"$ACTIVE_SLOT_FILE")
    if [ "$SLOT" = "blue" ] || [ "$SLOT" = "green" ]; then
      printf '%s' "$SLOT"
      return 0
    fi
  fi

  printf 'blue'
}

container_is_running() {
  CONTAINER_NAME="$1"
  STATUS=$(docker inspect -f '{{.State.Status}}' "$CONTAINER_NAME" 2>/dev/null || true)
  [ "$STATUS" = "running" ]
}

resolve_active_slot() {
  SLOT=$(read_active_slot)

  if [ "$SLOT" = "blue" ] && ! container_is_running "$BLUE_CONTAINER" && container_is_running "$GREEN_CONTAINER"; then
    printf 'green'
    return 0
  fi

  if [ "$SLOT" = "green" ] && ! container_is_running "$GREEN_CONTAINER" && container_is_running "$BLUE_CONTAINER"; then
    printf 'blue'
    return 0
  fi

  printf '%s' "$SLOT"
}

write_proxy_config() {
  SLOT="$1"
  TARGET_CONTAINER="$BLUE_CONTAINER"

  if [ "$SLOT" = "green" ]; then
    TARGET_CONTAINER="$GREEN_CONTAINER"
  fi

  cat >"$PROXY_CONFIG_FILE" <<EOF2
server {
  listen 3100;
  server_name _;

  location /ws/chat {
    proxy_http_version 1.1;
    proxy_set_header Host \$host;
    proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for;
    proxy_set_header X-Forwarded-Proto \$scheme;
    proxy_set_header X-Forwarded-Host \$host;
    proxy_set_header X-Forwarded-Port \$server_port;
    proxy_set_header Upgrade \$http_upgrade;
    proxy_set_header Connection "upgrade";
    proxy_pass http://$TARGET_CONTAINER:3100;
  }

  location / {
    proxy_http_version 1.1;
    proxy_set_header Host \$host;
    proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for;
    proxy_set_header X-Forwarded-Proto \$scheme;
    proxy_set_header X-Forwarded-Host \$host;
    proxy_set_header X-Forwarded-Port \$server_port;
    proxy_set_header Connection "";
    proxy_pass http://$TARGET_CONTAINER:3100;
  }
}
EOF2
}

wait_for_container_health() {
  TARGET_CONTAINER="$1"
  ATTEMPT=0

  while [ "$ATTEMPT" -lt 60 ]; do
    if docker exec "$TARGET_CONTAINER" node -e "fetch(process.argv[1]).then(async (response) => { if (!response.ok) process.exit(1); process.stdout.write(await response.text()); }).catch(() => process.exit(1));" "$HEALTH_ENDPOINT" >/dev/null 2>&1; then
      return 0
    fi

    ATTEMPT=$((ATTEMPT + 1))
    sleep 2
  done

  echo "health check failed for $TARGET_CONTAINER" >&2
  return 1
}

read_runtime_value() {
  TARGET_CONTAINER="$1"
  FIELD_NAME="$2"

  docker exec "$TARGET_CONTAINER" node -e "fetch(process.argv[1]).then((response) => response.json()).then((payload) => { const value = payload?.[process.argv[2]]; if (typeof value === 'boolean') { process.stdout.write(value ? 'true' : 'false'); return; } if (value == null) { process.stdout.write(''); return; } process.stdout.write(String(value)); }).catch(() => process.exit(1));" "$RUNTIME_ENDPOINT" "$FIELD_NAME"
}

set_container_draining() {
  TARGET_CONTAINER="$1"
  DRAINING_VALUE="$2"

  docker exec "$TARGET_CONTAINER" node -e "fetch(process.argv[1], { method: 'POST', headers: { 'content-type': 'application/json' }, body: JSON.stringify({ draining: process.argv[2] === 'true' }) }).then((response) => { if (!response.ok) process.exit(1); }).catch(() => process.exit(1));" "${RUNTIME_ENDPOINT}/drain" "$DRAINING_VALUE"
}

wait_for_previous_slot_drain() {
  TARGET_CONTAINER="$1"
  ELAPSED=0

  while [ "$ELAPSED" -lt "$PREVIOUS_SLOT_DRAIN_TIMEOUT_SECONDS" ]; do
    ACTIVE_COUNT=$(read_runtime_value "$TARGET_CONTAINER" activeChatRequestCount 2>/dev/null || printf '0')
    QUEUED_COUNT=$(read_runtime_value "$TARGET_CONTAINER" queuedChatRequestCount 2>/dev/null || printf '0')

    if [ "${ACTIVE_COUNT:-0}" = "0" ] && [ "${QUEUED_COUNT:-0}" = "0" ]; then
      return 0
    fi

    sleep 2
    ELAPSED=$((ELAPSED + 2))
  done

  echo "drain timeout reached for $TARGET_CONTAINER" >&2
  return 1
}

ensure_proxy_running() {
  docker compose -f "$COMPOSE_FILE" up -d --no-deps "$PROXY_SERVICE" >/dev/null
  docker exec "$PROXY_CONTAINER" nginx -s reload >/dev/null
}

ACTIVE_SLOT=$(resolve_active_slot)
TARGET_SLOT="green"
TARGET_SERVICE="$GREEN_SERVICE"
TARGET_CONTAINER="$GREEN_CONTAINER"
PREVIOUS_SERVICE="$BLUE_SERVICE"
PREVIOUS_CONTAINER="$BLUE_CONTAINER"

if [ "$ACTIVE_SLOT" = "green" ]; then
  TARGET_SLOT="blue"
  TARGET_SERVICE="$BLUE_SERVICE"
  TARGET_CONTAINER="$BLUE_CONTAINER"
  PREVIOUS_SERVICE="$GREEN_SERVICE"
  PREVIOUS_CONTAINER="$GREEN_CONTAINER"
fi

docker compose -f "$COMPOSE_FILE" up -d --build --force-recreate --no-deps "$TARGET_SERVICE"
wait_for_container_health "$TARGET_CONTAINER"
write_proxy_config "$TARGET_SLOT"
ensure_proxy_running
printf '%s\n' "$TARGET_SLOT" >"$ACTIVE_SLOT_FILE"

if [ "$PREVIOUS_SERVICE" != "$TARGET_SERVICE" ]; then
  set_container_draining "$PREVIOUS_CONTAINER" true
  wait_for_previous_slot_drain "$PREVIOUS_CONTAINER"
  docker compose -f "$COMPOSE_FILE" up -d --build --force-recreate --no-deps "$PREVIOUS_SERVICE"
  wait_for_container_health "$PREVIOUS_CONTAINER"
fi

printf 'work-server zero-downtime switch completed: %s -> %s\n' "$ACTIVE_SLOT" "$TARGET_SLOT"
