#!/usr/bin/env sh

set -eu

SCRIPT_DIR=$(CDPATH= cd -- "$(dirname -- "$0")" && pwd)
BOOTSTRAP="$SCRIPT_DIR/getagentflow.sh"
LINUX_INSTALLER="$SCRIPT_DIR/agentflow-linux.sh"
MACOS_INSTALLER="$SCRIPT_DIR/agentflow-macos.sh"

for required in "$BOOTSTRAP" "$LINUX_INSTALLER" "$MACOS_INSTALLER"; do
  if [ ! -f "$required" ]; then
    echo "required script not found: $required" >&2
    exit 1
  fi
done

sha256_file() {
  if command -v sha256sum >/dev/null 2>&1; then
    sha256sum "$1" | awk '{print $1}'
    return
  fi
  if command -v shasum >/dev/null 2>&1; then
    shasum -a 256 "$1" | awk '{print $1}'
    return
  fi
  openssl dgst -sha256 "$1" | awk '{print $NF}'
}

create_test_binary() {
  output_path=$1
  version_text=$2
  cat >"$output_path" <<EOF
#!/usr/bin/env sh
if [ "\${1:-}" = "--version" ]; then
  printf '%s\n' '$version_text'
  exit 0
fi
printf '%s\n' '$version_text'
EOF
  chmod +x "$output_path"
}

create_long_running_test_binary() {
  output_path=$1
  version_text=$2
  cat >"$output_path" <<EOF
#!/usr/bin/env sh
if [ "\${1:-}" = "--version" ]; then
  printf '%s\n' '$version_text'
  exit 0
fi
trap 'exit 0' TERM INT
while :; do
  sleep 1
done
EOF
  chmod +x "$output_path"
}

assert_path_exists() {
  path=$1
  if [ ! -e "$path" ]; then
    echo "expected path to exist: $path" >&2
    exit 1
  fi
}

assert_contains() {
  needle=$1
  haystack_file=$2
  if ! grep -F "$needle" "$haystack_file" >/dev/null 2>&1; then
    echo "expected to find '$needle' in $haystack_file" >&2
    exit 1
  fi
}

assert_not_contains() {
  needle=$1
  haystack_file=$2
  if grep -F "$needle" "$haystack_file" >/dev/null 2>&1; then
    echo "expected not to find '$needle' in $haystack_file" >&2
    exit 1
  fi
}

assert_file_not_contains() {
  needle=$1
  path=$2
  if grep -F "$needle" "$path" >/dev/null 2>&1; then
    echo "expected not to find '$needle' in $path" >&2
    exit 1
  fi
}

assert_equals() {
  expected=$1
  actual=$2
  if [ "$expected" != "$actual" ]; then
    echo "expected '$expected' but got '$actual'" >&2
    exit 1
  fi
}

write_version_json() {
  build_dir=$1
  linux_binary=$2
  macos_binary=$3
  linux_sha=$(sha256_file "$linux_binary")
  macos_sha=$(sha256_file "$macos_binary")

  cat >"$build_dir/version.json" <<EOF
{
  "version": "2.3.4",
  "platforms": {
    "linux": {
      "amd64": {
        "file": "agentflow-linux-amd64",
        "sha256": "$linux_sha",
        "url": "file://$linux_binary"
      }
    },
    "darwin": {
      "arm64": {
        "file": "agentflow-darwin-arm64",
        "sha256": "$macos_sha",
        "url": "file://$macos_binary"
      }
    }
  }
}
EOF
}

run_linux_install_case() {
  tmpdir=$(mktemp -d)
  trap 'rm -rf "$tmpdir"' EXIT INT TERM

  home_dir="$tmpdir/home"
  build_dir="$tmpdir/build"
  mkdir -p "$home_dir" "$build_dir"
  : >"$home_dir/.profile"

  binary_path="$build_dir/agentflow-linux-amd64"
  create_test_binary "$binary_path" 'linux-2.3.4'
  macos_binary_path="$build_dir/agentflow-darwin-arm64"
  create_test_binary "$macos_binary_path" 'darwin-2.3.4'
  write_version_json "$build_dir" "$binary_path" "$macos_binary_path"

  env \
    HOME="$home_dir" \
    SHELL="/bin/sh" \
    AGENTFLOW_VERSION_JSON_URL="file://$build_dir/version.json" \
    AGENTFLOW_SKIP_SERVICE=1 \
    AGENTFLOW_INSTALLER_ARCH=amd64 \
    bash "$LINUX_INSTALLER" install >"$tmpdir/linux-install.log"

  assert_path_exists "$home_dir/.agentflow/bin/agentflow"
  assert_path_exists "$home_dir/.agentflow/bin/agentflowctl"
  assert_path_exists "$home_dir/.agentflow/bin/codex-agentflow"
  assert_path_exists "$home_dir/.agentflow/data"
  assert_path_exists "$home_dir/.agentflow/logs"
  assert_path_exists "$home_dir/.agentflow/run"
  assert_path_exists "$home_dir/.config/systemd/user/agentflow.service"
  assert_not_contains 'AGENT_FLOW_DATA=' "$home_dir/.config/systemd/user/agentflow.service"
  assert_contains 'Environment=CODEX_EXECUTABLE=%h/.agentflow/bin/codex-agentflow' "$home_dir/.config/systemd/user/agentflow.service"
  assert_contains 'exec codex "$@"' "$home_dir/.agentflow/bin/codex-agentflow"
  assert_contains 'agentflow installer' "$home_dir/.profile"
  assert_contains 'export PATH="$HOME/.agentflow/bin:$PATH"' "$home_dir/.profile"
  assert_not_contains 'AGENT_FLOW_DATA=' "$home_dir/.profile"
  assert_contains 'Open Browser URL: http://127.0.0.1:48285' "$tmpdir/linux-install.log"
  assert_contains 'export PATH="' "$tmpdir/linux-install.log"
  assert_not_contains 'export AGENT_FLOW_DATA="' "$tmpdir/linux-install.log"

  if [ "$("$home_dir/.agentflow/bin/agentflow" --version)" != "linux-2.3.4" ]; then
    echo "linux installer produced unexpected version" >&2
    exit 1
  fi

  rm -rf "$tmpdir"
  trap - EXIT INT TERM
}

