#!/usr/bin/env bash
set -Eeuo pipefail

APP_NAME="${CC_WEB_PM2_NAME:-ccweb}"
ENTRY_FILE="${CC_WEB_ENTRY:-server.js}"

SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
APP_DIR="${SCRIPT_DIR}"
ENTRY_PATH="${APP_DIR}/${ENTRY_FILE}"
ENV_FILE="${APP_DIR}/.env"
ENV_EXAMPLE_FILE="${APP_DIR}/.env.example"

log() {
  printf '[cc-web pm2] %s\n' "$*"
}

fail() {
  printf '[cc-web pm2] ERROR: %s\n' "$*" >&2
  exit 1
}

warn() {
  printf '[cc-web pm2] WARNING: %s\n' "$*" >&2
}

node_upgrade_hint() {
  cat <<'EOF'
可选处理方式：
  # 已安装 nvm 时，使用当前 LTS 版本
  nvm install --lts
  nvm use --lts
  node -v

  # Ubuntu / Debian / WSL，可固定安装 Node.js 22；root 用户可去掉 sudo
  curl -fsSL https://deb.nodesource.com/setup_22.x | sudo -E bash -
  sudo apt-get install -y nodejs
  node -v

  # RHEL / Rocky / AlmaLinux 8/9，可固定安装 Node.js 22；root 用户可去掉 sudo
  curl -fsSL https://rpm.nodesource.com/setup_22.x | sudo bash -
  sudo dnf install -y nodejs || sudo yum install -y nodejs
  node -v

  # CentOS 7 / glibc 2.17 不适合安装 NodeSource Node.js 22。
  # 推荐在较新的构建机生成 Bun baseline 单文件发布包，再拷贝到 CentOS 7 直接运行：
  npm install
  npm run build:single-exe
  # 拷贝 dist-exe/bun-linux-x64-baseline/ 到 CentOS 7 后：
  cd dist-exe/bun-linux-x64-baseline
  PORT=8002 CC_WEB_PASSWORD='请改成强密码' ./cc-web
EOF
}

ensure_command() {
  local cmd="$1"
  local hint="$2"
  if ! command -v "${cmd}" >/dev/null 2>&1; then
    fail "${cmd} 未安装。${hint}"
  fi
}

get_env_value() {
  local key="$1"
  [[ -f "${ENV_FILE}" ]] || return 0

  awk -v key="${key}" '
    $0 ~ /^[[:space:]]*#/ { next }
    {
      line = $0
      sub(/^[[:space:]]*/, "", line)
      if (index(line, key "=") == 1) {
        sub(/^[^=]*=/, "", line)
        print line
        exit
      }
    }
  ' "${ENV_FILE}"
}

set_env_value() {
  local key="$1"
  local value="$2"
  local tmp_file
  tmp_file="$(mktemp)"

  if [[ -f "${ENV_FILE}" ]]; then
    awk -v key="${key}" -v value="${value}" '
      BEGIN { updated = 0 }
      {
        line = $0
        sub(/^[[:space:]]*/, "", line)
        if (index(line, key "=") == 1) {
          print key "=" value
          updated = 1
          next
        }
        print
      }
      END {
        if (!updated) print key "=" value
      }
    ' "${ENV_FILE}" > "${tmp_file}"
  else
    printf '%s=%s\n' "${key}" "${value}" > "${tmp_file}"
  fi

  mv "${tmp_file}" "${ENV_FILE}"
  chmod 600 "${ENV_FILE}"
}

validate_password() {
  local password="$1"
  local types=0

  if [[ "${#password}" -lt 8 ]]; then
    printf '密码长度至少 8 位\n'
    return 1
  fi

  [[ "${password}" =~ [a-z] ]] && types=$((types + 1))
  [[ "${password}" =~ [A-Z] ]] && types=$((types + 1))
  [[ "${password}" =~ [0-9] ]] && types=$((types + 1))
  [[ "${password}" =~ [^a-zA-Z0-9] ]] && types=$((types + 1))

  if [[ "${types}" -lt 2 ]]; then
    printf '密码需包含至少 2 种字符类型（大写/小写/数字/特殊字符）\n'
    return 1
  fi

  return 0
}

