#!/usr/bin/env bash
# 芯化和云 SRE Agent — 一键安装 / 升级（独立产品包，可与监控同机或分机）
#
# 用法:
#   curl -fsSL https://downloads.monitor.chem-cloud.cn/install_sre_agent.sh -o install_sre_agent.sh && bash install_sre_agent.sh
#
# 可选参数:
#   bash install_sre_agent.sh              # 自动：有现网则升级，否则全新安装
#   bash install_sre_agent.sh install      # 强制全新安装
#   bash install_sre_agent.sh upgrade      # 强制升级
#   bash install_sre_agent.sh -h
#
# 环境变量（可选）:
#   DOWNLOAD_BASE=https://downloads.monitor.chem-cloud.cn
#   VERSION=1.0.0
#   INSTALL_PARENT=/root
#   MONITOR_API_BASE=https://demo.monitor.chem-cloud.cn
#   JWT_SECRET=...                 # 须与监控 JWT_SECRET 一致
#   MONITOR_READ_TOKEN=...         # 与监控 MONITOR_READ_TOKEN 一致
#   PROMETHEUS_URL=http://host.docker.internal:9090
#   LOKI_URL=http://host.docker.internal:3100
#   NONINTERACTIVE=1
set -euo pipefail

DOWNLOAD_BASE="${DOWNLOAD_BASE:-https://downloads.monitor.chem-cloud.cn}"
INSTALL_PARENT="${INSTALL_PARENT:-$(pwd)}"
NONINTERACTIVE="${NONINTERACTIVE:-0}"

die() { echo "ERROR: $*" >&2; exit 1; }
info() { echo "==> $*"; }
warn() { echo "WARN: $*" >&2; }

usage() {
  cat <<'EOF'
用法:
  install_sre_agent.sh [install|upgrade|auto]   默认 auto

环境变量:
  DOWNLOAD_BASE       下载站，默认 https://downloads.monitor.chem-cloud.cn
  VERSION             版本号；空则读取下载站 /latest
  INSTALL_PARENT      安装父目录，默认当前目录
  MONITOR_API_BASE    监控 API 基址（如 https://demo.monitor.chem-cloud.cn）
  JWT_SECRET          与监控 JWT_SECRET 相同（Web 对话鉴权）
  MONITOR_READ_TOKEN  与监控 MONITOR_READ_TOKEN 相同
  PROMETHEUS_URL      默认 http://host.docker.internal:9090（同机）
  LOKI_URL            默认 http://host.docker.internal:3100（同机）
  NONINTERACTIVE=1    无交互；缺 MONITOR_API_BASE / JWT_SECRET 时仅警告并继续
EOF
}

need_cmd() {
  command -v "$1" >/dev/null 2>&1 || die "缺少命令: $1"
}

require_docker() {
  need_cmd docker
  docker compose version >/dev/null 2>&1 || die "需要 docker compose 插件"
}

resolve_version() {
  if [ -n "${VERSION:-}" ]; then
    echo "${VERSION}"
    return
  fi
  local v=""
  if command -v curl >/dev/null 2>&1; then
    v="$(curl -fsS --connect-timeout 10 "${DOWNLOAD_BASE}/latest" 2>/dev/null | tr -d '[:space:]' || true)"
  fi
  echo "${v:-1.0.0}"
}

download() {
  local url="$1" out="$2"
  local attempt=1 max_attempts=8
  if command -v curl >/dev/null 2>&1; then
    while [ "${attempt}" -le "${max_attempts}" ]; do
      info "下载尝试 ${attempt}/${max_attempts}: ${url}"
      if curl -fL --http1.1 \
        --connect-timeout 30 \
        --retry 3 \
        --retry-delay 2 \
        --speed-time 60 \
        --speed-limit 2048 \
        -C - \
        -o "${out}" \
        "${url}"
      then
        return 0
      fi
      warn "下载中断，${attempt}/${max_attempts}，3 秒后续传..."
      attempt=$((attempt + 1))
      sleep 3
    done
    die "下载失败: ${url}"
  elif command -v wget >/dev/null 2>&1; then
    wget --continue -O "${out}" "${url}" || die "下载失败: ${url}"
  else
    die "需要 curl 或 wget"
  fi
}