run_linux_install_creates_global_bin_links_case() {
  tmpdir=$(mktemp -d)
  trap 'rm -rf "$tmpdir"' EXIT INT TERM

  home_dir="$tmpdir/home"
  build_dir="$tmpdir/build"
  global_bin_dir="$tmpdir/global-bin"
  mkdir -p "$home_dir" "$build_dir" "$global_bin_dir"
  : >"$home_dir/.profile"

  binary_path="$build_dir/agentflow-linux-amd64"
  create_test_binary "$binary_path" 'linux-2.3.4'
  macos_binary_path="$build_dir/agentflow-darwin-arm64"
  create_test_binary "$macos_binary_path" 'darwin-2.3.4'
  write_version_json "$build_dir" "$binary_path" "$macos_binary_path"

  env \
    HOME="$home_dir" \
    SHELL="/bin/sh" \
    AGENTFLOW_VERSION_JSON_URL="file://$build_dir/version.json" \
    AGENTFLOW_SKIP_SERVICE=1 \
    AGENTFLOW_INSTALLER_ARCH=amd64 \
    AGENTFLOW_GLOBAL_BIN_DIR="$global_bin_dir" \
    bash "$LINUX_INSTALLER" install >"$tmpdir/linux-global-bin.log"

  assert_path_exists "$global_bin_dir/agentflow"
  assert_path_exists "$global_bin_dir/agentflowctl"
  assert_contains "$global_bin_dir" "$tmpdir/linux-global-bin.log"

  if [ "$("$global_bin_dir/agentflow" --version)" != "linux-2.3.4" ]; then
    echo "global agentflow link returned unexpected version" >&2
    exit 1
  fi

  rm -rf "$tmpdir"
  trap - EXIT INT TERM
}

run_macos_install_case() {
  tmpdir=$(mktemp -d)
  trap 'rm -rf "$tmpdir"' EXIT INT TERM

  home_dir="$tmpdir/home"
  build_dir="$tmpdir/build"
  mkdir -p "$home_dir" "$build_dir"
  : >"$home_dir/.profile"

  linux_binary_path="$build_dir/agentflow-linux-amd64"
  create_test_binary "$linux_binary_path" 'linux-2.3.4'
  binary_path="$build_dir/agentflow-darwin-arm64"
  create_test_binary "$binary_path" 'darwin-2.3.4'
  write_version_json "$build_dir" "$linux_binary_path" "$binary_path"

  env \
    HOME="$home_dir" \
    SHELL="/bin/sh" \
    AGENTFLOW_VERSION_JSON_URL="file://$build_dir/version.json" \
    AGENTFLOW_SKIP_SERVICE=1 \
    AGENTFLOW_INSTALLER_ARCH=arm64 \
    bash "$MACOS_INSTALLER" install >"$tmpdir/macos-install.log"

  assert_path_exists "$home_dir/.agentflow/bin/agentflow"
  assert_path_exists "$home_dir/.agentflow/bin/agentflowctl"
  assert_path_exists "$home_dir/.agentflow/bin/codex-agentflow"
  assert_path_exists "$home_dir/.agentflow/data"
  assert_path_exists "$home_dir/.agentflow/logs"
  assert_path_exists "$home_dir/.agentflow/run"
  assert_path_exists "$home_dir/Library/LaunchAgents/io.coolvibe.agentflow.plist"
  assert_not_contains 'AGENT_FLOW_DATA' "$home_dir/Library/LaunchAgents/io.coolvibe.agentflow.plist"
  assert_contains '<key>CODEX_EXECUTABLE</key>' "$home_dir/Library/LaunchAgents/io.coolvibe.agentflow.plist"
  assert_contains "$home_dir/.agentflow/bin/codex-agentflow" "$home_dir/Library/LaunchAgents/io.coolvibe.agentflow.plist"
  assert_contains 'exec codex "$@"' "$home_dir/.agentflow/bin/codex-agentflow"
  assert_contains 'agentflow installer' "$home_dir/.profile"
  assert_contains 'export PATH="$HOME/.agentflow/bin:$PATH"' "$home_dir/.profile"
  assert_not_contains 'AGENT_FLOW_DATA=' "$home_dir/.profile"
  assert_contains 'Open Browser URL: http://127.0.0.1:48285' "$tmpdir/macos-install.log"
  assert_contains 'export PATH="' "$tmpdir/macos-install.log"
  assert_not_contains 'export AGENT_FLOW_DATA="' "$tmpdir/macos-install.log"

  if [ "$("$home_dir/.agentflow/bin/agentflow" --version)" != "darwin-2.3.4" ]; then
    echo "macos installer produced unexpected version" >&2
    exit 1
  fi

  rm -rf "$tmpdir"
  trap - EXIT INT TERM
}

run_bootstrap_dispatch_case() {
  tmpdir=$(mktemp -d)
  trap 'rm -rf "$tmpdir"' EXIT INT TERM

  home_dir="$tmpdir/home"
  build_dir="$tmpdir/build"
  release_dir="$tmpdir/releases"
  mkdir -p "$home_dir" "$build_dir" "$release_dir"
  : >"$home_dir/.profile"

  linux_binary_path="$build_dir/agentflow-linux-amd64"
  create_test_binary "$linux_binary_path" 'linux-2.3.4'
  macos_binary_path="$build_dir/agentflow-darwin-arm64"
  create_test_binary "$macos_binary_path" 'darwin-2.3.4'
  write_version_json "$build_dir" "$linux_binary_path" "$macos_binary_path"

  cp "$LINUX_INSTALLER" "$release_dir/agentflow-linux.sh"
  cp "$MACOS_INSTALLER" "$release_dir/agentflow-macos.sh"

  env \
    HOME="$home_dir" \
    SHELL="/bin/sh" \
    AGENTFLOW_INSTALLER_BASE_URL="file://$release_dir" \
    AGENTFLOW_VERSION_JSON_URL="file://$build_dir/version.json" \
    AGENTFLOW_SKIP_SERVICE=1 \
    AGENTFLOW_BOOTSTRAP_OS=Linux \
    AGENTFLOW_INSTALLER_ARCH=amd64 \
    bash "$BOOTSTRAP" >"$tmpdir/bootstrap-install.log"

  assert_path_exists "$home_dir/.agentflow/bin/agentflow"
  assert_path_exists "$home_dir/.agentflow/bin/agentflowctl"
  assert_contains 'Open Browser URL: http://127.0.0.1:48285' "$tmpdir/bootstrap-install.log"
  if [ "$("$home_dir/.agentflow/bin/agentflow" --version)" != "linux-2.3.4" ]; then
    echo "bootstrap installed unexpected version" >&2
    exit 1
  fi

  rm -rf "$tmpdir"
  trap - EXIT INT TERM
}

