#!/usr/bin/env bash
# Plan B:整目录重装 /opt/crypto_monitor(备份 .env → 移走旧目录 → git clone → setup_env → 恢复配置 → PM2)
#
# 与 deploy/setup_env.sh 分工:
#   setup_env.sh  — 首次 / 日常:建 venv,装依赖,复制 .env.example(一键安装,不变)
#   reinstall.sh  — 生产清库重装:保留密钥与 hub 配置,丢弃旧代码/旧库/脏 PM2
#
# 用法(在现有安装目录以 root 执行):
#   cd /opt/crypto_monitor
#   bash deploy/reinstall.sh              # 交互确认
#   bash deploy/reinstall.sh --yes        # 跳过确认
#   bash deploy/reinstall.sh --dry-run    # 仅打印步骤
#
# 可选环境变量:
#   INSTALL_ROOT=/opt/crypto_monitor
#   GIT_URL=https://git.bz121.com/dekun/crypto_monitor.git
#   GIT_BRANCH=main
#   BACKUP_ROOT=/root/backups
#
set -e
set -u
if [ -n "${BASH_VERSION:-}" ]; then
  set -o pipefail
fi

DEPLOY_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
SCRIPT_SOURCE="${DEPLOY_DIR}/reinstall.sh"
REPO_ROOT="$(cd "${DEPLOY_DIR}/.." && pwd)"
# shellcheck source=lib/common.sh
source "${DEPLOY_DIR}/lib/common.sh"

INSTALL_ROOT="${INSTALL_ROOT:-/opt/crypto_monitor}"
GIT_URL="${GIT_URL:-https://git.bz121.com/dekun/crypto_monitor.git}"
GIT_BRANCH="${GIT_BRANCH:-main}"
BACKUP_ROOT="${BACKUP_ROOT:-/root/backups}"
TZ_NAME="${REINSTALL_TZ:-Asia/Shanghai}"

ASSUME_YES=0
DRY_RUN=0
INSTALL_BACKUP_CRON=1

CONFIG_PATHS=(
  "crypto_monitor_binance/.env"
  "crypto_monitor_okx/.env"
  "crypto_monitor_gate/.env"
  "manual_trading_hub/.env"
  "manual_trading_hub/hub_settings.json"
)

usage() {
  sed -n '2,18p' "$0" | sed 's/^# \?//'
  exit "${1:-0}"
}

while [[ $# -gt 0 ]]; do
  case "$1" in
    --yes|-y) ASSUME_YES=1; shift ;;
    --dry-run) DRY_RUN=1; shift ;;
    --no-backup-cron) INSTALL_BACKUP_CRON=0; shift ;;
    -h|--help) usage 0 ;;
    *) echo "未知参数: $1" >&2; usage 1 ;;
  esac
done

log() { printf '[%s] %s\n' "$(TZ="${TZ_NAME}" date '+%Y-%m-%d %H:%M:%S')" "$*"; }
step() { echo ""; log "==> $*"; }

run() {
  if [[ "${DRY_RUN}" -eq 1 ]]; then
    log "[dry-run] $*"
    return 0
  fi
  log "+ $*"
  "$@"
}

confirm() {
  if [[ "${ASSUME_YES}" -eq 1 || "${DRY_RUN}" -eq 1 ]]; then
    return 0
  fi
  local msg="$1"
  read -r -p "${msg} [y/N] " ans
  [[ "${ans}" == [yY] || "${ans}" == [yY][eE][sS] ]]
}

resolve_path() {
  local base="$1"
  local rel="$2"
  printf '%s/%s' "${base}" "${rel}"
}