download_product() {
  local version="$1" dest_tar="$2"
  local url="${DOWNLOAD_BASE}/sre-agent-product-${version}.tar.gz"
  info "下载 SRE 产品包: ${url}"
  download "${url}" "${dest_tar}"
  ls -lh "${dest_tar}"
}

extract_product() {
  local tar_path="$1" dest_dir="$2"
  rm -rf "${dest_dir}"
  mkdir -p "${dest_dir}"
  tar xzf "${tar_path}" -C "${dest_dir}" --strip-components=1
  [ -f "${dest_dir}/install.sh" ] || die "解压后缺少 install.sh"
  find "${dest_dir}" -type f \( -name '*.sh' -o -name '*.yml' \) -exec sed -i 's/\r$//' {} + 2>/dev/null || true
  chmod +x "${dest_dir}/install.sh"
}

detect_old_dir() {
  # 1) compose 项目名
  local cid
  cid="$(docker ps -q --filter "name=sre-agent-product" 2>/dev/null | head -n1 || true)"
  if [ -n "${cid}" ]; then
    local labels workdir
    labels="$(docker inspect -f '{{index .Config.Labels "com.docker.compose.project.working_dir"}}' "${cid}" 2>/dev/null || true)"
    if [ -n "${labels}" ] && [ -d "${labels}" ]; then
      echo "${labels}"
      return
    fi
  fi
  # 2) 常见路径
  local cand
  for cand in \
    "${INSTALL_PARENT}/sre-agent-product-1.0.0" \
    /root/sre-agent-product-1.0.0 \
    "$(pwd)/sre-agent-product-1.0.0"
  do
    if [ -f "${cand}/docker-compose.yml" ] && [ -f "${cand}/.env" ]; then
      echo "${cand}"
      return
    fi
  done
  echo ""
}

detect_monitor_env() {
  # 尝试从同机监控 .env 读取 JWT / READ_TOKEN / PUBLIC_BASE
  local mon=""
  local cid
  cid="$(docker ps -q --filter "name=monitor-product" 2>/dev/null | head -n1 || true)"
  if [ -n "${cid}" ]; then
    mon="$(docker inspect -f '{{index .Config.Labels "com.docker.compose.project.working_dir"}}' "${cid}" 2>/dev/null || true)"
  fi
  if [ -z "${mon}" ]; then
    for cand in /root/monitor-product-1.0.0 "${INSTALL_PARENT}/monitor-product-1.0.0"; do
      [ -f "${cand}/.env" ] && mon="${cand}" && break
    done
  fi
  if [ -n "${mon}" ] && [ -f "${mon}/.env" ]; then
    echo "${mon}"
  else
    echo ""
  fi
}

read_env_val() {
  local file="$1" key="$2"
  grep -E "^[[:space:]]*${key}=" "${file}" 2>/dev/null | tail -n1 | cut -d= -f2- | tr -d '\r' | sed 's/^"//;s/"$//' || true
}

ask() {
  local prompt="$1" def="${2:-}" ans=""
  if [ "${NONINTERACTIVE}" = "1" ]; then
    echo "${def}"
    return
  fi
  if [ -n "${def}" ]; then
    read -r -p "${prompt} [${def}]: " ans </dev/tty || true
    echo "${ans:-$def}"
  else
    read -r -p "${prompt}: " ans </dev/tty || true
    echo "${ans}"
  fi
}

upsert_env() {
  local file="$1" key="$2" val="$3"
  if grep -qE "^[[:space:]]*${key}=" "${file}" 2>/dev/null; then
    sed -i.bak "s|^[[:space:]]*${key}=.*|${key}=${val}|" "${file}"
  else
    echo "${key}=${val}" >> "${file}"
  fi
}