run_bootstrap_without_systemd_case() {
  tmpdir=$(mktemp -d)
  trap 'rm -rf "$tmpdir"' EXIT INT TERM

  home_dir="$tmpdir/home"
  build_dir="$tmpdir/build"
  release_dir="$tmpdir/releases"
  fake_bin_dir="$tmpdir/fake-bin"
  mkdir -p "$home_dir" "$build_dir" "$release_dir" "$fake_bin_dir"
  : >"$home_dir/.profile"

  linux_binary_path="$build_dir/agentflow-linux-amd64"
  create_test_binary "$linux_binary_path" 'linux-2.3.4'
  macos_binary_path="$build_dir/agentflow-darwin-arm64"
  create_test_binary "$macos_binary_path" 'darwin-2.3.4'
  write_version_json "$build_dir" "$linux_binary_path" "$macos_binary_path"

  cp "$LINUX_INSTALLER" "$release_dir/agentflow-linux.sh"
  cp "$MACOS_INSTALLER" "$release_dir/agentflow-macos.sh"

  cat >"$fake_bin_dir/systemctl" <<'EOF'
#!/usr/bin/env sh
printf '%s\n' 'systemctl is unavailable in this test environment' >&2
exit 1
EOF
  chmod +x "$fake_bin_dir/systemctl"

  env \
    HOME="$home_dir" \
    SHELL="/bin/sh" \
    PATH="$fake_bin_dir:$PATH" \
    AGENTFLOW_INSTALLER_BASE_URL="file://$release_dir" \
    AGENTFLOW_VERSION_JSON_URL="file://$build_dir/version.json" \
    AGENTFLOW_BOOTSTRAP_OS=Linux \
    AGENTFLOW_INSTALLER_ARCH=amd64 \
    bash "$BOOTSTRAP" >"$tmpdir/bootstrap-no-systemd.log"

  assert_path_exists "$home_dir/.agentflow/bin/agentflow"
  assert_path_exists "$home_dir/.agentflow/bin/agentflowctl"
  if [ "$("$home_dir/.agentflow/bin/agentflow" --version)" != "linux-2.3.4" ]; then
    echo "bootstrap without systemd installed unexpected version" >&2
    exit 1
  fi

  rm -rf "$tmpdir"
  trap - EXIT INT TERM
}

run_linux_update_without_systemd_case() {
  tmpdir=$(mktemp -d)
  trap 'rm -rf "$tmpdir"' EXIT INT TERM

  home_dir="$tmpdir/home"
  build_dir="$tmpdir/build"
  fake_bin_dir="$tmpdir/fake-bin"
  mkdir -p "$home_dir" "$build_dir" "$fake_bin_dir"
  : >"$home_dir/.profile"

  initial_binary_path="$build_dir/agentflow-linux-amd64-initial"
  create_test_binary "$initial_binary_path" 'linux-2.3.3'
  updated_binary_path="$build_dir/agentflow-linux-amd64"
  create_test_binary "$updated_binary_path" 'linux-2.3.4'
  macos_binary_path="$build_dir/agentflow-darwin-arm64"
  create_test_binary "$macos_binary_path" 'darwin-2.3.4'
  write_version_json "$build_dir" "$updated_binary_path" "$macos_binary_path"

  cat >"$fake_bin_dir/systemctl" <<'EOF'
#!/usr/bin/env sh
printf '%s\n' 'systemctl is unavailable in this test environment' >&2
exit 1
EOF
  chmod +x "$fake_bin_dir/systemctl"

  mkdir -p "$home_dir/.agentflow/bin"
  cp "$initial_binary_path" "$home_dir/.agentflow/bin/agentflow"
  chmod +x "$home_dir/.agentflow/bin/agentflow"

  env \
    HOME="$home_dir" \
    SHELL="/bin/sh" \
    PATH="$fake_bin_dir:$PATH" \
    AGENTFLOW_VERSION_JSON_URL="file://$build_dir/version.json" \
    AGENTFLOW_INSTALLER_ARCH=amd64 \
    bash "$LINUX_INSTALLER" update >"$tmpdir/linux-update-no-systemd.log"

  if [ "$("$home_dir/.agentflow/bin/agentflow" --version)" != "linux-2.3.4" ]; then
    echo "linux update without systemd installed unexpected version" >&2
    exit 1
  fi

  rm -rf "$tmpdir"
  trap - EXIT INT TERM
}