backup_configs() {
  local src_root="$1"
  local dest="$2"
  mkdir -p "${dest}"
  local rel copied=0
  for rel in "${CONFIG_PATHS[@]}"; do
    local src
    src="$(resolve_path "${src_root}" "${rel}")"
    if [[ -f "${src}" ]]; then
      mkdir -p "${dest}/$(dirname "${rel}")"
      if [[ "${DRY_RUN}" -eq 1 ]]; then
        log "[dry-run] backup ${src} -> ${dest}/${rel}"
      else
        cp -a "${src}" "${dest}/${rel}"
        log "backup ${rel}"
      fi
      copied=$((copied + 1))
    else
      log "skip (missing): ${rel}"
    fi
  done
  if [[ "${copied}" -eq 0 ]]; then
    echo "错误: 未备份到任何配置文件,请检查 ${src_root}" >&2
    exit 1
  fi
  if [[ -f "${src_root}/scripts/one_shot_backup_config_before_cleanup.py" ]]; then
    if [[ "${DRY_RUN}" -eq 1 ]]; then
      log "[dry-run] python3 scripts/one_shot_backup_config_before_cleanup.py (in ${src_root})"
    else
      (cd "${src_root}" && python3 scripts/one_shot_backup_config_before_cleanup.py) || true
      if compgen -G "${src_root}/backups/one-shot-*" >/dev/null; then
        cp -a "${src_root}"/backups/one-shot-* "${dest}/" 2>/dev/null || true
      fi
    fi
  fi
  if [[ "${DRY_RUN}" -eq 0 ]]; then
    {
      echo "created_at=${STAMP}"
      echo "install_root=${INSTALL_ROOT}"
      echo "old_dir=${OLD_DIR}"
      echo "git_url=${GIT_URL}"
      echo "git_branch=${GIT_BRANCH}"
      echo "script=${SCRIPT_SOURCE}"
    } >"${dest}/reinstall.manifest"
  fi
}

restore_configs() {
  local backup_dir="$1"
  local dest_root="$2"
  local rel
  for rel in "${CONFIG_PATHS[@]}"; do
    local src dest
    src="${backup_dir}/${rel}"
    dest="$(resolve_path "${dest_root}" "${rel}")"
    if [[ -f "${src}" ]]; then
      mkdir -p "$(dirname "${dest}")"
      if [[ "${DRY_RUN}" -eq 1 ]]; then
        log "[dry-run] restore ${src} -> ${dest}"
      else
        cp -a "${src}" "${dest}"
        log "restore ${rel}"
      fi
    fi
  done
  local hub_settings
  hub_settings="$(resolve_path "${dest_root}" "manual_trading_hub/hub_settings.json")"
  if [[ -f "${hub_settings}" && "${DRY_RUN}" -eq 0 ]]; then
    python3 "${dest_root}/deploy/sanitize_hub_settings.py" "${hub_settings}" || true
  fi
}

install_instance_backup_cron() {
  local dest_root="$1"
  local dir
  for dir in crypto_monitor_binance crypto_monitor_gate crypto_monitor_okx; do
    local proj="${dest_root}/${dir}"
    local inst="${proj}/scripts/install_backup_cron.sh"
    local data="${proj}/scripts/backup_data.sh"
    if [[ -f "${inst}" && -f "${data}" ]]; then
      chmod +x "${inst}" "${data}"
      run bash "${inst}"
    fi
  done
}

verify_pm2() {
  log "预期 PM2 进程(7 个): crypto_binance crypto_gate crypto_okx manual-trading-hub manual-agent-*"
  if [[ "${DRY_RUN}" -eq 1 ]]; then
    return 0
  fi
  pm2 list || true
  if pm2 list 2>/dev/null | grep -qiE 'gate_bot|15203'; then
    log "警告: PM2 列表仍含 gate_bot 相关进程,请 pm2 delete 后 pm2 save"
  fi
}

# --- 前置检查 ---

if [[ "$(id -u)" -ne 0 ]]; then
  echo "请使用 root 执行(推荐路径 ${INSTALL_ROOT})" >&2
  exit 1
fi

if [[ ! -f "${REPO_ROOT}/deploy/setup_env.sh" ]]; then
  echo "当前脚本不在有效仓库内: ${REPO_ROOT}" >&2
  exit 1
fi

if [[ "${REPO_ROOT}" != "${INSTALL_ROOT}" ]]; then
  log "提示: 当前仓库 ${REPO_ROOT} 与 INSTALL_ROOT=${INSTALL_ROOT} 不一致;将备份当前仓库并克隆到 INSTALL_ROOT"
fi

STAMP="$(TZ="${TZ_NAME}" date +%Y%m%d-%H%M%S)"
BACKUP_DIR="${BACKUP_ROOT}/pre-reinstall-${STAMP}"
OLD_DIR="${INSTALL_ROOT}.old.${STAMP}"
SRC_ROOT="${REPO_ROOT}"

if [[ -d "${INSTALL_ROOT}" && "${REPO_ROOT}" != "${INSTALL_ROOT}" ]]; then
  SRC_ROOT="${INSTALL_ROOT}"
