#!/usr/bin/env bash

set -euo pipefail

COMMAND="${1:-install}"
shift || true

AGENTFLOW_HOME="${AGENTFLOW_HOME:-$HOME/.agentflow}"
INSTALLER_BASE_URL="${AGENTFLOW_INSTALLER_BASE_URL:-http://fw.koolcenter.com/binary/geili/agentflow/installer}"
VERSION_JSON_URL="${AGENTFLOW_VERSION_JSON_URL:-http://fw.koolcenter.com/binary/geili/agentflow/build/agentflow-version}"
BIN_DIR="$AGENTFLOW_HOME/bin"
BIN_PATH="$BIN_DIR/agentflow"
CTL_PATH="$BIN_DIR/agentflowctl"
CODEX_WRAPPER_PATH="$BIN_DIR/codex-agentflow"
DATA_DIR="$AGENTFLOW_HOME/data"
ENV_PATH="$DATA_DIR/agentflow.env"
LOG_DIR="$AGENTFLOW_HOME/logs"
RUN_DIR="$AGENTFLOW_HOME/run"
RUN_MODE_PATH="$RUN_DIR/agentflow.mode"
RUN_PID_PATH="$RUN_DIR/agentflow.pid"
SYSTEMD_DIR="${XDG_CONFIG_HOME:-$HOME/.config}/systemd/user"
SERVICE_PATH="$SYSTEMD_DIR/agentflow.service"
TMP_DIR=""
REMOVE_DATA=0
PROFILE_MARKER_START='# >>> agentflow installer >>>'
PROFILE_MARKER_END='# <<< agentflow installer <<<'
DEFAULT_ACCESS_URL='http://127.0.0.1:3333'
AUTO_START_AFTER_INSTALL=1
GLOBAL_BIN_DIR="${AGENTFLOW_GLOBAL_BIN_DIR:-/usr/local/bin}"
GLOBAL_BIN_LINKED=0

log() {
  printf '[agentflow] %s\n' "$1"
}

fail() {
  printf '[agentflow] %s\n' "$1" >&2
  exit 1
}

quiet_run() {
  "$@" >/dev/null 2>&1
}

has_prompt_terminal() {
  if [ "${AGENTFLOW_PROMPT_USE_STDIN:-0}" = "1" ]; then
    return 0
  fi
  if [ -t 0 ] || [ -t 1 ] || [ -t 2 ]; then
    return 0
  fi
  if { exec 9<>/dev/tty; } 2>/dev/null; then
    exec 9>&-
    exec 9<&-
    return 0
  fi
  return 1
}

recover_working_directory() {
  if pwd >/dev/null 2>&1; then
    return 0
  fi
  cd "${HOME:-/}" 2>/dev/null || cd /
}

cleanup() {
  if [ -n "${TMP_DIR:-}" ] && [ -d "$TMP_DIR" ]; then
    rm -rf "$TMP_DIR"
  fi
}

trap cleanup EXIT
recover_working_directory

setup_update_log() {
  local update_log_path="$LOG_DIR/agentflow.update.log"
  if [ "${AGENTFLOW_UPDATE_LOG_ACTIVE:-0}" = "1" ]; then
    return
  fi
  mkdir -p "$LOG_DIR"
  : >"$update_log_path"
  export AGENTFLOW_UPDATE_LOG_ACTIVE=1
  exec > >(tee -a "$update_log_path") 2>&1
  log "Update log: $update_log_path"
}

usage() {
  cat <<EOF
Usage: agentflow-linux.sh <command> [options]

Commands:
  menu
  install
  update
  update-now
  uninstall [--remove-data]
  start
  stop
  restart
  status
  version
  remote-version
  doctor
EOF
}

while [ "$#" -gt 0 ]; do
  case "$1" in
    --remove-data)
      REMOVE_DATA=1
      shift
      ;;
    --help|-h)
      usage
      exit 0
      ;;
    *)
      fail "unknown argument: $1"
      ;;
  esac
done

detect_arch() {
  if [ -n "${AGENTFLOW_INSTALLER_ARCH:-}" ]; then
    printf '%s\n' "$AGENTFLOW_INSTALLER_ARCH"
    return
  fi
  case "$(uname -m)" in
    x86_64|amd64) printf 'amd64\n' ;;
    arm64|aarch64) printf 'arm64\n' ;;
    *) fail "unsupported architecture: $(uname -m)" ;;
  esac
}

curl_content_length() {
  local url=$1
  local length
  length=$(curl \
    --fail \
    --location \
    --head \
    --silent \
    --show-error \
    --connect-timeout 30 \
    --max-time 60 \
    "$url" | awk 'BEGIN { IGNORECASE = 1 } /^Content-Length:/ { gsub("\r", "", $2); value = $2 } END { print value }') || return 1
  case "$length" in
    ''|*[!0-9]*|0) return 1 ;;
  esac
  printf '%s\n' "$length"
}

print_percent_progress() {
  local destination=$1
  local total_size=$2
  local status_path=$3
  local last_percent=0
  local current_size percent

  while [ ! -f "$status_path" ]; do
    if [ -f "$destination" ]; then
      current_size=$(wc -c <"$destination" | tr -d ' ')
      percent=$((current_size * 100 / total_size))
      [ "$percent" -le 100 ] || percent=100
      while [ "$last_percent" -lt "$percent" ]; do
        last_percent=$((last_percent + 1))
        printf '%s%%\n' "$last_percent" >&2
      done
    fi
    sleep 1
  done

  if [ -f "$destination" ]; then
    current_size=$(wc -c <"$destination" | tr -d ' ')
    percent=$((current_size * 100 / total_size))
    [ "$percent" -le 100 ] || percent=100
    while [ "$last_percent" -lt "$percent" ]; do
      last_percent=$((last_percent + 1))
      printf '%s%%\n' "$last_percent" >&2
    done
  fi
}