run_linux_interactive_update_keeps_stopped_when_user_declines_restart_case() {
  tmpdir=$(mktemp -d)
  trap 'rm -rf "$tmpdir"' EXIT INT TERM

  home_dir="$tmpdir/home"
  build_dir="$tmpdir/build"
  fake_bin_dir="$tmpdir/fake-bin"
  mkdir -p "$home_dir" "$build_dir" "$fake_bin_dir" "$home_dir/.agentflow/bin" "$home_dir/.agentflow/run"
  : >"$home_dir/.profile"

  initial_binary_path="$build_dir/agentflow-linux-amd64-initial"
  create_long_running_test_binary "$initial_binary_path" 'linux-2.3.3'
  updated_binary_path="$build_dir/agentflow-linux-amd64"
  create_long_running_test_binary "$updated_binary_path" 'linux-2.3.4'
  macos_binary_path="$build_dir/agentflow-darwin-arm64"
  create_test_binary "$macos_binary_path" 'darwin-2.3.4'
  write_version_json "$build_dir" "$updated_binary_path" "$macos_binary_path"

  cat >"$fake_bin_dir/systemctl" <<'EOF'
#!/usr/bin/env sh
printf '%s\n' 'systemctl is unavailable in this test environment' >&2
exit 1
EOF
  chmod +x "$fake_bin_dir/systemctl"

  cp "$initial_binary_path" "$home_dir/.agentflow/bin/agentflow"
  chmod +x "$home_dir/.agentflow/bin/agentflow"

  "$home_dir/.agentflow/bin/agentflow" >/dev/null 2>&1 &
  agentflow_pid=$!
  printf 'background\n' >"$home_dir/.agentflow/run/agentflow.mode"
  printf '%s\n' "$agentflow_pid" >"$home_dir/.agentflow/run/agentflow.pid"
  sleep 1

  printf 'y\n2\n' | env \
    HOME="$home_dir" \
    SHELL="/bin/sh" \
    PATH="$fake_bin_dir:$PATH" \
    AGENTFLOW_VERSION_JSON_URL="file://$build_dir/version.json" \
    AGENTFLOW_INSTALLER_ARCH=amd64 \
    AGENTFLOW_PROMPT_USE_STDIN=1 \
    bash "$LINUX_INSTALLER" update >"$tmpdir/linux-update-decline-restart.log"

  if kill -0 "$agentflow_pid" 2>/dev/null; then
    kill "$agentflow_pid" 2>/dev/null || true
    echo 'expected interactive update to stop the previous background process' >&2
    exit 1
  fi

  assert_contains '升级后是否立即重新启动 AgentFlow？' "$tmpdir/linux-update-decline-restart.log"
  assert_contains '2. 暂不启动' "$tmpdir/linux-update-decline-restart.log"
  assert_contains 'AgentFlow 已升级' "$tmpdir/linux-update-decline-restart.log"
  assert_not_contains '运行模式: 后台用户进程' "$tmpdir/linux-update-decline-restart.log"

  if [ -f "$home_dir/.agentflow/run/agentflow.pid" ]; then
    echo 'expected no background pid after declining restart' >&2
    exit 1
  fi

  rm -rf "$tmpdir"
  trap - EXIT INT TERM
}

run_linux_interactive_update_restarts_when_user_accepts_case() {
  tmpdir=$(mktemp -d)
  trap 'rm -rf "$tmpdir"' EXIT INT TERM

  home_dir="$tmpdir/home"
  build_dir="$tmpdir/build"
  fake_bin_dir="$tmpdir/fake-bin"
  mkdir -p "$home_dir" "$build_dir" "$fake_bin_dir" "$home_dir/.agentflow/bin" "$home_dir/.agentflow/run"
  : >"$home_dir/.profile"

  initial_binary_path="$build_dir/agentflow-linux-amd64-initial"
  create_long_running_test_binary "$initial_binary_path" 'linux-2.3.3'
  updated_binary_path="$build_dir/agentflow-linux-amd64"
  create_long_running_test_binary "$updated_binary_path" 'linux-2.3.4'
  macos_binary_path="$build_dir/agentflow-darwin-arm64"
  create_test_binary "$macos_binary_path" 'darwin-2.3.4'
  write_version_json "$build_dir" "$updated_binary_path" "$macos_binary_path"

  cat >"$fake_bin_dir/systemctl" <<'EOF'
#!/usr/bin/env sh
printf '%s\n' 'systemctl is unavailable in this test environment' >&2
exit 1
EOF
  chmod +x "$fake_bin_dir/systemctl"

  cp "$initial_binary_path" "$home_dir/.agentflow/bin/agentflow"
  chmod +x "$home_dir/.agentflow/bin/agentflow"

  "$home_dir/.agentflow/bin/agentflow" >/dev/null 2>&1 &
  agentflow_pid=$!
  printf 'background\n' >"$home_dir/.agentflow/run/agentflow.mode"
  printf '%s\n' "$agentflow_pid" >"$home_dir/.agentflow/run/agentflow.pid"
  sleep 1

  printf 'y\n1\n' | env \
    HOME="$home_dir" \
    SHELL="/bin/sh" \
    PATH="$fake_bin_dir:$PATH" \
    AGENTFLOW_VERSION_JSON_URL="file://$build_dir/version.json" \
    AGENTFLOW_INSTALLER_ARCH=amd64 \
    AGENTFLOW_PROMPT_USE_STDIN=1 \
    bash "$LINUX_INSTALLER" update >"$tmpdir/linux-update-accept-restart.log"

  if kill -0 "$agentflow_pid" 2>/dev/null; then
    kill "$agentflow_pid" 2>/dev/null || true
    echo 'expected interactive update to replace the previous background process' >&2
    exit 1
  fi

  assert_contains '升级后是否立即重新启动 AgentFlow？' "$tmpdir/linux-update-accept-restart.log"
  assert_contains '运行模式: 后台用户进程' "$tmpdir/linux-update-accept-restart.log"

  new_pid=$(cat "$home_dir/.agentflow/run/agentflow.pid")
  if [ -z "$new_pid" ] || ! kill -0 "$new_pid" 2>/dev/null; then
    echo 'expected update restart to leave a running background process' >&2
    exit 1
  fi
  kill "$new_pid" 2>/dev/null || true
  wait "$new_pid" 2>/dev/null || true

  rm -rf "$tmpdir"
  trap - EXIT INT TERM
}

