#!/usr/bin/env bash

set -euo pipefail

SCRIPT_DIR=$(CDPATH= cd -- "$(dirname -- "$0")" && pwd)
REPO_ROOT=$(CDPATH= cd -- "$SCRIPT_DIR/.." && pwd)

CORE_BIN=${CORE_BIN:-"$REPO_ROOT/target/debug/easytier-core"}
CLI_BIN=${CLI_BIN:-"$REPO_ROOT/target/debug/easytier-cli"}
TMPDIR_PATH=""
CORE_PID=""
PYTHON_BIN=${PYTHON_BIN:-python3}

print_section() {
  printf '\n==> %s\n' "$1"
}

print_output() {
  local title="$1"
  local content="$2"
  printf -- '---- %s ----\n' "$title"
  printf '%s\n' "$content"
  printf -- '---- end %s ----\n' "$title"
}

build_binaries() {
  print_section "Building easytier-core and easytier-cli"
  cargo build -p easytier --bin easytier-core --bin easytier-cli
}

ensure_binaries() {
  if [[ "${SKIP_BUILD:-0}" != "1" ]] || [[ ! -x "$CORE_BIN" ]] || [[ ! -x "$CLI_BIN" ]]; then
    build_binaries
  fi
}

make_tmpdir() {
  "$PYTHON_BIN" - <<'PY'
import tempfile
print(tempfile.mkdtemp(prefix="easytier-cli-e2e-"))
PY
}

cleanup_tmpdir() {
  TMPDIR_TO_DELETE="$1" "$PYTHON_BIN" - <<'PY'
import os
import shutil

shutil.rmtree(os.environ["TMPDIR_TO_DELETE"], ignore_errors=True)
PY
}

alloc_port() {
  "$PYTHON_BIN" - <<'PY'
import socket

sock = socket.socket()
sock.bind(("127.0.0.1", 0))
print(sock.getsockname()[1])
sock.close()
PY
}

wait_for_cli() {
  local rpc_port="$1"
  local attempts=0
  while (( attempts < 50 )); do
    if "$CLI_BIN" -p "127.0.0.1:${rpc_port}" -o json node >/dev/null 2>&1; then
      return 0
    fi
    attempts=$((attempts + 1))
    sleep 0.2
  done
  return 1
}

run_cmd() {
  local __var_name="$1"
  local title="$2"
  shift 2

  print_section "$title"
  printf '+'
  for arg in "$@"; do
    printf ' %q' "$arg"
  done
  printf '\n'

  local output
  if ! output=$("$@" 2>&1); then
    print_output "$title output" "$output"
    return 1
  fi

  print_output "$title output" "$output"
  printf -v "$__var_name" '%s' "$output"
}

assert_text_output() {
  local text_output="$1"
  grep -F '== e2e-inst-a (' <<<"$text_output" >/dev/null
  grep -F '== e2e-inst-b (' <<<"$text_output" >/dev/null
}

assert_multi_instance_json() {
  local json_payload="$1"
  JSON_PAYLOAD="$json_payload" "$PYTHON_BIN" - <<'PY'
import json
import os

data = json.loads(os.environ["JSON_PAYLOAD"])
assert isinstance(data, list), data
assert len(data) == 2, data

names = {item["instance_name"] for item in data}
assert names == {"e2e-inst-a", "e2e-inst-b"}, names

for item in data:
    assert item["instance_id"], item
    assert isinstance(item["result"], dict), item
PY
}

assert_single_instance_json() {
  local json_payload="$1"
  JSON_PAYLOAD="$json_payload" "$PYTHON_BIN" - <<'PY'
import json
import os

data = json.loads(os.environ["JSON_PAYLOAD"])
assert isinstance(data, dict), data
assert data["config"].find('instance_name = "e2e-inst-a"') >= 0, data["config"]
PY
}

assert_whitelist_fanout() {
  local json_payload="$1"
  JSON_PAYLOAD="$json_payload" "$PYTHON_BIN" - <<'PY'
import json
import os

data = json.loads(os.environ["JSON_PAYLOAD"])
assert len(data) == 2, data
for item in data:
    assert item["result"]["tcp_ports"] == ["80", "443"], item
    assert item["result"]["udp_ports"] == [], item
PY
}