prepare_env() {
  local dir="$1"
  local envf="${dir}/.env"
  if [ ! -f "${envf}" ]; then
    cp -f "${dir}/.env.example" "${envf}"
  fi

  local mon_dir
  mon_dir="$(detect_monitor_env)"
  local jwt="${JWT_SECRET:-}"
  local read_token="${MONITOR_READ_TOKEN:-}"
  local api_base="${MONITOR_API_BASE:-}"
  local prom="${PROMETHEUS_URL:-}"
  local loki="${LOKI_URL:-}"
  local pub_base=""

  if [ -n "${mon_dir}" ]; then
    info "检测到同机监控目录: ${mon_dir}"
    [ -z "${jwt}" ] && jwt="$(read_env_val "${mon_dir}/.env" JWT_SECRET)"
    [ -z "${read_token}" ] && read_token="$(read_env_val "${mon_dir}/.env" MONITOR_READ_TOKEN)"
    pub_base="$(read_env_val "${mon_dir}/.env" PUBLIC_BASE_URL)"
    local domain host
    domain="$(read_env_val "${mon_dir}/.env" DOMAIN)"
    host="$(read_env_val "${mon_dir}/.env" PUBLIC_HOST)"
    if [ -z "${api_base}" ]; then
      if [ -n "${pub_base}" ]; then
        api_base="${pub_base}"
      elif [ -n "${domain}" ]; then
        api_base="https://${domain}"
      elif [ -n "${host}" ]; then
        api_base="http://${host}"
      fi
    fi
    if [ -z "${prom}" ]; then
      prom="http://host.docker.internal:9090"
    fi
    if [ -z "${loki}" ]; then
      loki="http://host.docker.internal:3100"
    fi
  fi

  if [ -z "${api_base}" ]; then
    api_base="$(ask "监控 API 基址 MONITOR_API_BASE（例 https://demo.example.com）" "")"
  fi
  if [ -z "${jwt}" ]; then
    jwt="$(ask "JWT_SECRET（须与监控一致）" "")"
  fi
  if [ -z "${read_token}" ]; then
    read_token="$(ask "MONITOR_READ_TOKEN（可与监控同值，空则安装时自动生成）" "")"
  fi
  [ -z "${prom}" ] && prom="http://host.docker.internal:9090"
  [ -z "${loki}" ] && loki="http://host.docker.internal:3100"

  # compose 访问宿主机监控端口
  if ! grep -q host.docker.internal "${dir}/docker-compose.yml" 2>/dev/null; then
    if grep -q 'image: sre-agent:' "${dir}/docker-compose.yml"; then
      sed -i '/image: sre-agent:/a\    extra_hosts:\n      - "host.docker.internal:host-gateway"' "${dir}/docker-compose.yml" || true
    fi
  fi

  [ -n "${api_base}" ] && upsert_env "${envf}" MONITOR_API_BASE "${api_base}"
  [ -n "${jwt}" ] && upsert_env "${envf}" JWT_SECRET "${jwt}"
  [ -n "${read_token}" ] && upsert_env "${envf}" MONITOR_READ_TOKEN "${read_token}"
  upsert_env "${envf}" PROMETHEUS_URL "${prom}"
  upsert_env "${envf}" LOKI_URL "${loki}"
  if [ -n "${pub_base}" ]; then
    upsert_env "${envf}" PUBLIC_BASE_URL "${pub_base}"
    upsert_env "${envf}" AGENT_REPORTS_BASE_URL "${pub_base}/agent-reports/"
    upsert_env "${envf}" REPORT_PUBLISH_API_BASE "${pub_base}"
  fi
  # cron 必须加引号，避免被 bash 解析
  if grep -qE '^INSPECTION_CRON=0 ' "${envf}" 2>/dev/null; then
    upsert_env "${envf}" INSPECTION_CRON '"0 9 * * *"'
  fi
}

