#!/usr/bin/env bash
#
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements.  See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership.  The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License.  You may obtain a copy of the License at
#
#   http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied.  See the License for the
# specific language governing permissions and limitations
# under the License.
#

set -euo pipefail

# -----------------------------
# Config (overridable via args)
# -----------------------------
MASTER_REF="${MASTER_REF:-master}"           # branch or commit for "baseline"
PR_REF="${PR_REF:-HEAD}"                     # commit to test (assumes current checkout)
HOST="${HOST:-127.0.0.1}"
PORT="${PORT:-8090}"
WAIT_SECS="${WAIT_SECS:-60}"
BATCHES="${BATCHES:-50}"
MSGS_PER_BATCH="${MSGS_PER_BATCH:-100}"
KEEP_TMP="${KEEP_TMP:-false}"

# -----------------------------
# Helpers
# -----------------------------
info(){ printf "\n\033[1;36m➤ %s\033[0m\n" "$*"; }
ok(){   printf "\033[0;32m✓ %s\033[0m\n" "$*"; }
err(){  printf "\033[0;31m✗ %s\033[0m\n" "$*" >&2; }
die(){  err "$*"; exit 1; }

need() {
  command -v "$1" >/dev/null 2>&1 || die "missing dependency: $1"
}

wait_for_port() {
  local host="$1" port="$2" deadline=$((SECONDS + WAIT_SECS))
  while (( SECONDS < deadline )); do
    if command -v nc >/dev/null 2>&1; then
      if nc -z "$host" "$port" 2>/dev/null; then return 0; fi
    else
      if (echo >"/dev/tcp/$host/$port") >/dev/null 2>&1; then return 0; fi
    fi
    sleep 1
  done
  return 1
}

stop_pid() {
  local pid="$1" name="${2:-process}"
  if kill -0 "$pid" 2>/dev/null; then
    kill -TERM "$pid" || true
    for _ in $(seq 1 15); do
      kill -0 "$pid" 2>/dev/null || { ok "stopped $name (pid $pid)"; return 0; }
      sleep 1
    done
    err "$name (pid $pid) still running; sending SIGKILL"
    kill -KILL "$pid" || true
  fi
}

print_logs_if_any() {
  local dir="$1"
  if compgen -G "$dir/local_data/logs/iggy*" > /dev/null; then
    echo "---- $dir/local_data/logs ----"
    cat "$dir"/local_data/logs/iggy* || true
    echo "------------------------------"
  else
    echo "(no iggy logs found in $dir/local_data/logs)"
  fi
}

# -----------------------------
# Args
# -----------------------------
usage() {
  cat <<EOF
Usage: $0 [--master-ref REF] [--pr-ref REF] [--host 127.0.0.1] [--port 8090]
          [--wait-secs 60] [--batches 50] [--msgs-per-batch 100] [--keep-tmp]

Runs a backwards-compatibility check:
  1) Build and run master (or REF), write data via benches
  2) Stop server, capture local_data/
  3) Build PR (or REF), restore local_data/
  4) Run consumer bench; fail if anything breaks

Env overrides supported: MASTER_REF, PR_REF, HOST, PORT, WAIT_SECS, BATCHES, MSGS_PER_BATCH, KEEP_TMP
EOF
}

while [[ $# -gt 0 ]]; do
  case "$1" in
    --master-ref) MASTER_REF="$2"; shift 2;;
    --pr-ref) PR_REF="$2"; shift 2;;
    --host) HOST="$2"; shift 2;;
    --port) PORT="$2"; shift 2;;
    --wait-secs) WAIT_SECS="$2"; shift 2;;
    --batches) BATCHES="$2"; shift 2;;
    --msgs-per-batch) MSGS_PER_BATCH="$2"; shift 2;;
    --keep-tmp) KEEP_TMP=true; shift;;
    -h|--help) usage; exit 0;;
    *) die "unknown arg: $1";;
  esac
done

# -----------------------------
# Pre-flight
# -----------------------------
need git
need cargo
need pkill
need pgrep
need awk
need sed
command -v timeout >/dev/null 2>&1 || true  # optional, we'll use it if present

REPO_ROOT="$(git rev-parse --show-toplevel 2>/dev/null || pwd)"
cd "$REPO_ROOT"

# Free the port proactively (best-effort)
pkill -f iggy-server >/dev/null 2>&1 || true

TMP_ROOT="$(mktemp -d -t iggy-backcompat-XXXXXX)"
MASTER_DIR="$TMP_ROOT/master"
PR_DIR="$REPO_ROOT"  # assume script is run from PR checkout
MASTER_LOG="$TMP_ROOT/server-master.stdout.log"
PR_LOG="$TMP_ROOT/server-pr.stdout.log"

cleanup() {
  # Stop any leftover iggy-server
  pkill -f iggy-server >/dev/null 2>&1 || true
  git worktree remove --force "$MASTER_DIR" >/dev/null 2>&1 || true
  if [[ "$KEEP_TMP" != "true" ]]; then
    rm -rf "$TMP_ROOT" || true
  else
    info "keeping temp dir: $TMP_ROOT"
  fi
}
trap cleanup EXIT