run_linux_menu_install_manual_start_case() {
  tmpdir=$(mktemp -d)
  trap 'rm -rf "$tmpdir"' EXIT INT TERM

  home_dir="$tmpdir/home"
  build_dir="$tmpdir/build"
  mkdir -p "$home_dir" "$build_dir"
  : >"$home_dir/.profile"

  linux_binary_path="$build_dir/agentflow-linux-amd64"
  create_test_binary "$linux_binary_path" 'linux-2.3.4'
  macos_binary_path="$build_dir/agentflow-darwin-arm64"
  create_test_binary "$macos_binary_path" 'darwin-2.3.4'
  write_version_json "$build_dir" "$linux_binary_path" "$macos_binary_path"

  printf '2\n1\n2\n' | env \
    HOME="$home_dir" \
    SHELL="/bin/sh" \
    AGENTFLOW_VERSION_JSON_URL="file://$build_dir/version.json" \
    AGENTFLOW_SKIP_SERVICE=1 \
    AGENTFLOW_INSTALLER_ARCH=amd64 \
    AGENTFLOW_PROMPT_USE_STDIN=1 \
    bash "$LINUX_INSTALLER" menu >"$tmpdir/linux-menu-install.log"

  assert_contains '1. 一键安装' "$tmpdir/linux-menu-install.log"
  assert_contains '2. 自定义安装' "$tmpdir/linux-menu-install.log"
  assert_contains '请选择访问地址：' "$tmpdir/linux-menu-install.log"
  assert_contains '1. http://127.0.0.1:48285' "$tmpdir/linux-menu-install.log"
  assert_contains '2. http://0.0.0.0:48285' "$tmpdir/linux-menu-install.log"
  assert_contains '现在启动 AgentFlow 吗？' "$tmpdir/linux-menu-install.log"
  assert_contains 'AgentFlow 已安装' "$tmpdir/linux-menu-install.log"
  assert_contains '访问地址: http://127.0.0.1:48285' "$tmpdir/linux-menu-install.log"
  assert_contains '启动命令: agentflow' "$tmpdir/linux-menu-install.log"

  assert_path_exists "$home_dir/.agentflow/data/agentflow.env"
  assert_equals 'HOST=127.0.0.1
PORT=48285' "$(cat "$home_dir/.agentflow/data/agentflow.env")"

  rm -rf "$tmpdir"
  trap - EXIT INT TERM
}

run_bootstrap_menu_install_manual_start_case() {
  tmpdir=$(mktemp -d)
  trap 'rm -rf "$tmpdir"' EXIT INT TERM

  home_dir="$tmpdir/home"
  build_dir="$tmpdir/build"
  release_dir="$tmpdir/releases"
  mkdir -p "$home_dir" "$build_dir" "$release_dir"
  : >"$home_dir/.profile"

  linux_binary_path="$build_dir/agentflow-linux-amd64"
  create_test_binary "$linux_binary_path" 'linux-2.3.4'
  macos_binary_path="$build_dir/agentflow-darwin-arm64"
  create_test_binary "$macos_binary_path" 'darwin-2.3.4'
  write_version_json "$build_dir" "$linux_binary_path" "$macos_binary_path"

  cp "$LINUX_INSTALLER" "$release_dir/agentflow-linux.sh"
  cp "$MACOS_INSTALLER" "$release_dir/agentflow-macos.sh"

  printf '2\n1\n2\n' | env \
    HOME="$home_dir" \
    SHELL="/bin/sh" \
    AGENTFLOW_INSTALLER_BASE_URL="file://$release_dir" \
    AGENTFLOW_VERSION_JSON_URL="file://$build_dir/version.json" \
    AGENTFLOW_SKIP_SERVICE=1 \
    AGENTFLOW_BOOTSTRAP_OS=Linux \
    AGENTFLOW_INSTALLER_ARCH=amd64 \
    AGENTFLOW_PROMPT_USE_STDIN=1 \
    bash "$BOOTSTRAP" >"$tmpdir/bootstrap-menu-install.log"

  assert_contains '1. 一键安装' "$tmpdir/bootstrap-menu-install.log"
  assert_contains 'AgentFlow 已安装' "$tmpdir/bootstrap-menu-install.log"
  assert_contains '访问地址: http://127.0.0.1:48285' "$tmpdir/bootstrap-menu-install.log"
  assert_path_exists "$home_dir/.agentflow/data/agentflow.env"

  rm -rf "$tmpdir"
  trap - EXIT INT TERM
}

run_bootstrap_update_preserves_tty_prompt_case() {
  if ! command -v script >/dev/null 2>&1; then
    return 0
  fi

  tmpdir=$(mktemp -d)
  trap 'rm -rf "$tmpdir"' EXIT INT TERM

  home_dir="$tmpdir/home"
  release_dir="$tmpdir/releases"
  fake_bin_dir="$tmpdir/fake-bin"
  mkdir -p "$home_dir" "$release_dir" "$fake_bin_dir"
  : >"$home_dir/.profile"

  cat >"$release_dir/agentflow-linux.sh" <<'EOF'
#!/usr/bin/env sh
printf '%s\n' "stdin:$(if [ -t 0 ]; then printf 'tty'; else printf '%s' "$(readlink /proc/$$/fd/0 2>/dev/null || printf 'notty')"; fi)"
EOF
  chmod +x "$release_dir/agentflow-linux.sh"
  cp "$MACOS_INSTALLER" "$release_dir/agentflow-macos.sh"

  real_bash=$(command -v bash)
  cat >"$fake_bin_dir/bash" <<EOF
#!/usr/bin/env sh
exec "$real_bash" "\$@"
EOF
  chmod +x "$fake_bin_dir/bash"

  env \
    HOME="$home_dir" \
    SHELL="/bin/sh" \
    PATH="$fake_bin_dir:$PATH" \
    AGENTFLOW_INSTALLER_BASE_URL="file://$release_dir" \
    AGENTFLOW_BOOTSTRAP_OS=Linux \
    script -qec "exec \"$real_bash\" \"$BOOTSTRAP\" update </dev/null" "$tmpdir/bootstrap-update-preserves-tty.log" >/dev/null 2>&1

  assert_contains 'stdin:tty' "$tmpdir/bootstrap-update-preserves-tty.log"

  rm -rf "$tmpdir"
  trap - EXIT INT TERM
}

run_linux_menu_install_without_eager_service_start_case() {
  tmpdir=$(mktemp -d)
  trap 'rm -rf "$tmpdir"' EXIT INT TERM

  home_dir="$tmpdir/home"
  build_dir="$tmpdir/build"
  fake_bin_dir="$tmpdir/fake-bin"
  mkdir -p "$home_dir" "$build_dir" "$fake_bin_dir"
  : >"$home_dir/.profile"

  linux_binary_path="$build_dir/agentflow-linux-amd64"
  create_test_binary "$linux_binary_path" 'linux-2.3.4'
  macos_binary_path="$build_dir/agentflow-darwin-arm64"
  create_test_binary "$macos_binary_path" 'darwin-2.3.4'
  write_version_json "$build_dir" "$linux_binary_path" "$macos_binary_path"

  cat >"$fake_bin_dir/systemctl" <<'EOF'
#!/usr/bin/env sh
case "${1:-}" in
  --user)
    shift
    ;;
