#!/usr/bin/env bash
# run-otel-test.sh - run the OTel pipeline for each cluster and persist output through local Vector
# sink types: dd (Datadog path) and nr (NewRelic path), each executed once
set -euo pipefail

SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
source "${SCRIPT_DIR}/test-common.sh"
BIN_DIR="${GENERATED_BIN_DIR}"
CONFIG_DIR="${GENERATED_CONFIG_DIR}"

# Wait for the Vector HTTP port to become ready (up to 20s)
wait_vector_ready() {
  local port="$1"
  local i
  for i in $(seq 1 20); do
    if curl -sf "http://127.0.0.1:${port}" > /dev/null 2>&1 || \
       nc -z 127.0.0.1 "${port}" 2>/dev/null; then
      return 0
    fi
    sleep 1
  done
  echo "Vector did not become ready on port ${port} within 20s" >&2
  return 1
}

# run_otel_test <binary> <cluster_id> <service_plan> <branch> <sink> <port_offset>
#   sink: dd | nr
#   port_offset: 0/100/200/300/400 - port offset for each parallel slot to avoid collisions
run_otel_test() {
  local binary="$1"
  local cluster_id="$2"
  local service_plan="$3"
  local branch="$4"
  local sink="$5"
  local port_offset="${6:-0}"

  local output_dir="${RESULTS_DIR}/${cluster_id}-${service_plan}/${branch}/otel-${sink}"
  mkdir -p "${output_dir}"

  # Base ports plus per-slot offsets to keep parallel runs isolated
  local base_grpc base_http base_api
  if [[ "${sink}" == "dd" ]]; then
    base_grpc=14317; base_http=14318; base_api=8686
  else
    base_grpc=24317; base_http=24318; base_api=8687
  fi
  local vector_grpc_port=$(( base_grpc + port_offset ))
  local vector_http_port=$(( base_http + port_offset ))
  local vector_api_port=$(( base_api  + port_offset ))
  local otel_health_port=$(( 13133 + port_offset ))

  # Generate a temporary Vector config with rewritten ports
  local tmp_vector_config
  tmp_vector_config=$(mktemp --suffix=.yaml)
  sed -e "s/0\.0\.0\.0:${base_grpc}/0.0.0.0:${vector_grpc_port}/g" \
      -e "s/0\.0\.0\.0:${base_http}/0.0.0.0:${vector_http_port}/g" \
      -e "s/0\.0\.0\.0:${base_api}/0.0.0.0:${vector_api_port}/g" \
      "${CONFIG_ROOT}/vector/vector-${sink}.yaml" > "${tmp_vector_config}"

  # Generate a temporary OTel config with rewritten endpoints
  local tmp_otel_config
  tmp_otel_config=$(mktemp --suffix=.yaml)
  sed -e "s/127\.0\.0\.1:${base_http}/127.0.0.1:${vector_http_port}/g" \
      -e "/^  health_check:$/a\\    endpoint: 127.0.0.1:${otel_health_port}" \
      "${CONFIG_ROOT}/otel/otel-config-${sink}.yaml" > "${tmp_otel_config}"

  local vector_output_dir
  vector_output_dir=$(mktemp -d)

  local container_name="vector-test-${sink}-${cluster_id}-${branch}"
  local file_prefix="${service_plan}-${cluster_id}"
  local pid=""
  local cleanup_done=0

  cleanup() {
    if [[ "${cleanup_done}" == "1" ]]; then
      return
    fi
    cleanup_done=1

    if [[ -n "${pid}" ]]; then
      kill "${pid}" 2>/dev/null || true
      wait "${pid}" 2>/dev/null || true
    fi
    docker stop "${container_name}" >/dev/null 2>&1 || true
    rm -rf "${vector_output_dir}"
    rm -f "${tmp_vector_config}" "${tmp_otel_config}" "${tmp_metrics_config:-}"
  }
  trap cleanup EXIT INT TERM

  echo "  [${branch}][${service_plan}][${sink}] starting Vector (http=${vector_http_port})..."
  # Remove any leftover container with the same name to avoid docker run exit 125
  docker stop "${container_name}" >/dev/null 2>&1 || true
  docker rm   "${container_name}" 2>/dev/null || true
  docker run -d --rm \
    --name "${container_name}" \
    --network host \
    -v "${tmp_vector_config}:/etc/vector/vector.yaml:ro" \
    -v "${vector_output_dir}:/output" \
    timberio/vector:0.47.0-debian \
    --config /etc/vector/vector.yaml \
    > "${output_dir}/vector-start-${file_prefix}.log" 2>&1

  wait_vector_ready "${vector_http_port}"
  sleep 2  # Extra wait to let the Vector source become fully ready

  echo "  starting pipeline binary... (metrics=$(( 8080 + port_offset )), health=${otel_health_port})"
  local otel_config="${tmp_otel_config}"
  local tmp_metrics_config
  tmp_metrics_config=$(mktemp --suffix=.yaml)
  sed -e "s/grpcPort: 10000/grpcPort: $(( 10000 + port_offset ))/" \
      -e "s/metricsPort: 9090/metricsPort: $(( 9090 + port_offset ))/" \
      -e "s/0\.0\.0\.0:8080/0.0.0.0:$(( 8080 + port_offset ))/" \
      "${CONFIG_DIR}/metrics-config-${service_plan}-${cluster_id}.yaml" > "${tmp_metrics_config}"

  "${BIN_DIR}/${binary}" \
    --config "${otel_config}" \
    --metrics-config "${tmp_metrics_config}" \
    > "${output_dir}/pipeline-${file_prefix}.log" 2>&1 &
  local pid=$!

  # Wait long enough for two collection intervals plus Auth0 token fetch time
  local wait_secs=120
  echo "  collecting data, waiting ${wait_secs}s..."
  sleep "${wait_secs}"

  # Stop the pipeline
  kill "${pid}" 2>/dev/null || true
  wait "${pid}" 2>/dev/null || true

  # Give Vector a few seconds to flush buffers
  sleep 5

  # Collect Vector logs before stopping the container because --rm removes it after stop
  docker logs "${container_name}" > "${output_dir}/vector-${file_prefix}.log" 2>&1 || true

  docker stop "${container_name}" >/dev/null 2>&1 || true

  # Merge Vector output files (time-sliced ndjson segments)
  local -a ndjson_files=()
  while IFS= read -r -d '' file; do
    ndjson_files+=("${file}")
  done < <(find "${vector_output_dir}" -name "*.ndjson" -print0 2>/dev/null | sort -z)
  if [[ "${#ndjson_files[@]}" -gt 0 ]]; then
    cat "${ndjson_files[@]}" > "${output_dir}/${file_prefix}.ndjson"
    local line_count
    line_count=$(wc -l < "${output_dir}/${file_prefix}.ndjson")
    echo "  ✅ Vector output: ${output_dir#"${REPO_ROOT}/"}/${file_prefix}.ndjson (${line_count} lines)"
  else
    echo "  ⚠️  Vector produced no NDJSON output. Check ${output_dir#"${TOOL_ROOT}/"}/vector-start-${file_prefix}.log and vector-${file_prefix}.log"
  fi

  local pipeline_lines
  pipeline_lines=$(wc -l < "${output_dir}/pipeline-${file_prefix}.log")
  echo "  Pipeline log: ${output_dir#"${TOOL_ROOT}/"}/pipeline-${file_prefix}.log (${pipeline_lines} lines)"

  trap - EXIT INT TERM
  cleanup
}