# -----------------------------
# 1) Prepare master worktree
# -----------------------------
info "Preparing baseline worktree at '$MASTER_REF'"
git fetch --all --tags --prune >/dev/null 2>&1 || true
git worktree add --force "$MASTER_DIR" "$MASTER_REF"
ok "worktree at $MASTER_DIR"

# -----------------------------
# 2) Build & run master server
# -----------------------------
pushd "$MASTER_DIR" >/dev/null

info "Building iggy-server & benches (baseline: $MASTER_REF)"
cargo build --locked --bin iggy-server --bin iggy-bench
ok "built baseline"

info "Starting iggy-server (baseline)"
set +e
( IGGY_ROOT_USERNAME=iggy IGGY_ROOT_PASSWORD=iggy nohup target/debug/iggy-server >"$MASTER_LOG" 2>&1 & echo $! > "$TMP_ROOT/master.pid" )
set -e
MASTER_PID="$(cat "$TMP_ROOT/master.pid")"
ok "iggy-server started (pid $MASTER_PID), logs: $MASTER_LOG"

info "Waiting for $HOST:$PORT to be ready (up to ${WAIT_SECS}s)"
if ! wait_for_port "$HOST" "$PORT"; then
  err "server did not become ready in ${WAIT_SECS}s"
  print_logs_if_any "$MASTER_DIR"
  [[ -f "$MASTER_LOG" ]] && { tail -n 200 "$MASTER_LOG" || true; }
  exit 1
fi
ok "server is ready"

# Producer bench (baseline)
info "Running producer bench on baseline"
BENCH_CMD=( target/debug/iggy-bench --message-batches "$BATCHES" --messages-per-batch "$MSGS_PER_BATCH" pinned-producer tcp )
if command -v timeout >/dev/null 2>&1; then timeout 60s "${BENCH_CMD[@]}"; else "${BENCH_CMD[@]}"; fi
ok "producer bench done"

# Consumer bench (baseline)
info "Running consumer bench on baseline"
BENCH_CMD=( target/debug/iggy-bench --message-batches "$BATCHES" --messages-per-batch "$MSGS_PER_BATCH" pinned-consumer tcp )
if command -v timeout >/dev/null 2>&1; then timeout 60s "${BENCH_CMD[@]}"; else "${BENCH_CMD[@]}"; fi
ok "consumer bench done (baseline)"

# Stop baseline server
info "Stopping baseline server"
stop_pid "$MASTER_PID" "iggy-server(baseline)"
print_logs_if_any "$MASTER_DIR"

# Clean baseline logs (like CI step)
if compgen -G "local_data/logs/iggy*" > /dev/null; then
  rm -f local_data/logs/iggy* || true
fi

# Snapshot local_data/
info "Snapshotting baseline local_data/"
cp -a local_data "$TMP_ROOT/local_data"
ok "snapshot stored at $TMP_ROOT/local_data"

popd >/dev/null

# -----------------------------
# 3) Build PR & restore data
# -----------------------------
pushd "$PR_DIR" >/dev/null
info "Ensuring PR ref is present: $PR_REF"
git rev-parse --verify "$PR_REF^{commit}" >/dev/null 2>&1 || die "PR_REF '$PR_REF' not found"
git checkout -q "$PR_REF"

info "Building iggy-server & benches (PR: $PR_REF)"
cargo build --locked --bin iggy-server --bin iggy-bench
ok "built PR"

info "Restoring baseline local_data/ into PR workspace"
rm -rf local_data
cp -a "$TMP_ROOT/local_data" ./local_data
ok "restored local_data/"

# -----------------------------
# 4) Run PR server & consumer bench
# -----------------------------
info "Starting iggy-server (PR)"
set +e
( IGGY_ROOT_USERNAME=iggy IGGY_ROOT_PASSWORD=iggy nohup target/debug/iggy-server >"$PR_LOG" 2>&1 & echo $! > "$TMP_ROOT/pr.pid" )
set -e
PR_PID="$(cat "$TMP_ROOT/pr.pid")"
ok "iggy-server (PR) started (pid $PR_PID), logs: $PR_LOG"

info "Waiting for $HOST:$PORT to be ready (up to ${WAIT_SECS}s)"
if ! wait_for_port "$HOST" "$PORT"; then
  err "PR server did not become ready in ${WAIT_SECS}s"
  print_logs_if_any "$PR_DIR"
  [[ -f "$PR_LOG" ]] && { tail -n 200 "$PR_LOG" || true; }
  exit 1
fi
ok "PR server is ready"

# Only consumer bench against PR
info "Running consumer bench on PR (compat check)"
BENCH_CMD=( target/debug/iggy-bench --message-batches "$BATCHES" --messages-per-batch "$MSGS_PER_BATCH" pinned-consumer tcp )
if command -v timeout >/dev/null 2>&1; then timeout 60s "${BENCH_CMD[@]}"; else "${BENCH_CMD[@]}"; fi
ok "consumer bench done (PR)"

# Stop PR server
info "Stopping PR server"
stop_pid "$PR_PID" "iggy-server(PR)"
print_logs_if_any "$PR_DIR"

ok "backwards-compatibility check PASSED"
popd >/dev/null