print_done() {
  local dir="$1"
  echo
  echo "========================================"
  echo " SRE Agent 安装完成"
  echo " 目录: ${dir}"
  echo " 健康检查: http://127.0.0.1:8088/healthz"
  echo " 请确认 .env 中已配置："
  echo "   JWT_SECRET / MONITOR_API_BASE / MONITOR_READ_TOKEN"
  echo "   LLM_BASE_URL / LLM_API_KEY / LLM_MODEL  （对话必填）"
  echo "   WECOM_*  （企微可选）"
  echo " 监控侧请开启告警回调："
  echo "   SRE_AGENT_ENABLED=true"
  echo "   SRE_AGENT_URL=http://host.docker.internal:8088"
  echo "   SRE_AGENT_CALLBACK_TOKEN=<与本包 ALERT_CALLBACK_TOKEN 相同>"
  echo "========================================"
}

copy_preserve() {
  local src="$1" dst="$2"
  if [ -d "${src}" ]; then
    mkdir -p "${dst}"
    cp -a "${src}/." "${dst}/"
  fi
}

do_install() {
  local version="$1"
  local parent="${INSTALL_PARENT}"
  local tar_path="${parent}/sre-agent-product-${version}.tar.gz"
  local new_dir="${parent}/sre-agent-product-${version}"

  mkdir -p "${parent}"
  cd "${parent}"

  if [ -d "${new_dir}" ] && [ -f "${new_dir}/docker-compose.yml" ]; then
    die "目录已存在: ${new_dir}。若要升级请用: bash install_sre_agent.sh upgrade"
  fi

  download_product "${version}" "${tar_path}"
  extract_product "${tar_path}" "${new_dir}"
  prepare_env "${new_dir}"

  info "开始安装: ${new_dir}"
  ( cd "${new_dir}" && ./install.sh )
  print_done "${new_dir}"
}

do_upgrade() {
  local version="$1"
  local old_dir="${OLD_DIR:-}"
  if [ -z "${old_dir}" ]; then
    old_dir="$(detect_old_dir)"
  fi
  if [ -z "${old_dir}" ] || [ ! -d "${old_dir}" ]; then
    die "未找到现网 SRE 目录。可: OLD_DIR=/path bash install_sre_agent.sh upgrade"
  fi
  [ -f "${old_dir}/.env" ] || die "现网目录缺少 .env: ${old_dir}"

  local parent tar_path stage bak
  parent="$(dirname "${old_dir}")"
  tar_path="${parent}/sre-agent-product-${version}.tar.gz"
  stage="${parent}/.sre-upgrade-staging.$$"
  bak="${parent}/.sre-upgrade-bak.$$"

  info "原地升级目录: ${old_dir}"
  download_product "${version}" "${tar_path}"
  extract_product "${tar_path}" "${stage}"

  cp -a "${old_dir}/.env" "${stage}/.env"
  copy_preserve "${old_dir}/data" "${stage}/data"

  info "停止旧实例（不删数据卷）"
  ( cd "${old_dir}" && docker compose down ) || true

  rm -rf "${bak}"
  mv "${old_dir}" "${bak}"
  mv "${stage}" "${old_dir}"

  if ( cd "${old_dir}" && ./install.sh ); then
    rm -rf "${bak}"
  else
    warn "安装失败，回滚..."
    ( cd "${old_dir}" && docker compose down ) || true
    rm -rf "${old_dir}"
    mv "${bak}" "${old_dir}"
    die "升级失败，已回滚: ${old_dir}"
  fi
  print_done "${old_dir}"
}

main() {
  local mode="${1:-auto}"
  case "${mode}" in
    -h|--help|help) usage; exit 0 ;;
    install|upgrade|auto) ;;
    *) die "未知参数: ${mode}（用 -h 查看帮助）" ;;
  esac

  require_docker
  local version
  version="$(resolve_version)"
  info "VERSION=${version}"
  info "DOWNLOAD_BASE=${DOWNLOAD_BASE}"

  if [ "${mode}" = "auto" ]; then
    local detected=""
    detected="$(detect_old_dir)"
    if [ -n "${detected}" ]; then
      info "检测到现网 SRE: ${detected} → 升级"
      OLD_DIR="${detected}" do_upgrade "${version}"
    else
      info "未检测到现网 SRE → 全新安装"
      do_install "${version}"
    fi
  elif [ "${mode}" = "install" ]; then
    do_install "${version}"
  else
    do_upgrade "${version}"
  fi
}

main "${1:-auto}"
