#!/usr/bin/env bash
#
# appctl.sh — pi-web 应用控制脚本
# 基于 pm2 + ecosystem.config.cjs，封装启动 / 停止 / 重启 / 构建 / 部署等日常运维操作。
#
# 用法: ./appctl.sh <命令> [参数]
#   ./appctl.sh help        查看完整命令列表
#

set -euo pipefail

# ===================== 配置（可按需修改） =====================
APP_NAME="${APP_NAME:-pi-web}"
APP_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
ECOSYSTEM_FILE="$APP_DIR/ecosystem.config.cjs"
PM2="${PM2:-pm2}"
# 包管理器：留空则自动检测（存在 bun.lock 优先 bun，否则 npm）
PKG_MGR="${PKG_MGR:-}"
# ===================== 配置结束 =====================

cd "$APP_DIR"

# ---- 颜色输出 ----
if [[ -t 1 ]]; then
  C_BLUE='\033[0;34m'; C_GREEN='\033[0;32m'; C_YELLOW='\033[0;33m'
  C_RED='\033[0;31m'; C_CYAN='\033[0;36m'; C_BOLD='\033[1m'; C_NC='\033[0m'
else
  C_BLUE=''; C_GREEN=''; C_YELLOW=''; C_RED=''; C_CYAN=''; C_BOLD=''; C_NC=''
fi
info()  { printf "${C_BLUE}[INFO]${C_NC} %s\n"  "$*"; }
ok()    { printf "${C_GREEN}[OK]${C_NC} %s\n"   "$*"; }
warn()  { printf "${C_YELLOW}[WARN]${C_NC} %s\n" "$*"; }
err()   { printf "${C_RED}[ERROR]${C_NC} %s\n"  "$*" >&2; }
step()  { printf "${C_CYAN}▶ %s${C_NC}\n" "$*"; }

# ---- 工具函数 ----
detect_pkg_mgr() {
  if [[ -n "$PKG_MGR" ]]; then return; fi
  if command -v bun >/dev/null 2>&1 && [[ -f "$APP_DIR/bun.lock" ]]; then
    PKG_MGR="bun"
  else
    PKG_MGR="npm"
  fi
}

ensure_pm2() {
  if ! command -v "$PM2" >/dev/null 2>&1; then
    err "未找到 pm2，请先安装：npm install -g pm2"
    exit 1
  fi
}

ensure_ecosystem() {
  if [[ ! -f "$ECOSYSTEM_FILE" ]]; then
    err "未找到配置文件 $ECOSYSTEM_FILE"
    exit 1
  fi
}

ensure_node_modules() {
  if [[ ! -d "$APP_DIR/node_modules" ]]; then
    warn "未找到 node_modules，先执行 install"
    cmd_install
  fi
}

# 应用是否在 pm2 中在线运行
is_online() {
  local pid
  pid="$($PM2 pid "$APP_NAME" 2>/dev/null || true)"
  [[ -n "$pid" && "$pid" != "0" ]]
}

# 应用是否已在 pm2 注册（不论 online/stopped）
is_registered() {
  $PM2 describe "$APP_NAME" >/dev/null 2>&1
}

# ---- 命令实现 ----
cmd_install() {
  detect_pkg_mgr
  step "安装依赖 ($PKG_MGR install)"
  "$PKG_MGR" install
  ok "依赖安装完成"
}

cmd_build() {
  detect_pkg_mgr
  ensure_node_modules
  step "构建项目 ($PKG_MGR run build)"
  "$PKG_MGR" run build
  ok "构建完成"
}

cmd_start() {
  ensure_pm2
  ensure_ecosystem
  if is_online; then
    warn "$APP_NAME 已在运行中（如需重新加载请用 restart）"
    return 0
  fi
  if [[ ! -d "$APP_DIR/.next" ]]; then
    warn "未找到构建产物 .next，建议先执行 ./appctl.sh build"
  fi
  step "启动 $APP_NAME"
  $PM2 start "$ECOSYSTEM_FILE"
  ok "$APP_NAME 已启动"
}

cmd_stop() {
  ensure_pm2
  if ! is_registered; then
    warn "$APP_NAME 未在 pm2 中注册"
    return 0
  fi
  step "停止 $APP_NAME"
  $PM2 stop "$APP_NAME"
  ok "$APP_NAME 已停止"
}