esac
case "${1:-}" in
  show-environment)
    exit 0
    ;;
  daemon-reload|enable|start|restart|status|stop|disable)
    printf '%s\n' 'service actions must not run before explicit start choice' >&2
    exit 5
    ;;
esac
exit 0
EOF
  chmod +x "$fake_bin_dir/systemctl"

  printf '2\n1\n2\n' | env \
    HOME="$home_dir" \
    SHELL="/bin/sh" \
    PATH="$fake_bin_dir:$PATH" \
    AGENTFLOW_VERSION_JSON_URL="file://$build_dir/version.json" \
    AGENTFLOW_INSTALLER_ARCH=amd64 \
    AGENTFLOW_PROMPT_USE_STDIN=1 \
    bash "$LINUX_INSTALLER" menu >"$tmpdir/linux-menu-no-eager-service.log"

  assert_contains 'AgentFlow 已安装' "$tmpdir/linux-menu-no-eager-service.log"
  assert_not_contains 'service actions must not run before explicit start choice' "$tmpdir/linux-menu-no-eager-service.log"

  rm -rf "$tmpdir"
  trap - EXIT INT TERM
}

run_macos_install_with_launchctl_failure_case() {
  tmpdir=$(mktemp -d)
  trap 'rm -rf "$tmpdir"' EXIT INT TERM

  home_dir="$tmpdir/home"
  build_dir="$tmpdir/build"
  fake_bin_dir="$tmpdir/fake-bin"
  mkdir -p "$home_dir" "$build_dir" "$fake_bin_dir"
  : >"$home_dir/.profile"

  linux_binary_path="$build_dir/agentflow-linux-amd64"
  create_test_binary "$linux_binary_path" 'linux-2.3.4'
  macos_binary_path="$build_dir/agentflow-darwin-arm64"
  create_test_binary "$macos_binary_path" 'darwin-2.3.4'
  write_version_json "$build_dir" "$linux_binary_path" "$macos_binary_path"

  cat >"$fake_bin_dir/launchctl" <<'EOF'
#!/usr/bin/env sh
case "${1:-}" in
  print)
    exit 1
    ;;
  bootout)
    exit 0
    ;;
  bootstrap|enable|kickstart|disable)
    printf '%s\n' 'Bootstrap failed: 5: Input/output error' >&2
    exit 5
    ;;
esac
exit 0
EOF
  chmod +x "$fake_bin_dir/launchctl"

  env \
    HOME="$home_dir" \
    SHELL="/bin/sh" \
    PATH="$fake_bin_dir:$PATH" \
    AGENTFLOW_VERSION_JSON_URL="file://$build_dir/version.json" \
    AGENTFLOW_INSTALLER_ARCH=arm64 \
    bash "$MACOS_INSTALLER" install >"$tmpdir/macos-install-launchctl-failure.log" 2>&1

  assert_path_exists "$home_dir/.agentflow/bin/agentflow"
  assert_contains 'launchctl service registration failed; installed AgentFlow without starting a service.' "$tmpdir/macos-install-launchctl-failure.log"
  assert_contains 'Run agentflow directly from:' "$tmpdir/macos-install-launchctl-failure.log"
  assert_not_contains 'Bootstrap failed: 5: Input/output error' "$tmpdir/macos-install-launchctl-failure.log"

  rm -rf "$tmpdir"
  trap - EXIT INT TERM
}

run_macos_menu_start_falls_back_to_background_case() {
  tmpdir=$(mktemp -d)
  trap 'rm -rf "$tmpdir"' EXIT INT TERM

  home_dir="$tmpdir/home"
  build_dir="$tmpdir/build"
  fake_bin_dir="$tmpdir/fake-bin"
  mkdir -p "$home_dir" "$build_dir" "$fake_bin_dir"
  : >"$home_dir/.profile"

  linux_binary_path="$build_dir/agentflow-linux-amd64"
  create_test_binary "$linux_binary_path" 'linux-2.3.4'
  macos_binary_path="$build_dir/agentflow-darwin-arm64"
  create_test_binary "$macos_binary_path" 'darwin-2.3.4'
  write_version_json "$build_dir" "$linux_binary_path" "$macos_binary_path"

  cat >"$fake_bin_dir/launchctl" <<'EOF'
#!/usr/bin/env sh
case "${1:-}" in
  print)
    exit 1
    ;;
  bootout)
    exit 0
    ;;
  bootstrap|enable|kickstart|disable)
    printf '%s\n' 'Bootstrap failed: 5: Input/output error' >&2
    exit 5
    ;;
esac
exit 0
EOF
  chmod +x "$fake_bin_dir/launchctl"

  printf '2\n1\n1\n' | env \
    HOME="$home_dir" \
    SHELL="/bin/sh" \
    PATH="$fake_bin_dir:$PATH" \
    AGENTFLOW_VERSION_JSON_URL="file://$build_dir/version.json" \
    AGENTFLOW_INSTALLER_ARCH=arm64 \
    AGENTFLOW_PROMPT_USE_STDIN=1 \
    bash "$MACOS_INSTALLER" menu >"$tmpdir/macos-menu-background.log" 2>&1

  assert_contains '运行模式: 后台用户进程' "$tmpdir/macos-menu-background.log"
  assert_contains '访问地址: http://127.0.0.1:48285' "$tmpdir/macos-menu-background.log"
  assert_contains '停止方式: ~/.agentflow/bin/agentflowctl stop' "$tmpdir/macos-menu-background.log"
  assert_not_contains '运行模式: 当前终端前台运行' "$tmpdir/macos-menu-background.log"
  assert_not_contains 'Bootstrap failed: 5: Input/output error' "$tmpdir/macos-menu-background.log"

  rm -rf "$tmpdir"
  trap - EXIT INT TERM
}