curl_download_with_percent_progress() {
  local url=$1
  local destination=$2
  local total_size status_path status

  total_size=$(curl_content_length "$url") || return 125
  status_path="$destination.curl-status.$$"
  rm -f "$status_path"

  (
    set +e
    curl \
      --fail \
      --location \
      --retry 3 \
      --retry-delay 2 \
      --connect-timeout 30 \
      --max-time 300 \
      --silent \
      --show-error \
      "$url" \
      -o "$destination"
    printf '%s\n' "$?" >"$status_path"
  ) &

  print_percent_progress "$destination" "$total_size" "$status_path"
  wait "$!" || true
  status=$(cat "$status_path" 2>/dev/null || printf '1')
  rm -f "$status_path"
  return "$status"
}

download_to_file() {
  local url=$1
  local destination=$2
  local requested_progress=${3:-0}
  local show_progress=0
  local percent_progress=0
  local progress_status

  if [ "$requested_progress" = "1" ] && [ "${AGENTFLOW_UPDATE_LOG_ACTIVE:-0}" = "1" ]; then
    show_progress=1
    percent_progress=1
  elif [ "$requested_progress" = "1" ] && [ -t 2 ]; then
    show_progress=1
  fi

  if command -v curl >/dev/null 2>&1; then
    if [ "$percent_progress" -eq 1 ]; then
      progress_status=0
      curl_download_with_percent_progress "$url" "$destination" || progress_status=$?
      if [ "$progress_status" -eq 0 ]; then
        return 0
      fi
      if [ "$progress_status" -ne 125 ]; then
        return "$progress_status"
      fi
    fi
    if [ "$show_progress" -eq 1 ]; then
      curl \
        --fail \
        --location \
        --retry 3 \
        --retry-delay 2 \
        --connect-timeout 30 \
        --max-time 300 \
        --progress-bar \
        --show-error \
        "$url" \
        -o "$destination"
    else
      curl \
        --fail \
        --location \
        --retry 3 \
        --retry-delay 2 \
        --connect-timeout 30 \
        --max-time 300 \
        --silent \
        --show-error \
        "$url" \
        -o "$destination"
    fi
    return
  fi

  if command -v wget >/dev/null 2>&1; then
    if [ "$show_progress" -eq 1 ]; then
      wget \
        --tries=3 \
        --timeout=30 \
        --show-progress \
        --progress=bar:force:noscroll \
        -O "$destination" \
        "$url"
    else
      wget --tries=3 --timeout=30 -qO "$destination" "$url"
    fi
    return
  fi

  fail 'curl or wget is required'
}

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
  if command -v openssl >/dev/null 2>&1; then
    openssl dgst -sha256 "$1" | awk '{print $NF}'
    return
  fi
  fail 'no sha256 tool available'
}

compact_json() {
  tr -d '\n\r\t ' <"$1"
}

read_version_field() {
  local metadata_compact=$1
  printf '%s' "$metadata_compact" | sed -n 's/.*"version":"\([^"]*\)".*/\1/p'
}

read_artifact_field() {
  local metadata_path=$1
  local platform=$2
  local arch=$3
  local field=$4

  awk \
    -v target_platform="$platform" \
    -v target_arch="$arch" \
    -v target_field="$field" '
      function count_char(text, char,    idx, total) {
        total = 0
        for (idx = 1; idx <= length(text); idx++) {
          if (substr(text, idx, 1) == char) {
            total++
          }
        }
        return total
      }

      BEGIN {
        depth = 0
        in_platform = 0
        waiting_arch = 0
        in_arch = 0
        platform_depth = -1
        arch_depth = -1
      }

      {
        line = $0

        if (!in_platform && index(line, "\"" target_platform "\"") > 0) {
          in_platform = 1
          platform_depth = depth
        } else if (in_platform && !in_arch && index(line, "\"" target_arch "\"") > 0) {
          waiting_arch = 1
        }

        if (in_platform && waiting_arch && index(line, "{") > 0) {
          in_arch = 1
          waiting_arch = 0
          arch_depth = depth
        }

        if (in_arch && index(line, "\"" target_field "\"") > 0) {
          value = line
          sub("^.*\"" target_field "\":[[:space:]]*\"", "", value)
          sub("\".*$", "", value)
          print value
          exit
        }

        depth += count_char(line, "{")
        depth -= count_char(line, "}")

        if (in_arch && depth <= arch_depth) {
          in_arch = 0
        }
        if (in_platform && depth <= platform_depth) {
          in_platform = 0
          waiting_arch = 0
        }
      }
    ' "$metadata_path"
}

ensure_layout() {
  mkdir -p "$BIN_DIR" "$DATA_DIR" "$LOG_DIR" "$RUN_DIR" "$SYSTEMD_DIR"
}

pick_profile_file() {
  local shell_name
  shell_name=$(basename "${SHELL:-}")
  case "$shell_name" in
    zsh) printf '%s/.zshrc\n' "$HOME" ;;
    bash) printf '%s/.bashrc\n' "$HOME" ;;
    *) printf '%s/.profile\n' "$HOME" ;;
  esac
}