run_otel_test_if_ready() {
  local binary="$1"
  local cluster_id="$2"
  local service_plan="$3"
  local branch="$4"
  local sink="$5"
  local port_offset="${6:-0}"

  if [[ -z "${cluster_id}" ]]; then
    echo "  [${branch}][${service_plan}][${sink}] skipping: cluster_id is empty, you can retry later"
    return 0
  fi

  run_otel_test "${binary}" "${cluster_id}" "${service_plan}" "${branch}" "${sink}" "${port_offset}"
}

# run_otel_pair - start feature and baseline together for the same cluster/sink to keep timing aligned
# Usage: run_otel_pair <cluster_id> <service_plan> <sink> <feature_port_offset> <baseline_port_offset>
run_otel_pair() {
  local cluster_id="$1"
  local service_plan="$2"
  local sink="$3"
  local feature_port_offset="$4"
  local baseline_port_offset="$5"

  if [[ -z "${cluster_id}" ]]; then
    echo "  [${service_plan}][${sink}] skipping: cluster_id is empty"
    return 0
  fi

  echo "--- ${service_plan} [${sink}] (starting feature:${feature_port_offset} + ${BASELINE_LABEL}:${baseline_port_offset} together) ---"
  run_otel_test "metrics-collector-feature" "${cluster_id}" "${service_plan}" "feature" "${sink}" "${feature_port_offset}" &
  local pid_feature=$!
  run_otel_test "metrics-collector-${BASELINE_LABEL}" "${cluster_id}" "${service_plan}" "${BASELINE_LABEL}" "${sink}" "${baseline_port_offset}" &
  local pid_baseline=$!

  wait "${pid_feature}" || true
  wait "${pid_baseline}" || true
  echo ""
}