assert_single_instance_write() {
  local json_payload="$1"
  JSON_PAYLOAD="$json_payload" "$PYTHON_BIN" - <<'PY'
import json
import os

data = {item["instance_name"]: item["result"] for item in json.loads(os.environ["JSON_PAYLOAD"])}
assert data["e2e-inst-a"]["tcp_ports"] == ["80", "443"], data
assert data["e2e-inst-b"]["tcp_ports"] == [], data
PY
}

main() {
  command -v "$PYTHON_BIN" >/dev/null 2>&1 || {
    echo "python interpreter not found: $PYTHON_BIN" >&2
    exit 1
  }

  ensure_binaries

  TMPDIR_PATH=$(make_tmpdir)
  print_section "Created temporary test directory"
  printf '%s\n' "$TMPDIR_PATH"

  local rpc_port
  rpc_port=$(alloc_port)
  print_section "Allocated RPC port"
  printf '%s\n' "$rpc_port"

  cleanup() {
    if [[ -n "$CORE_PID" ]] && kill -0 "$CORE_PID" >/dev/null 2>&1; then
      kill "$CORE_PID" >/dev/null 2>&1 || true
      wait "$CORE_PID" >/dev/null 2>&1 || true
    fi
    if [[ -n "$TMPDIR_PATH" ]]; then
      cleanup_tmpdir "$TMPDIR_PATH"
    fi
  }
  trap cleanup EXIT

  cat >"$TMPDIR_PATH/inst-a.toml" <<'EOF'
instance_name = "e2e-inst-a"
listeners = []

[network_identity]
network_name = "e2e-net-a"
network_secret = ""

[flags]
no_tun = true
enable_ipv6 = false
EOF

  cat >"$TMPDIR_PATH/inst-b.toml" <<'EOF'
instance_name = "e2e-inst-b"
listeners = []

[network_identity]
network_name = "e2e-net-b"
network_secret = ""

[flags]
no_tun = true
enable_ipv6 = false
EOF

  "$CORE_BIN" --config-dir "$TMPDIR_PATH" --rpc-portal "127.0.0.1:${rpc_port}" \
    >"$TMPDIR_PATH/core.log" 2>&1 &
  CORE_PID=$!
  print_section "Started easytier-core"
  printf 'pid=%s\n' "$CORE_PID"

  wait_for_cli "$rpc_port"
  print_output "easytier-core startup log" "$(cat "$TMPDIR_PATH/core.log")"

  local text_output
  run_cmd text_output \
    "Case 1: node fanout in table mode" \
    "$CLI_BIN" -p "127.0.0.1:${rpc_port}" node
  assert_text_output "$text_output"

  local json_output
  run_cmd json_output \
    "Case 2: node fanout in JSON mode" \
    "$CLI_BIN" -p "127.0.0.1:${rpc_port}" -o json node
  assert_multi_instance_json "$json_output"

  local single_output
  run_cmd single_output \
    "Case 3: explicit instance selector stays single-instance" \
    "$CLI_BIN" -p "127.0.0.1:${rpc_port}" --instance-name e2e-inst-a -o json node
  assert_single_instance_json "$single_output"

  local set_whitelist_output
  run_cmd set_whitelist_output \
    "Case 4: whitelist set-tcp fans out to all instances" \
    "$CLI_BIN" -p "127.0.0.1:${rpc_port}" whitelist set-tcp 80,443

  local whitelist_output
  run_cmd whitelist_output \
    "Case 5: whitelist show confirms fanout write" \
    "$CLI_BIN" -p "127.0.0.1:${rpc_port}" -o json whitelist show
  assert_whitelist_fanout "$whitelist_output"

  local clear_whitelist_output
  run_cmd clear_whitelist_output \
    "Case 6: explicit selector write only touches one instance" \
    "$CLI_BIN" -p "127.0.0.1:${rpc_port}" --instance-name e2e-inst-b whitelist clear-tcp

  local cleared_output
  run_cmd cleared_output \
    "Case 7: whitelist show confirms single-instance write isolation" \
    "$CLI_BIN" -p "127.0.0.1:${rpc_port}" -o json whitelist show
  assert_single_instance_write "$cleared_output"

  print_section "Result"
  echo "CLI multi-instance E2E passed"
}

main "$@"