remove_profile_block_from_file() {
  local target_file=$1
  local tmp_file
  [ -f "$target_file" ] || return 0

  tmp_file=$(mktemp)
  awk -v start="$PROFILE_MARKER_START" -v end="$PROFILE_MARKER_END" '
    $0 == start { skip=1; next }
    $0 == end { skip=0; next }
    !skip { print }
  ' "$target_file" >"$tmp_file"
  mv "$tmp_file" "$target_file"
}

ensure_profile_block() {
  local profile_file
  profile_file=$(pick_profile_file)
  mkdir -p "$(dirname "$profile_file")"
  [ -f "$profile_file" ] || : >"$profile_file"

  if grep -F "$PROFILE_MARKER_START" "$profile_file" >/dev/null 2>&1; then
    return 0
  fi

  {
    printf '\n%s\n' "$PROFILE_MARKER_START"
    printf '# agentflow installer\n'
    printf 'export PATH="$HOME/.agentflow/bin:$PATH"\n'
    printf '%s\n' "$PROFILE_MARKER_END"
  } >>"$profile_file"
}

remove_profile_block() {
  remove_profile_block_from_file "$HOME/.profile"
  remove_profile_block_from_file "$HOME/.bashrc"
  remove_profile_block_from_file "$HOME/.zshrc"
}

prompt_print() {
  if [ "${AGENTFLOW_PROMPT_USE_STDIN:-0}" = "1" ]; then
    printf '%s' "$1"
    return
  fi
  if [ -w /dev/tty ]; then
    printf '%s' "$1" >/dev/tty
    return
  fi
  printf '%s' "$1"
}

prompt_read_line() {
  local value
  if [ "${AGENTFLOW_PROMPT_USE_STDIN:-0}" = "1" ]; then
    IFS= read -r value || return 1
    printf '%s\n' "$value"
    return
  fi
  if [ -r /dev/tty ]; then
    IFS= read -r value </dev/tty || return 1
    printf '%s\n' "$value"
    return
  fi
  IFS= read -r value || return 1
  printf '%s\n' "$value"
}

PROMPT_REPLY=''

menu_select() {
  local prompt=$1
  prompt_print "$prompt"
  PROMPT_REPLY=$(prompt_read_line) || fail 'interactive input is required'
}

confirm_default_no() {
  local prompt=$1
  local answer
  prompt_print "$prompt"
  answer=$(prompt_read_line || true)
  case "$(printf '%s' "$answer" | tr '[:upper:]' '[:lower:]')" in
    y|yes) return 0 ;;
    *) return 1 ;;
  esac
}

read_runtime_value() {
  local key=$1
  [ -f "$ENV_PATH" ] || return 0
  awk -F= -v target="$key" '$1 == target { print substr($0, index($0, "=") + 1); exit }' "$ENV_PATH"
}

browser_access_url() {
  local host port
  host=$(read_runtime_value HOST)
  port=$(read_runtime_value PORT)
  [ -n "$host" ] || host='127.0.0.1'
  [ -n "$port" ] || port='3333'
  if [ "$host" = '0.0.0.0' ]; then
    host='127.0.0.1'
  fi
  printf 'http://%s:%s\n' "$host" "$port"
}

current_access_url() {
  local host port
  host=$(read_runtime_value HOST)
  port=$(read_runtime_value PORT)
  if [ -n "$host" ] && [ -n "$port" ]; then
    printf 'http://%s:%s\n' "$host" "$port"
    return
  fi
  printf '%s\n' "$DEFAULT_ACCESS_URL"
}