run_macos_update_with_launchctl_failure_case() {
  tmpdir=$(mktemp -d)
  trap 'rm -rf "$tmpdir"' EXIT INT TERM

  home_dir="$tmpdir/home"
  build_dir="$tmpdir/build"
  fake_bin_dir="$tmpdir/fake-bin"
  mkdir -p "$home_dir" "$build_dir" "$fake_bin_dir"
  : >"$home_dir/.profile"

  mkdir -p "$home_dir/.agentflow/bin"
  create_test_binary "$home_dir/.agentflow/bin/agentflow" 'darwin-2.3.3'

  linux_binary_path="$build_dir/agentflow-linux-amd64"
  create_test_binary "$linux_binary_path" 'linux-2.3.4'
  macos_binary_path="$build_dir/agentflow-darwin-arm64"
  create_test_binary "$macos_binary_path" 'darwin-2.3.4'
  write_version_json "$build_dir" "$linux_binary_path" "$macos_binary_path"

  cat >"$fake_bin_dir/launchctl" <<'EOF'
#!/usr/bin/env sh
case "${1:-}" in
  print)
    exit 1
    ;;
  bootout)
    exit 0
    ;;
  bootstrap|enable|kickstart|disable)
    printf '%s\n' 'Bootstrap failed: 5: Input/output error' >&2
    exit 5
    ;;
esac
exit 0
EOF
  chmod +x "$fake_bin_dir/launchctl"

  env \
    HOME="$home_dir" \
    SHELL="/bin/sh" \
    PATH="$fake_bin_dir:$PATH" \
    AGENTFLOW_VERSION_JSON_URL="file://$build_dir/version.json" \
    AGENTFLOW_INSTALLER_ARCH=arm64 \
    bash "$MACOS_INSTALLER" update >"$tmpdir/macos-update-launchctl-failure.log" 2>&1

  assert_contains 'launchctl service registration failed; installed AgentFlow without starting a service.' "$tmpdir/macos-update-launchctl-failure.log"
  assert_not_contains 'Bootstrap failed: 5: Input/output error' "$tmpdir/macos-update-launchctl-failure.log"

  rm -rf "$tmpdir"
  trap - EXIT INT TERM
}

run_linux_one_click_install_starts_immediately_case() {
  tmpdir=$(mktemp -d)
  trap 'rm -rf "$tmpdir"' EXIT INT TERM

  home_dir="$tmpdir/home"
  build_dir="$tmpdir/build"
  fake_bin_dir="$tmpdir/fake-bin"
  mkdir -p "$home_dir" "$build_dir" "$fake_bin_dir"
  : >"$home_dir/.profile"

  linux_binary_path="$build_dir/agentflow-linux-amd64"
  create_test_binary "$linux_binary_path" 'linux-2.3.4'
  macos_binary_path="$build_dir/agentflow-darwin-arm64"
  create_test_binary "$macos_binary_path" 'darwin-2.3.4'
  write_version_json "$build_dir" "$linux_binary_path" "$macos_binary_path"

  cat >"$fake_bin_dir/systemctl" <<'EOF'
#!/usr/bin/env sh
case "${1:-}" in
  --user)
    shift
    ;;
esac
case "${1:-}" in
  show-environment)
    exit 0
    ;;
  daemon-reload|enable|start|restart|status|stop|disable)
    exit 5
    ;;
esac
exit 0
EOF
  chmod +x "$fake_bin_dir/systemctl"

  printf '1\n' | env \
    HOME="$home_dir" \
    SHELL="/bin/sh" \
    PATH="$fake_bin_dir:$PATH" \
    AGENTFLOW_VERSION_JSON_URL="file://$build_dir/version.json" \
    AGENTFLOW_INSTALLER_ARCH=amd64 \
    AGENTFLOW_PROMPT_USE_STDIN=1 \
    bash "$LINUX_INSTALLER" menu >"$tmpdir/linux-one-click.log"

  assert_contains '运行模式: 后台用户进程' "$tmpdir/linux-one-click.log"
  assert_contains '访问地址: http://127.0.0.1:48285' "$tmpdir/linux-one-click.log"
  assert_not_contains '现在启动 AgentFlow 吗？' "$tmpdir/linux-one-click.log"

  rm -rf "$tmpdir"
  trap - EXIT INT TERM
}

run_linux_restart_without_user_bus_case() {
  tmpdir=$(mktemp -d)
  trap 'rm -rf "$tmpdir"' EXIT INT TERM

  home_dir="$tmpdir/home"
  fake_bin_dir="$tmpdir/fake-bin"
  mkdir -p "$home_dir/.agentflow/bin" "$fake_bin_dir"
  : >"$home_dir/.profile"

  create_test_binary "$home_dir/.agentflow/bin/agentflow" 'linux-2.3.4'

  cat >"$fake_bin_dir/systemctl" <<'EOF'
#!/usr/bin/env sh
case "${1:-}" in
  --user)
    shift
    ;;
esac
case "${1:-}" in
  show-environment)
    exit 0
    ;;
  is-active)
    printf '%s\n' 'Failed to connect to bus: No medium found' >&2
    exit 1
    ;;
  daemon-reload|enable|start|restart|status|stop|disable)
    printf '%s\n' 'Failed to connect to bus: No medium found' >&2
    exit 1
    ;;
esac
exit 0
EOF
  chmod +x "$fake_bin_dir/systemctl"

  env \
    HOME="$home_dir" \
    SHELL="/bin/sh" \
    PATH="$fake_bin_dir:$PATH" \
    bash "$LINUX_INSTALLER" restart >"$tmpdir/linux-restart-no-bus.log" 2>&1

  assert_contains '运行模式: 后台用户进程' "$tmpdir/linux-restart-no-bus.log"
  assert_not_contains 'Failed to connect to bus: No medium found' "$tmpdir/linux-restart-no-bus.log"

  rm -rf "$tmpdir"
  trap - EXIT INT TERM
}

run_macos_stop_when_already_stopped_case() {
  tmpdir=$(mktemp -d)
  trap 'rm -rf "$tmpdir"' EXIT INT TERM

  home_dir="$tmpdir/home"
  fake_bin_dir="$tmpdir/fake-bin"
  mkdir -p "$home_dir" "$fake_bin_dir"
  : >"$home_dir/.profile"

  cat >"$fake_bin_dir/launchctl" <<'EOF'
#!/usr/bin/env sh
case "${1:-}" in
  print)
    exit 1
    ;;
  bootout)
    printf '%s\n' 'Boot-out failed: 5: Input/output error' >&2
    exit 5
    ;;