fi

step "计划"
echo "  备份目录: ${BACKUP_DIR}"
echo "  配置来源: ${SRC_ROOT}"
echo "  旧目录移走: ${OLD_DIR}"
echo "  新克隆: ${GIT_URL} (${GIT_BRANCH}) -> ${INSTALL_ROOT}"
echo "  环境: deploy/setup_env.sh --skip-env-copy --recreate-venv --skip-pm2"
echo ""
echo "  将停止并 delete 全部 PM2 进程;不备份 crypto.db / hub data / 图片."

if ! confirm "确认执行 Plan B 整目录重装?"; then
  log "已取消"
  exit 0
fi

# --- 1. 备份 ---

step "备份配置到 ${BACKUP_DIR}"
backup_configs "${SRC_ROOT}" "${BACKUP_DIR}"

# --- 2. 停 PM2 ---

step "停止并移除本项目 PM2 进程(不影响其它 PM2)"
if command -v pm2 >/dev/null 2>&1; then
  if [[ "${DRY_RUN}" -eq 1 ]]; then
    log "[dry-run] pm2 stop/delete 仅: ${PM2_APPS[*]} (及历史别名)"
  else
    pm2_stop_project_apps
    pm2_delete_project_apps
  fi
else
  log "未安装 pm2,跳过"
fi

# --- 3. 移走旧目录 ---

step "移走旧安装 ${INSTALL_ROOT} -> ${OLD_DIR}"
if [[ -d "${INSTALL_ROOT}" ]]; then
  if [[ "${DRY_RUN}" -eq 1 ]]; then
    log "[dry-run] mv ${INSTALL_ROOT} ${OLD_DIR}"
  else
    mv "${INSTALL_ROOT}" "${OLD_DIR}"
  fi
else
  log "目标目录不存在,跳过 mv"
fi

# --- 4. 克隆 ---

step "git clone"
if [[ "${DRY_RUN}" -eq 1 ]]; then
  log "[dry-run] git clone -b ${GIT_BRANCH} ${GIT_URL} ${INSTALL_ROOT}"
else
  git clone -b "${GIT_BRANCH}" "${GIT_URL}" "${INSTALL_ROOT}"
fi

# --- 5. setup_env(一键安装逻辑,不复制 .env)---

step "重建 Python 虚拟环境 (setup_env.sh)"
if [[ "${DRY_RUN}" -eq 1 ]]; then
  log "[dry-run] bash ${INSTALL_ROOT}/deploy/setup_env.sh --skip-env-copy --recreate-venv --skip-pm2"
else
  bash "${INSTALL_ROOT}/deploy/setup_env.sh" --skip-env-copy --recreate-venv --skip-pm2
fi

# --- 6. 恢复配置 ---

step "恢复 .env 与 hub_settings.json"
restore_configs "${BACKUP_DIR}" "${INSTALL_ROOT}"

# --- 7. PM2 启动 ---

step "PM2 启动全部进程"
if command -v pm2 >/dev/null 2>&1; then
  run bash "${INSTALL_ROOT}/deploy/pm2_start_all.sh"
  run pm2 save
else
  log "未安装 pm2;请手动: bash ${INSTALL_ROOT}/deploy/pm2_start_all.sh"
fi

# --- 8. 定时备份 cron(可选)---

if [[ "${INSTALL_BACKUP_CRON}" -eq 1 ]]; then
  step "安装三所每日备份 cron"
  install_instance_backup_cron "${INSTALL_ROOT}"
fi

# --- 完成 ---

step "完成"
verify_pm2
echo ""
echo "备份: ${BACKUP_DIR}"
echo "旧目录(确认无误后可删): ${OLD_DIR}"
echo ""
echo "验收建议:"
echo "  pm2 list"
echo "  curl -s -o /dev/null -w '%{http_code}\n' http://127.0.0.1:5100/"
echo "  浏览器打开中控 /monitor,确认三所 LINK 正常"
echo ""
echo "回滚(未删旧目录时):"
echo "  pm2 delete all"
echo "  rm -rf ${INSTALL_ROOT}"
echo "  mv ${OLD_DIR} ${INSTALL_ROOT}"
echo "  cp -a ${BACKUP_DIR}/*/ ${INSTALL_ROOT}/   # 若需恢复配置"
echo "  bash ${INSTALL_ROOT}/deploy/pm2_start_all.sh"