parse_access_url() {
  local url=$1
  local normalized host port

  normalized=${url%/}
  case "$normalized" in
    http://*)
      host=${normalized#http://}
      ;;
    *)
      return 1
      ;;
  esac

  port=${host##*:}
  host=${host%:*}
  case "$host" in
    ''|*/*) return 1 ;;
  esac
  case "$port" in
    ''|*[!0-9]*) return 1 ;;
  esac

  printf '%s\n%s\n' "$host" "$port"
}

write_access_url() {
  local url=$1
  local parsed host port

  parsed=$(parse_access_url "$url") || fail '访问地址必须是完整 URL，例如 http://127.0.0.1:3333'
  host=$(printf '%s\n' "$parsed" | sed -n '1p')
  port=$(printf '%s\n' "$parsed" | sed -n '2p')
  mkdir -p "$DATA_DIR"
  cat >"$ENV_PATH" <<EOF
HOST=$host
PORT=$port
EOF
}

select_access_url() {
  menu_select $'请选择访问地址：\n1. http://127.0.0.1:3333\n2. http://0.0.0.0:3333\n选择 [1]: '
  case "${PROMPT_REPLY:-1}" in
    ''|1) PROMPT_REPLY='http://127.0.0.1:3333' ;;
    2) PROMPT_REPLY='http://0.0.0.0:3333' ;;
    *) fail '无效选项，请输入 1 或 2' ;;
  esac
}

record_run_state() {
  local mode=$1
  local pid=${2:-}
  mkdir -p "$RUN_DIR"
  printf '%s\n' "$mode" >"$RUN_MODE_PATH"
  if [ -n "$pid" ]; then
    printf '%s\n' "$pid" >"$RUN_PID_PATH"
  else
    rm -f "$RUN_PID_PATH"
  fi
}

clear_run_state() {
  rm -f "$RUN_MODE_PATH" "$RUN_PID_PATH"
}

read_run_mode() {
  [ -f "$RUN_MODE_PATH" ] || return 0
  sed -n '1p' "$RUN_MODE_PATH"
}

pid_running() {
  local pid=$1
  [ -n "$pid" ] || return 1
  kill -0 "$pid" 2>/dev/null
}

foreground_running() {
  local mode pid
  mode=$(read_run_mode)
  [ "$mode" = 'foreground' ] || return 1
  [ -f "$RUN_PID_PATH" ] || return 1
  pid=$(sed -n '1p' "$RUN_PID_PATH")
  if pid_running "$pid"; then
    return 0
  fi
  clear_run_state
  return 1
}

background_running() {
  local mode pid
  mode=$(read_run_mode)
  [ "$mode" = 'background' ] || return 1
  [ -f "$RUN_PID_PATH" ] || return 1
  pid=$(sed -n '1p' "$RUN_PID_PATH")
  if pid_running "$pid"; then
    return 0
  fi
  clear_run_state
  return 1
}

find_binary_process_pids() {
  ps -eo pid=,command= 2>/dev/null | awk -v bin="$BIN_PATH" '
    {
      pid = $1
      for (i = 2; i <= NF; i++) {
        if ($i == bin) {
          print pid
          break
        }
      }
    }
  '
}

adopt_binary_process_state() {
  local pid
  pid=$(find_binary_process_pids | sed -n '1p')
  [ -n "$pid" ] || return 1
  record_run_state background "$pid"
  return 0
}

systemd_user_available() {
  command -v systemctl >/dev/null 2>&1 || return 1
  systemctl --user show-environment >/dev/null 2>&1
}

service_running() {
  if ! systemd_user_available; then
    return 1
  fi
  quiet_run systemctl --user is-active --quiet agentflow.service
}

import_systemd_user_environment() {
  quiet_run systemctl --user import-environment || true
}

detect_run_mode() {
  if service_running; then
    record_run_state service
    printf 'service\n'
    return
  fi
  if background_running; then
    printf 'background\n'
    return
  fi
  if foreground_running; then
    printf 'foreground\n'
    return
  fi
  if adopt_binary_process_state; then
    printf 'background\n'
    return
  fi
  clear_run_state
  printf 'stopped\n'
}

terminate_pid() {
  local pid
  pid=$1
  if ! pid_running "$pid"; then
    return 0
  fi
  kill "$pid" 2>/dev/null || true
  for _ in 1 2 3 4 5; do
    if ! pid_running "$pid"; then
      return 0
    fi
    sleep 1
  done
  kill -9 "$pid" 2>/dev/null || true
}

stop_detected_binary_processes() {
  local recorded_pid
  local -a pids=()
  while IFS= read -r pid; do
    [ -n "$pid" ] || continue
    pids+=("$pid")
  done < <(find_binary_process_pids)
  if [ "${#pids[@]}" -eq 0 ] && [ -f "$RUN_PID_PATH" ]; then
    recorded_pid=$(sed -n '1p' "$RUN_PID_PATH")
    if [ -n "$recorded_pid" ]; then
      pids=("$recorded_pid")
    fi
  fi
  for pid in "${pids[@]}"; do
    [ -n "$pid" ] || continue
    if [ "$pid" = "$$" ] || [ "$pid" = "${PPID:-}" ]; then
      continue
    fi
    terminate_pid "$pid"
  done
  clear_run_state
}

stop_foreground_instance() {
  stop_detected_binary_processes
}

stop_background_instance() {
  stop_detected_binary_processes
}

print_current_shell_hint() {
  if [ "$GLOBAL_BIN_LINKED" -eq 1 ]; then
    log "AgentFlow is available on PATH from: $GLOBAL_BIN_DIR"
    return
  fi
  log "To use agentflow in this terminal now, run:"
  printf 'export PATH="$HOME/.agentflow/bin:$PATH"\n'
}

print_runtime_url() {
  log "Open Browser URL: $(browser_access_url)"
}

fetch_metadata() {
  TMP_DIR=$(mktemp -d "${TMPDIR:-/tmp}/agentflow-linux.XXXXXX")
  METADATA_PATH="$TMP_DIR/version.json"
  if ! download_to_file "$VERSION_JSON_URL" "$METADATA_PATH" 0; then
    fail "failed to download version metadata from $VERSION_JSON_URL"
  fi
  [ -s "$METADATA_PATH" ] || fail "downloaded version metadata is empty: $VERSION_JSON_URL"
}

get_remote_version() {
  fetch_metadata
  read_version_field "$(compact_json "$METADATA_PATH")"
}

get_installed_version() {
  if [ ! -x "$BIN_PATH" ]; then
    return 0
  fi
  "$BIN_PATH" --version 2>/dev/null | awk 'NR == 1 { print; exit }'
}

install_control_script() {
  local source_script
  source_script=$(cd "$(dirname "$0")" && pwd)/$(basename "$0")
  if [ "$source_script" = "$CTL_PATH" ]; then
    return
  fi
  cp "$source_script" "$CTL_PATH"
  chmod 755 "$CTL_PATH"
}

running_from_installed_control_script() {
  local source_script resolved_script
  source_script=$(cd "$(dirname "$0")" && pwd)/$(basename "$0")
  if [ "$source_script" = "$CTL_PATH" ]; then
    return 0
  fi
  if [ -L "$source_script" ] && command -v readlink >/dev/null 2>&1; then
    resolved_script=$(readlink "$source_script" 2>/dev/null || true)
    if [ "$resolved_script" = "$CTL_PATH" ]; then
      return 0
    fi
  fi
  return 1
}

run_bootstrap_update() {
  local update_command=${1:-update}
  local bootstrap_url bootstrap_path
  bootstrap_url="${INSTALLER_BASE_URL%/}/getagentflow.sh"
  TMP_DIR=$(mktemp -d "${TMPDIR:-/tmp}/agentflow-bootstrap-update.XXXXXX")
  bootstrap_path="$TMP_DIR/getagentflow.sh"

  log "Refreshing installer from $bootstrap_url"
  download_to_file "$bootstrap_url" "$bootstrap_path" 0
  chmod +x "$bootstrap_path"
  exec bash "$bootstrap_path" "$update_command"
}

install_codex_wrapper() {
  cat >"$CODEX_WRAPPER_PATH" <<'EOF'
#!/usr/bin/env bash
set -euo pipefail

if command -v codex >/dev/null 2>&1; then
  exec codex "$@"
fi

FNM_PATH="${HOME}/.local/share/fnm"
if [ -d "$FNM_PATH" ]; then
  export PATH="$FNM_PATH:$PATH"
fi
if command -v fnm >/dev/null 2>&1; then
  eval "$(fnm env --shell bash)"
fi

if [ -d "${HOME}/.local/share/mise/shims" ]; then
  export PATH="${HOME}/.local/share/mise/shims:$PATH"
fi
if [ -d "${HOME}/.mise/shims" ]; then
  export PATH="${HOME}/.mise/shims:$PATH"
fi

exec codex "$@"
EOF
  chmod 755 "$CODEX_WRAPPER_PATH"
}

link_global_binary() {
  local source_path=$1
  local link_path=$2

  if [ -e "$link_path" ] && [ ! -L "$link_path" ]; then
    return 1
  fi

  rm -f "$link_path"
  ln -s "$source_path" "$link_path"
}

install_global_bin_links() {
  local parent_dir
  [ -n "$GLOBAL_BIN_DIR" ] || return 0

  if [ ! -d "$GLOBAL_BIN_DIR" ]; then
    parent_dir=$(dirname "$GLOBAL_BIN_DIR")
    [ -w "$parent_dir" ] || return 0
    mkdir -p "$GLOBAL_BIN_DIR" 2>/dev/null || return 0
  fi

  [ -w "$GLOBAL_BIN_DIR" ] || return 0

  if link_global_binary "$BIN_PATH" "$GLOBAL_BIN_DIR/agentflow"; then
    GLOBAL_BIN_LINKED=1
  fi
  link_global_binary "$CTL_PATH" "$GLOBAL_BIN_DIR/agentflowctl" || true
}

remove_global_bin_links() {
  local link_path resolved_path

  for link_path in "$GLOBAL_BIN_DIR/agentflow" "$GLOBAL_BIN_DIR/agentflowctl"; do
    [ -L "$link_path" ] || continue
    resolved_path=$(readlink "$link_path" 2>/dev/null || true)
    case "$resolved_path" in
      "$BIN_PATH"|"$CTL_PATH")
        rm -f "$link_path"
        ;;
    esac
  done
}

write_service_file() {
  cat >"$SERVICE_PATH" <<'EOF'
[Unit]
Description=AgentFlow Service
After=network.target

[Service]
Type=simple
ExecStart=%h/.agentflow/bin/agentflow
Environment=CODEX_EXECUTABLE=%h/.agentflow/bin/codex-agentflow
WorkingDirectory=%h/.agentflow
Restart=always
RestartSec=3
StandardOutput=append:%h/.agentflow/logs/agentflow.stdout.log
StandardError=append:%h/.agentflow/logs/agentflow.stderr.log

[Install]
WantedBy=default.target
EOF
}

run_service_action() {
  local action=$1
  local requirement=${2:-required}
  if [ "${AGENTFLOW_SKIP_SERVICE:-0}" = "1" ]; then
    log "Skipping service action: $action"
    return 0
  fi
  if ! systemd_user_available; then
    if [ "$requirement" = 'optional' ]; then
      return 0
    fi
    fail 'systemd --user is unavailable on this system'
  fi
  case "$action" in
    enable-now)
      import_systemd_user_environment
      if quiet_run systemctl --user daemon-reload &&
        quiet_run systemctl --user enable --now agentflow.service; then
        return 0
      fi
      ;;
    disable-now)
      quiet_run systemctl --user disable --now agentflow.service || true
      return 0
      ;;
    start|restart)
      import_systemd_user_environment
      if quiet_run systemctl --user "$action" agentflow.service; then
        return 0
      fi
      ;;
    stop|status)
      if quiet_run systemctl --user "$action" agentflow.service; then
        return 0
      fi
      ;;
    daemon-reload)
      if quiet_run systemctl --user daemon-reload; then
        return 0
      fi
      ;;
    *)
      fail "unknown service action: $action"
      ;;
  esac
  if [ "$requirement" = 'optional' ]; then
    return 1
  fi
  fail "systemd --user action failed: $action"
}

download_artifact() {
  local metadata_path=$1
  local arch=$2
  local artifact_file artifact_sha artifact_url download_path actual_sha

  artifact_file=$(read_artifact_field "$metadata_path" linux "$arch" file)
  artifact_sha=$(read_artifact_field "$metadata_path" linux "$arch" sha256)
  artifact_url=$(read_artifact_field "$metadata_path" linux "$arch" url)

  [ -n "$artifact_file" ] || fail "version.json does not contain metadata for linux/$arch"
  [ -n "$artifact_sha" ] || fail "version.json entry for linux/$arch is missing sha256"
  [ -n "$artifact_url" ] || fail "version.json entry for linux/$arch is missing url"

  download_path="$TMP_DIR/$artifact_file"
  rm -f "$download_path"
  if ! download_to_file "$artifact_url" "$download_path" 1; then
    rm -f "$download_path"
    fail "failed to download artifact $artifact_file from $artifact_url"
  fi
  actual_sha=$(sha256_file "$download_path")
  [ "$actual_sha" = "$artifact_sha" ] || fail "SHA256 mismatch for $artifact_file"

  printf '%s\n' "$download_path"
}

replace_binary() {
  local downloaded_path=$1
  local staged_path="$BIN_PATH.new"

  cp "$downloaded_path" "$staged_path"
  chmod 755 "$staged_path"
  mv "$staged_path" "$BIN_PATH"
}

install_or_update() {
  local metadata_compact remote_version arch download_path installed_version

  fetch_metadata
  metadata_compact=$(compact_json "$METADATA_PATH")
  remote_version=$(read_version_field "$metadata_compact")
  [ -n "$remote_version" ] || fail 'version.json did not provide a version value.'

  arch=$(detect_arch)
  download_path=$(download_artifact "$METADATA_PATH" "$arch")
  installed_version=$(get_installed_version || true)

  ensure_layout
  install_control_script
  install_codex_wrapper

  if { [ "$COMMAND" = "update" ] || [ "$COMMAND" = "update-now" ]; } && [ -x "$BIN_PATH" ]; then
    log "Updating AgentFlow from ${installed_version:-unknown} to $remote_version"
    if [ "$COMMAND" = "update" ]; then
      run_service_action stop optional
    fi
  else
    log "Installing AgentFlow $remote_version"
  fi

  replace_binary "$download_path"
  write_service_file
  install_global_bin_links
  ensure_profile_block

  if [ "$AUTO_START_AFTER_INSTALL" -eq 1 ]; then
    if [ "$COMMAND" = "update" ]; then
      if ! run_service_action daemon-reload optional || ! run_service_action start optional; then
        log 'systemd --user service registration failed; installed AgentFlow without starting a service.'
        log 'Run agentflow directly from:'
        printf '%s\n' "$BIN_PATH"
      fi
    elif [ "$COMMAND" = "update-now" ]; then
      true
    elif ! run_service_action enable-now optional; then
      log 'systemd --user service registration failed; installed AgentFlow without starting a service.'
      log 'Run agentflow directly from:'
      printf '%s\n' "$BIN_PATH"
    fi
  fi

  print_current_shell_hint
  if [ "$AUTO_START_AFTER_INSTALL" -eq 1 ] && [ "${AGENTFLOW_SKIP_SERVICE:-0}" = "1" ]; then
    print_runtime_url
  elif [ "$AUTO_START_AFTER_INSTALL" -eq 1 ] && service_running; then
    print_runtime_url
  fi
}

try_start_service() {
  if ! systemd_user_available; then
    return 1
  fi
  if run_service_action enable-now optional && service_running; then
    record_run_state service
    return 0
  fi
  return 1
}

try_restart_service() {
  if ! systemd_user_available; then
    return 1
  fi
  if service_running; then
    if run_service_action daemon-reload optional && run_service_action restart optional && service_running; then
      record_run_state service
      return 0
    fi
    return 1
  fi
  if run_service_action daemon-reload optional && run_service_action start optional && service_running; then
    record_run_state service
    return 0
  fi
  return 1
}

print_status_overview() {
  local installed_text local_version remote_version run_mode
  if [ -x "$BIN_PATH" ]; then
    installed_text='yes'
    local_version=$(get_installed_version || true)
  else
    installed_text='no'
    local_version='not installed'
  fi
  remote_version=$(get_remote_version)
  run_mode=$(detect_run_mode)
  printf '安装状态: %s\n' "$installed_text"
  printf '本地版本: %s\n' "${local_version:-unknown}"
  printf '最新版本: %s\n' "${remote_version:-unknown}"
  printf '运行状态: %s\n' "$run_mode"
  printf '访问地址: %s\n' "$(current_access_url)"
}

print_service_summary() {
  local title=$1
  printf 'AgentFlow %s\n' "$title"
  printf '运行模式: 系统服务\n'
  printf '访问地址: %s\n' "$(current_access_url)"
  printf '配置文件: %s\n' "$ENV_PATH"
  printf '停止方式: %s\n' 'systemctl --user stop agentflow.service'
  printf '启动方式: %s\n' 'systemctl --user start agentflow.service'
}

print_manual_summary() {
  printf 'AgentFlow 已安装\n'
  printf '访问地址: %s\n' "$(current_access_url)"
  printf '配置文件: %s\n' "$ENV_PATH"
  printf '启动命令: %s start\n' "$BIN_PATH"
  printf '兜底命令: %s\n' "$BIN_PATH"
  printf '如命令未生效，请重开终端后再试\n'
}

print_stopped_summary() {
  local title=$1
  printf 'AgentFlow %s\n' "$title"
  printf '当前状态: 未启动\n'
  printf '访问地址: %s\n' "$(current_access_url)"
  printf '配置文件: %s\n' "$ENV_PATH"
  printf '启动命令: %s start\n' "$BIN_PATH"
  printf '兜底命令: %s\n' "$BIN_PATH"
  printf '如命令未生效，请重开终端后再试\n'
}

print_update_only_summary() {
  printf 'AgentFlow 已升级\n'
  printf '当前运行中的 AgentFlow 进程未停止或重启\n'
  printf '访问地址: %s\n' "$(current_access_url)"
  printf '配置文件: %s\n' "$ENV_PATH"
  printf '如需重启，请运行: %s restart\n' "$CTL_PATH"
}

print_background_summary() {
  local title=$1
  printf 'AgentFlow %s\n' "$title"
  printf '运行模式: 后台用户进程\n'
  printf '访问地址: %s\n' "$(current_access_url)"
  printf '配置文件: %s\n' "$ENV_PATH"
  printf '停止方式: %s stop\n' "$BIN_PATH"
  printf '启动方式: %s start\n' "$BIN_PATH"
}

print_foreground_summary() {
  local title=$1
  printf 'AgentFlow %s\n' "$title"
  printf '运行模式: 当前终端前台运行\n'
  printf '访问地址: %s\n' "$(current_access_url)"
  printf '配置文件: %s\n' "$ENV_PATH"
  printf '停止方式: Ctrl+C\n'
  printf '再次启动: agentflow\n'
  printf '关闭此终端后 AgentFlow 会停止\n'
}

run_in_foreground() {
  local title=$1
  record_run_state foreground "$$"
  print_foreground_summary "$title"
  exec env AGENT_FLOW_DATA="$DATA_DIR" "$BIN_PATH"
}

start_background_process() {
  ensure_layout
  mkdir -p "$LOG_DIR" "$RUN_DIR" "$DATA_DIR"
  nohup env AGENT_FLOW_DATA="$DATA_DIR" "$BIN_PATH" \
    >>"$LOG_DIR/agentflow.stdout.log" \
    2>>"$LOG_DIR/agentflow.stderr.log" \
    </dev/null &
  record_run_state background "$!"
}

start_best_effort() {
  local title=$1
  if try_start_service; then
    print_service_summary "$title"
    return 0
  fi
  if start_background_process; then
    print_background_summary "$title"
    return 0
  fi
  fail 'AgentFlow 已安装，但启动失败'
}

restart_best_effort() {
  local title=$1
  if try_restart_service; then
    print_service_summary "$title"
    return 0
  fi
  # service restart unavailable/failed: enforce true restart for binary mode
  stop_detected_binary_processes
  if start_background_process; then
    print_background_summary "$title"
    return 0
  fi
  fail 'AgentFlow 已升级，但自动重启失败'
}

stop_for_update() {
  local run_mode=$1
  case "$run_mode" in
    service)
      run_service_action stop optional
      ;;
    foreground|background)
      stop_detected_binary_processes
      ;;
  esac
}

install_updated_binary() {
  COMMAND=update
  AUTO_START_AFTER_INSTALL=0
  install_or_update
  AUTO_START_AFTER_INSTALL=1
}

prompt_restart_after_update() {
  local selection
  printf '升级后是否立即重新启动 AgentFlow？\n'
  printf '1. 立即启动（推荐）\n'
  printf '2. 暂不启动\n'
  printf '会优先注册为系统服务；若失败，则回退到后台用户进程。\n'
  menu_select "> "
  selection=${PROMPT_REPLY:-2}
  case "$selection" in
    1)
      restart_best_effort '已升级并重新启动'
      ;;
    2|'')
      print_stopped_summary '已升级'
      ;;
    *)
      fail '无效选项，请输入 1 或 2'
      ;;
  esac
}

run_update_flow() {
  local skip_prompts=${1:-0}
  local was_running remote_version
  if running_from_installed_control_script; then
    if [ "$skip_prompts" -eq 1 ]; then
      run_bootstrap_update update-now
    else
      run_bootstrap_update update
    fi
    return
  fi

  was_running=$(detect_run_mode)
  remote_version=$(get_remote_version)

  if [ "$was_running" != 'stopped' ]; then
    printf '检测到 AgentFlow 正在运行\n'
    printf '升级会停止当前进程'
    if [ "$skip_prompts" -eq 1 ]; then
      printf '，升级完成后默认保持停止\n'
    elif has_prompt_terminal; then
      printf '，升级完成后将询问是否重新启动\n'
    else
      printf '，升级完成后默认保持停止\n'
    fi
    printf '正在执行的 AI 任务可能中断\n'
    if [ "$skip_prompts" -ne 1 ] && has_prompt_terminal; then
      confirm_default_no '是否继续？ [y/N] ' || return 0
    fi
  else
    printf '将升级 AgentFlow 到最新版本'
    if [ -n "$remote_version" ]; then
      printf ' (%s)' "$remote_version"
    fi
    printf '\n'
    if [ "$skip_prompts" -ne 1 ] && has_prompt_terminal; then
      confirm_default_no '是否继续？ [y/N] ' || return 0
    fi
  fi

  stop_for_update "$was_running"
  install_updated_binary

  if [ "$skip_prompts" -ne 1 ] && [ "$was_running" != 'stopped' ] && has_prompt_terminal; then
    prompt_restart_after_update
    return
  fi

  print_stopped_summary '已升级'
}

run_update_now_flow() {
  local remote_version
  if running_from_installed_control_script; then
    run_bootstrap_update update-now
    return
  fi

  remote_version=$(get_remote_version)
  printf '将升级 AgentFlow 到最新版本'
  if [ -n "$remote_version" ]; then
    printf ' (%s)' "$remote_version"
  fi
  printf '\n'

  COMMAND=update-now
  AUTO_START_AFTER_INSTALL=0
  install_or_update
  AUTO_START_AFTER_INSTALL=1
  print_update_only_summary
}

run_install_menu() {
  local url start_choice
  select_access_url
  url=$PROMPT_REPLY
  write_access_url "$url"
  COMMAND=install
  AUTO_START_AFTER_INSTALL=0
  install_or_update
  AUTO_START_AFTER_INSTALL=1
  printf '现在启动 AgentFlow 吗？\n'
  printf '1. 立即启动（推荐）\n'
  printf '2. 稍后手动启动\n'
  printf '会优先注册为系统服务；若失败，则回退到后台用户进程。\n'
  menu_select "> "
  start_choice=$PROMPT_REPLY
  case "$start_choice" in
    1) start_best_effort '已启动' ;;
    *) print_manual_summary ;;
  esac
}

run_one_click_install() {
  write_access_url "$DEFAULT_ACCESS_URL"
  COMMAND=install
  AUTO_START_AFTER_INSTALL=0
  install_or_update
  AUTO_START_AFTER_INSTALL=1
  start_best_effort '已启动'
}

run_update_menu() {
  run_update_flow
}

run_uninstall_menu() {
  local run_mode
  run_mode=$(detect_run_mode)
  if [ "$run_mode" != 'stopped' ]; then
    printf '卸载将停止当前 AgentFlow 进程\n'
    confirm_default_no '是否继续？ [y/N] ' || return 0
  fi
  if confirm_default_no '是否同时删除数据和配置文件？ [y/N] '; then
    REMOVE_DATA=1
  fi
  uninstall_agentflow
}

interactive_menu() {
  local selection
  print_status_overview
  if [ -x "$BIN_PATH" ]; then
    printf '1. 升级\n'
    printf '2. 卸载\n'
  else
    printf '1. 一键安装\n'
    printf '2. 自定义安装\n'
  fi
  menu_select "> "
  selection=$PROMPT_REPLY
  case "$selection" in
    1)
      if [ -x "$BIN_PATH" ]; then
        run_update_menu
      else
        run_one_click_install
      fi
      ;;
    2)
      if [ -x "$BIN_PATH" ]; then
        run_uninstall_menu
      else
        run_install_menu
      fi
      ;;
    *)
      fail 'unknown menu selection'
      ;;
  esac
}

uninstall_agentflow() {
  run_service_action disable-now optional
  stop_background_instance
  stop_foreground_instance
  clear_run_state
  remove_global_bin_links
  remove_profile_block
  rm -f "$SERVICE_PATH" "$BIN_PATH" "$CTL_PATH"
  rmdir "$BIN_DIR" 2>/dev/null || true
  if [ "$REMOVE_DATA" -eq 1 ]; then
    rm -rf "$DATA_DIR"
  fi
  log 'Uninstall complete.'
}

show_version() {
  local installed_version
  installed_version=$(get_installed_version || true)
  printf 'installed=%s\n' "${installed_version:-unknown}"
}

show_remote_version() {
  local remote_version
  remote_version=$(get_remote_version)
  [ -n "$remote_version" ] || fail 'version metadata did not provide a version value.'
  printf '%s\n' "$remote_version"
}

doctor() {
  printf 'platform=linux\n'
  printf 'arch=%s\n' "$(detect_arch)"
  printf 'home=%s\n' "$AGENTFLOW_HOME"
  printf 'binary=%s\n' "$BIN_PATH"
  printf 'control=%s\n' "$CTL_PATH"
  printf 'data=%s\n' "$DATA_DIR"
  printf 'config=%s\n' "$ENV_PATH"
  printf 'service=%s\n' "$SERVICE_PATH"
  printf 'run_mode=%s\n' "$(detect_run_mode)"
}

case "$COMMAND" in
  menu) interactive_menu ;;
  install) install_or_update ;;
  update) run_update_flow ;;
  update-now)
    setup_update_log
    run_update_now_flow
    ;;
  uninstall) uninstall_agentflow ;;
  start|restart)
    if [ "$COMMAND" = 'start' ]; then
      start_best_effort '已启动'
    else
      restart_best_effort '已启动'
    fi
    ;;
  status)
    case "$(detect_run_mode)" in
      service)
        if run_service_action status optional; then
          printf 'running=service\n'
        else
          clear_run_state
          printf 'running=stopped\n'
        fi
        ;;
      background)
        printf 'running=background\n'
        ;;
      foreground)
        printf 'running=foreground\n'
        ;;
      *)
        printf 'running=stopped\n'
        ;;
    esac
    print_runtime_url
    ;;
  stop)
    case "$(detect_run_mode)" in
      background) stop_background_instance ;;
      foreground) stop_foreground_instance ;;
      service) run_service_action stop optional ;;
      *) true ;;
    esac
    ;;
  version) show_version ;;
  remote-version) show_remote_version ;;
  doctor) doctor ;;
  help|-h|--help) usage ;;
  *) fail "unknown command: $COMMAND" ;;
esac