cmd_restart() {
  ensure_pm2
  ensure_ecosystem
  if ! is_registered; then
    warn "$APP_NAME 未注册，改为执行 start"
    cmd_start
    return
  fi
  step "重启 $APP_NAME"
  $PM2 restart "$ECOSYSTEM_FILE" --update-env
  ok "$APP_NAME 已重启"
}

cmd_reload() {
  ensure_pm2
  ensure_ecosystem
  if ! is_registered; then
    warn "$APP_NAME 未注册，改为执行 start"
    cmd_start
    return
  fi
  step "零停机重载 $APP_NAME"
  $PM2 reload "$ECOSYSTEM_FILE" --update-env
  ok "$APP_NAME 已 reload"
}

cmd_status() {
  ensure_pm2
  if is_registered; then
    $PM2 describe "$APP_NAME"
  else
    warn "$APP_NAME 未注册"
  fi
  echo
  $PM2 list
}

cmd_logs() {
  ensure_pm2
  if ! is_registered; then
    warn "$APP_NAME 未注册，可能没有日志"
  fi
  $PM2 logs "$APP_NAME" --lines 200
}

# 用法: ./appctl.sh deploy [--no-install]
cmd_deploy() {
  local do_install=1
  if [[ "${1:-}" == "--no-install" ]]; then do_install=0; fi

  step "部署 $APP_NAME"
  if [[ -d "$APP_DIR/.git" ]]; then
    step "拉取最新代码 (git pull --ff-only)"
    git -C "$APP_DIR" pull --ff-only
  else
    warn "当前目录非 git 仓库，跳过 git pull"
  fi

  if (( do_install )); then
    cmd_install
  else
    info "已跳过依赖安装 (--no-install)"
  fi
  cmd_build
  cmd_restart
  ok "部署完成"
  $PM2 list
}

cmd_startup() {
  ensure_pm2
  step "保存进程列表"
  $PM2 save
  echo
  info "下面将生成开机自启命令，请按提示执行其中的 sudo 命令："
  $PM2 startup
}

cmd_delete() {
  ensure_pm2
  step "从 pm2 移除 $APP_NAME"
  $PM2 delete "$APP_NAME" >/dev/null 2>&1 || warn "$APP_NAME 未注册，无需删除"
  ok "$APP_NAME 已从 pm2 移除"
}

usage() {
  cat <<EOF
${C_BOLD}pi-web 应用控制脚本${C_NC}

${C_BOLD}用法:${C_NC} ./appctl.sh <命令> [参数]

${C_BOLD}生命周期:${C_NC}
  start                 启动应用（pm2 start ecosystem）
  stop                  停止应用
  restart               重启应用（重新加载环境变量）
  reload                零停机重载
  delete                从 pm2 列表移除应用

${C_BOLD}查看:${C_NC}
  status                查看运行状态
  logs                  查看实时日志（Ctrl+C 退出）

${C_BOLD}构建与部署:${C_NC}
  install               安装依赖（bun 或 npm，自动检测）
  build                 构建生产产物到 .next/
  deploy [--no-install] git pull + (install) + build + restart

${C_BOLD}其他:${C_NC}
  startup               保存进程列表 + 配置开机自启
  help                  显示本帮助

${C_BOLD}可覆盖的环境变量:${C_NC}
  APP_NAME              应用名（默认 pi-web）
  PM2                   pm2 可执行路径
  PKG_MGR               包管理器（bun/npm，留空自动检测）
EOF
}

# ---- 入口 ----
main() {
  local cmd="${1:-help}"
  shift || true
  case "$cmd" in
    start)              cmd_start ;;
    stop)               cmd_stop ;;
    restart)            cmd_restart ;;
    reload)             cmd_reload ;;
    status|list|ps)     cmd_status ;;
    logs|log)           cmd_logs ;;
    install)            cmd_install ;;
    build)              cmd_build ;;
    deploy)             cmd_deploy "$@" ;;
    startup)            cmd_startup ;;
    delete|remove)      cmd_delete ;;
    help|-h|--help)     usage ;;
    *)
      err "未知命令: $cmd"
      echo
      usage
      exit 1
      ;;
  esac
}

main "$@"