esac
exit 0
EOF
  chmod +x "$fake_bin_dir/launchctl"

  env \
    HOME="$home_dir" \
    SHELL="/bin/sh" \
    PATH="$fake_bin_dir:$PATH" \
    bash "$MACOS_INSTALLER" stop >"$tmpdir/macos-stop.log" 2>&1

  assert_not_contains 'Boot-out failed: 5: Input/output error' "$tmpdir/macos-stop.log"

  rm -rf "$tmpdir"
  trap - EXIT INT TERM
}

run_macos_stop_unmanaged_background_process_case() {
  tmpdir=$(mktemp -d)
  trap 'rm -rf "$tmpdir"' EXIT INT TERM

  home_dir="$tmpdir/home"
  fake_bin_dir="$tmpdir/fake-bin"
  mkdir -p "$home_dir/.agentflow/bin" "$fake_bin_dir"
  : >"$home_dir/.profile"

  cat >"$home_dir/.agentflow/bin/agentflow" <<'EOF'
#!/usr/bin/env sh
if [ "${1:-}" = "--version" ]; then
  printf '%s\n' 'darwin-2.3.4'
  exit 0
fi
trap 'exit 0' TERM INT
while :; do
  sleep 1
done
EOF
  chmod +x "$home_dir/.agentflow/bin/agentflow"

  cat >"$fake_bin_dir/launchctl" <<'EOF'
#!/usr/bin/env sh
case "${1:-}" in
  print)
    exit 1
    ;;
esac
exit 0
EOF
  chmod +x "$fake_bin_dir/launchctl"

  "$home_dir/.agentflow/bin/agentflow" >/dev/null 2>&1 &
  agentflow_pid=$!
  sleep 1

  env \
    HOME="$home_dir" \
    SHELL="/bin/sh" \
    PATH="$fake_bin_dir:$PATH" \
    bash "$MACOS_INSTALLER" stop >"$tmpdir/macos-stop-unmanaged.log" 2>&1

  sleep 1
  if kill -0 "$agentflow_pid" 2>/dev/null; then
    kill "$agentflow_pid" 2>/dev/null || true
    echo 'expected stop to terminate unmanaged background process' >&2
    exit 1
  fi

  rm -rf "$tmpdir"
  trap - EXIT INT TERM
}

run_bash32_compatibility_case() {
  assert_file_not_contains 'readarray' "$MACOS_INSTALLER"
  assert_file_not_contains 'readarray' "$LINUX_INSTALLER"
}

run_linux_menu_fails_when_metadata_unavailable_case() {
  tmpdir=$(mktemp -d)
  trap 'rm -rf "$tmpdir"' EXIT INT TERM

  home_dir="$tmpdir/home"
  mkdir -p "$home_dir"

  if env \
    HOME="$home_dir" \
    SHELL="/bin/sh" \
    AGENTFLOW_VERSION_JSON_URL="file://$tmpdir/missing-version" \
    AGENTFLOW_SKIP_SERVICE=1 \
    AGENTFLOW_INSTALLER_ARCH=amd64 \
    bash "$LINUX_INSTALLER" menu >"$tmpdir/linux-metadata-unavailable.log" 2>&1; then
    echo 'expected linux menu to fail when version metadata is unavailable' >&2
    exit 1
  fi

  assert_contains 'failed to download version metadata from file://' "$tmpdir/linux-metadata-unavailable.log"
  assert_not_contains 'No such file or directory' "$tmpdir/linux-metadata-unavailable.log"
  assert_not_contains '1. 一键安装' "$tmpdir/linux-metadata-unavailable.log"

  rm -rf "$tmpdir"
  trap - EXIT INT TERM
}

run_macos_menu_fails_when_metadata_unavailable_case() {
  tmpdir=$(mktemp -d)
  trap 'rm -rf "$tmpdir"' EXIT INT TERM

  home_dir="$tmpdir/home"
  mkdir -p "$home_dir"

  if env \
    HOME="$home_dir" \
    SHELL="/bin/sh" \
    AGENTFLOW_VERSION_JSON_URL="file://$tmpdir/missing-version" \
    AGENTFLOW_SKIP_SERVICE=1 \
    AGENTFLOW_INSTALLER_ARCH=arm64 \
    bash "$MACOS_INSTALLER" menu >"$tmpdir/macos-metadata-unavailable.log" 2>&1; then
    echo 'expected macos menu to fail when version metadata is unavailable' >&2
    exit 1
  fi

  assert_contains 'failed to download version metadata from file://' "$tmpdir/macos-metadata-unavailable.log"
  assert_not_contains 'No such file or directory' "$tmpdir/macos-metadata-unavailable.log"
  assert_not_contains '1. 一键安装' "$tmpdir/macos-metadata-unavailable.log"

  rm -rf "$tmpdir"
  trap - EXIT INT TERM
}

run_linux_install_case
run_linux_install_creates_global_bin_links_case
run_macos_install_case
run_bootstrap_dispatch_case
run_bootstrap_without_systemd_case
run_linux_update_without_systemd_case
run_linux_interactive_update_keeps_stopped_when_user_declines_restart_case
run_linux_interactive_update_restarts_when_user_accepts_case
run_linux_menu_install_manual_start_case
run_bootstrap_menu_install_manual_start_case
run_bootstrap_update_preserves_tty_prompt_case
run_linux_menu_install_without_eager_service_start_case
run_macos_install_with_launchctl_failure_case
run_macos_menu_start_falls_back_to_background_case
run_macos_update_with_launchctl_failure_case
run_linux_one_click_install_starts_immediately_case
run_linux_restart_without_user_bus_case
run_macos_stop_when_already_stopped_case
run_macos_stop_unmanaged_background_process_case
run_bash32_compatibility_case
run_linux_menu_fails_when_metadata_unavailable_case
run_macos_menu_fails_when_metadata_unavailable_case

echo "bootstrap installer tests passed"