resolve_config_dir() {
  local configured
  configured="${CC_WEB_CONFIG_DIR:-$(get_env_value CC_WEB_CONFIG_DIR)}"

  if [[ -n "${configured}" ]]; then
    if [[ "${configured}" = /* ]]; then
      printf '%s\n' "${configured}"
    else
      printf '%s/%s\n' "${APP_DIR}" "${configured}"
    fi
    return
  fi

  printf '%s/config\n' "${APP_DIR}"
}

ensure_env_file() {
  if [[ -f "${ENV_FILE}" ]]; then
    log "检测到 .env 配置文件"
    chmod 600 "${ENV_FILE}"
    return
  fi

  if [[ -f "${ENV_EXAMPLE_FILE}" ]]; then
    cp "${ENV_EXAMPLE_FILE}" "${ENV_FILE}"
    chmod 600 "${ENV_FILE}"
    log "已从 .env.example 创建 .env"
  else
    : > "${ENV_FILE}"
    chmod 600 "${ENV_FILE}"
    log "已创建空 .env"
  fi
}

ensure_port_config() {
  local port
  port="$(get_env_value PORT)"

  if [[ -z "${port}" ]]; then
    set_env_value PORT "8002"
    log "未配置 PORT，已写入默认端口 8002"
    return
  fi

  log "服务端口: ${port}"
}

ensure_initial_password() {
  local config_dir auth_file env_password password confirm message

  config_dir="$(resolve_config_dir)"
  auth_file="${config_dir}/auth.json"

  if [[ -f "${auth_file}" ]]; then
    log "检测到已有登录配置: ${auth_file}，跳过初始密码设置"
    return
  fi

  env_password="$(get_env_value CC_WEB_PASSWORD)"
  if [[ -n "${env_password}" && "${env_password}" != "changeme" ]]; then
    log "检测到 .env 已配置 CC_WEB_PASSWORD，首次启动时会迁移到 auth.json"
    return
  fi

  if [[ ! -t 0 ]]; then
    fail "首次启动缺少初始密码。请在 .env 中设置 CC_WEB_PASSWORD，或在交互终端执行 ./start.sh。"
  fi

  log "首次启动需要设置 Web 登录初始密码"
  while true; do
    read -r -s -p "请输入初始密码: " password
    printf '\n'
    read -r -s -p "请再次输入初始密码: " confirm
    printf '\n'

    if [[ "${password}" != "${confirm}" ]]; then
      log "两次输入不一致，请重新输入"
      continue
    fi

    if ! message="$(validate_password "${password}")"; then
      log "${message}"
      continue
    fi

    set_env_value CC_WEB_PASSWORD "${password}"
    log "初始密码已写入 .env；服务首次启动后会迁移到 config/auth.json"
    break
  done
}

check_agent_cli() {
  local key="$1"
  local default_cmd="$2"
  local label="$3"
  local configured

  configured="$(get_env_value "${key}")"
  configured="${configured:-${default_cmd}}"

  if command -v "${configured}" >/dev/null 2>&1; then
    log "${label} CLI: $(command -v "${configured}")"
    return
  fi

  log "未检测到 ${label} CLI (${configured})；服务可启动，但对应 Agent 功能需要安装并登录后才能使用"
}

check_runtime_config() {
  ensure_env_file
  ensure_port_config
  ensure_initial_password
  check_agent_cli CLAUDE_PATH claude "Claude"
  check_agent_cli CODEX_PATH codex "Codex"
}

install_pm2() {
  if command -v pm2 >/dev/null 2>&1; then
    log "PM2 已安装: $(command -v pm2)"
    return
  fi

  log "未检测到 PM2，开始安装..."
  if npm install -g pm2; then
    log "PM2 安装完成"
    return
  fi

  if command -v sudo >/dev/null 2>&1; then
    log "普通 npm 全局安装失败，尝试使用 sudo 安装 PM2..."
    sudo npm install -g pm2
    log "PM2 安装完成"
    return
  fi

  fail "PM2 自动安装失败，且当前环境没有 sudo。请先手动安装: npm install -g pm2"
}

install_dependencies() {
  cd "${APP_DIR}"

  if [[ -f package-lock.json ]]; then
    log "安装项目依赖: npm ci"
    npm ci
  else
    log "安装项目依赖: npm install"
    npm install
  fi
}

start_or_restart_app() {
  cd "${APP_DIR}"

  if pm2 describe "${APP_NAME}" >/dev/null 2>&1; then
    log "PM2 应用已存在，执行重启: ${APP_NAME}"
    pm2 restart "${APP_NAME}" --update-env
  else
    log "启动 PM2 应用: ${APP_NAME}"
    pm2 start "${ENTRY_PATH}" --name "${APP_NAME}" --cwd "${APP_DIR}"
  fi

  log "保存 PM2 进程快照"
  pm2 save

  log "当前 PM2 状态"
  pm2 status "${APP_NAME}"
}

main() {
  if [[ "$(id -u)" -eq 0 ]]; then
    warn "当前正在使用 root 用户执行。该服务会启动 Claude/Codex 子进程，root 运行风险较高，建议改用非 root 用户。"
  fi

  [[ -f "${ENTRY_PATH}" ]] || fail "找不到入口文件: ${ENTRY_PATH}"

  ensure_command node "请先安装 Node.js 18 或更高版本。

$(node_upgrade_hint)"
  ensure_command npm "请先安装 npm。"

  local node_major
  node_major="$(node -p "Number(process.versions.node.split('.')[0])")"
  if [[ "${node_major}" -lt 18 ]]; then
    fail "Node.js 版本过低，当前为 $(node -v)，要求 >= 18。

$(node_upgrade_hint)"
  fi

  check_runtime_config
  install_pm2
  install_dependencies
  start_or_restart_app

  log "完成。若需要开机自启，请执行 pm2 startup，并按输出提示执行生成的命令。"
}

main "$@"