echo "================================================================"
echo " OTel Pipeline Test (Datadog + NewRelic paths)"
echo "================================================================"
echo ""
echo "Results round directory: ${RESULTS_DIR}"
echo ""

mkdir -p "${RESULTS_DIR}"

# ══════════════════════════════════════════════════════════════════
# Part 1: Datadog path (all clusters in parallel)
# ══════════════════════════════════════════════════════════════════
echo "----------------------------------------------------------------"
echo " Part 1: Datadog path"
echo "----------------------------------------------------------------"
echo ""

declare -a PIDS_DD=()
# port_offset: Premium=100, Essential=200, Starter=300, Dedicated pair=500/600
run_otel_pair "${CLUSTER_ID_DEDICATED}" "Dedicated" "dd" 500 600 &
PIDS_DD+=($!)
run_otel_test_if_ready "metrics-collector-feature" "${CLUSTER_ID_PREMIUM}"   "Premium"   "feature" "dd" 100 & PIDS_DD+=($!)
run_otel_test_if_ready "metrics-collector-feature" "${CLUSTER_ID_ESSENTIAL}" "Essential" "feature" "dd" 200 & PIDS_DD+=($!)
run_otel_test_if_ready "metrics-collector-feature" "${CLUSTER_ID_STARTER}"   "Starter"   "feature" "dd" 300 & PIDS_DD+=($!)

for pid in "${PIDS_DD[@]}"; do wait "${pid}" || true; done

# ══════════════════════════════════════════════════════════════════
# Part 2: NewRelic path (all clusters in parallel)
# ══════════════════════════════════════════════════════════════════
echo ""
echo "----------------------------------------------------------------"
echo " Part 2: NewRelic path"
echo "----------------------------------------------------------------"
echo ""

declare -a PIDS_NR=()
run_otel_pair "${CLUSTER_ID_DEDICATED}" "Dedicated" "nr" 500 600 &
PIDS_NR+=($!)
run_otel_test_if_ready "metrics-collector-feature" "${CLUSTER_ID_PREMIUM}"   "Premium"   "feature" "nr" 100 & PIDS_NR+=($!)
run_otel_test_if_ready "metrics-collector-feature" "${CLUSTER_ID_ESSENTIAL}" "Essential" "feature" "nr" 200 & PIDS_NR+=($!)
run_otel_test_if_ready "metrics-collector-feature" "${CLUSTER_ID_STARTER}"   "Starter"   "feature" "nr" 300 & PIDS_NR+=($!)

for pid in "${PIDS_NR[@]}"; do wait "${pid}" || true; done

echo ""
echo "✅ OTel test completed"
