# shellcheck shell=bash
# AI 建站 Linux 部署公共函数。由 start/restart/stop/pull-and-restart 在 ROOT 下 source。

run_sudo() {
  if [ "$(id -u)" -eq 0 ]; then
    "$@"
  else
    sudo "$@"
  fi
}

ensure_runtime_dirs() {
  mkdir -p "$ROOT/.runtime/pgdata" "$ROOT/.runtime/uploads" "$ROOT/.runtime/logs" \
    "$ROOT/.runtime/logs/generate" "$ROOT/web/dist"
}

ensure_env_file() {
  if [ ! -f "$ROOT/.env" ]; then
    if [ -f "$ROOT/.env.example" ]; then
      cp "$ROOT/.env.example" "$ROOT/.env"
      echo "已从 .env.example 创建 .env，请按需填写 API Key。"
    else
      cat >"$ROOT/.env" <<'EOF'
LLM_PROVIDER=deepseek
DEEPSEEK_API_KEY=
MINIMAX_API_KEY=
EOF
      echo "已创建默认 .env"
    fi
  fi
  sed -i 's/\r$//' "$ROOT/.env" 2>/dev/null || true
  # 供 compose 变量替换与本脚本健康检查使用
  set -a
  # shellcheck disable=SC1091
  [ -f "$ROOT/.env" ] && . "$ROOT/.env"
  set +a
  export AIJZ_WEB_PUBLISH="${AIJZ_WEB_PUBLISH:-127.0.0.1:5173}"
  export AIJZ_GATEWAY_PUBLISH="${AIJZ_GATEWAY_PUBLISH:-127.0.0.1:8180}"
  export AIJZ_PLATFORM_PUBLISH="${AIJZ_PLATFORM_PUBLISH:-127.0.0.1:8888}"
  export AIJZ_AI_PUBLISH="${AIJZ_AI_PUBLISH:-127.0.0.1:8001}"
  export AIJZ_PG_PUBLISH="${AIJZ_PG_PUBLISH:-127.0.0.1:5432}"
  export AIJZ_ENABLE_HOST_NGINX="${AIJZ_ENABLE_HOST_NGINX:-0}"
  export AIJZ_DOMAIN="${AIJZ_DOMAIN:-}"
  export AIJZ_PUBLIC_BASE_URL="${AIJZ_PUBLIC_BASE_URL:-}"
}

# 若 .env 设了 AIJZ_PUBLIC_BASE_URL，同步到 platform.docker.yaml（挂载文件，restart platform 即生效）
apply_public_base_url() {
  local base="${AIJZ_PUBLIC_BASE_URL:-}"
  local pf="$ROOT/platform/etc/platform.docker.yaml"
  [ -n "$base" ] || return 0
  [ -f "$pf" ] || return 0
  base="${base%/}"
  local storage_base="${base}/api/v1/storage"
  if grep -q '^PublicBaseURL:' "$pf"; then
    sed -i "s|^PublicBaseURL:.*|PublicBaseURL: \"${base}\"|" "$pf"
  fi
  if grep -q '^  PublicBase:' "$pf"; then
    sed -i "s|^  PublicBase:.*|  PublicBase: \"${storage_base}\"|" "$pf"
  fi
  echo "已同步 PublicBaseURL -> ${base}（platform/etc/platform.docker.yaml）"
}

sync_ssl_certs() {
  local domain="${AIJZ_DOMAIN:-}"
  [ -n "$domain" ] || return 0
  local ssl_dir="/etc/ssl/aijianzhan/$domain"
  run_sudo mkdir -p "$ssl_dir"
  if [ -f "$ROOT/nginx/$domain.pem" ] && [ -f "$ROOT/nginx/$domain.key" ]; then
    run_sudo cp -f "$ROOT/nginx/$domain.pem" "$ssl_dir/fullchain.pem"
    run_sudo cp -f "$ROOT/nginx/$domain.key" "$ssl_dir/privkey.pem"
  elif [ -f "$ROOT/nginx/fullchain.pem" ] && [ -f "$ROOT/nginx/privkey.pem" ]; then
    run_sudo cp -f "$ROOT/nginx/fullchain.pem" "$ROOT/nginx/privkey.pem" "$ssl_dir/"
  elif [ -f "$ROOT/nginx/$domain/fullchain.pem" ] && [ -f "$ROOT/nginx/$domain/privkey.pem" ]; then
    run_sudo cp -f "$ROOT/nginx/$domain/fullchain.pem" "$ROOT/nginx/$domain/privkey.pem" "$ssl_dir/"
  else
    echo "提示: 未找到 nginx/ 下证书，宿主机 HTTPS 需手动放入 $ssl_dir" >&2
    return 0
  fi
  run_sudo chmod 644 "$ssl_dir/fullchain.pem" 2>/dev/null || true
  run_sudo chmod 600 "$ssl_dir/privkey.pem" 2>/dev/null || true
  echo "证书已同步 -> $ssl_dir"
}

host_nginx_online() {
  command -v systemctl >/dev/null 2>&1 || return 1
  systemctl is-active nginx >/dev/null 2>&1 && return 0
  systemctl is-active nginx.service >/dev/null 2>&1 && return 0
  return 1
}

ensure_host_nginx_started() {
  command -v nginx >/dev/null 2>&1 || {
    echo "错误: 未安装 nginx，无法启用 AIJZ_ENABLE_HOST_NGINX" >&2
    exit 1
  }
  if host_nginx_online; then
    return 0
  fi
  echo "宿主机 Nginx 未在线，尝试启动..."
  run_sudo systemctl start nginx 2>/dev/null || run_sudo systemctl start nginx.service
  run_sudo systemctl enable nginx 2>/dev/null || true
  host_nginx_online || {
    echo "错误: 无法启动宿主机 nginx" >&2
    exit 1
  }
}

install_host_nginx_site_conf() {
  local domain="${AIJZ_DOMAIN:-}"
  [ -n "$domain" ] || return 0
  [ "${AIJZ_ENABLE_HOST_NGINX:-0}" = "1" ] || return 0
  local tpl="$ROOT/nginx/aijz.host.conf"
  local out="/etc/nginx/conf.d/aijz_${domain}.conf"
  local web_port gw_port
  web_port="$(publish_host_port "${AIJZ_WEB_PUBLISH:-127.0.0.1:5173}")"
  gw_port="$(publish_host_port "${AIJZ_GATEWAY_PUBLISH:-127.0.0.1:8180}")"
  [ -f "$tpl" ] || {
    echo "未找到 $tpl，跳过宿主机 Nginx 配置" >&2
    return 0
  }
  mkdir -p "$ROOT/verify-root"
  sync_ssl_certs
  sed -e "s|__DOMAIN__|${domain}|g" \
      -e "s|__WEB_PORT__|${web_port}|g" \
      -e "s|__GATEWAY_PORT__|${gw_port}|g" \
      -e "s|__VERIFY_ROOT__|${ROOT}/verify-root|g" \
      "$tpl" | run_sudo tee "$out" >/dev/null
  if ! run_sudo nginx -t 2>/dev/null; then
    echo "错误: nginx -t 失败，请检查 $out" >&2
    exit 1
  fi
  if host_nginx_online; then
    run_sudo systemctl reload nginx 2>/dev/null && echo "宿主机 Nginx 已重载（$out）"
  else
    ensure_host_nginx_started
  fi
}

reload_web_nginx() {
  # compose 挂的是 templates：改 conf 后需 recreate 才会重新 envsubst
  echo "重建 web 容器以应用 nginx 模板 ..."
  if compose_cmd up -d --force-recreate web; then
    echo "web 容器已 recreate（nginx.conf.template）"
  else
    echo "web 重建失败" >&2
    return 1
  fi
}

restart_service() {
  local svc="$1"
  compose_cmd restart "$svc"
  echo "已 restart $svc"
}

reload_host_nginx() {
  [ "${AIJZ_ENABLE_HOST_NGINX:-0}" = "1" ] || {
    echo "AIJZ_ENABLE_HOST_NGINX 未启用，跳过宿主机 Nginx" >&2
    return 0
  }
  install_host_nginx_site_conf
}

reload_all_config() {
  ensure_env_file
  apply_public_base_url
  reload_host_nginx || true
  reload_web_nginx || true
  restart_service platform
  restart_service gateway
  compose_cmd up -d --force-recreate ai >/dev/null 2>&1 || compose_cmd restart ai
  echo "配置重载完成（未 rebuild 镜像、未重建 web/dist）"
}

ensure_docker() {
  if command -v docker >/dev/null 2>&1 && run_sudo docker info >/dev/null 2>&1; then
    echo "Docker 已就绪."
    return 0
  fi
  if command -v docker >/dev/null 2>&1; then
    echo "Docker 守护进程未连接，尝试启动..."
    run_sudo systemctl start docker 2>/dev/null || run_sudo systemctl start podman 2>/dev/null || true
    if run_sudo docker info >/dev/null 2>&1; then
      echo "Docker 已就绪."
      return 0
    fi
  fi
  echo "错误: Docker 不可用。请先安装并启动 docker 后再执行。" >&2
  exit 1
}

resolve_compose_cmd() {
  if run_sudo docker compose version >/dev/null 2>&1; then
    echo "docker compose"
    return
  fi
  if command -v docker-compose >/dev/null 2>&1; then
    echo "docker-compose"
    return
  fi
  if [ -x /usr/local/bin/docker-compose ]; then
    echo "/usr/local/bin/docker-compose"
    return
  fi
  echo ""
}

COMPOSE_CMD=""
compose_cmd() {
  # stack_down 可能早于 stack_up，先补默认值，避免 set -u 静默退出
  export_defaults
  : "${AIJZ_WEB_PUBLISH:=127.0.0.1:5173}"
  : "${AIJZ_GATEWAY_PUBLISH:=127.0.0.1:8180}"
  : "${AIJZ_PLATFORM_PUBLISH:=127.0.0.1:8888}"
  : "${AIJZ_AI_PUBLISH:=127.0.0.1:8001}"
  : "${AIJZ_PG_PUBLISH:=127.0.0.1:5432}"
  if [ -z "$COMPOSE_CMD" ]; then
    COMPOSE_CMD="$(resolve_compose_cmd)"
  fi
  if [ -z "$COMPOSE_CMD" ]; then
    echo "错误: 找不到 docker compose / docker-compose" >&2
    exit 1
  fi
  # shellcheck disable=SC2086
  run_sudo env \
    DOCKER_BASE_REGISTRY="${DOCKER_BASE_REGISTRY}" \
    GOPROXY="${GOPROXY}" \
    AIJZ_WEB_PUBLISH="${AIJZ_WEB_PUBLISH}" \
    AIJZ_GATEWAY_PUBLISH="${AIJZ_GATEWAY_PUBLISH}" \
    AIJZ_PLATFORM_PUBLISH="${AIJZ_PLATFORM_PUBLISH}" \
    AIJZ_AI_PUBLISH="${AIJZ_AI_PUBLISH}" \
    AIJZ_PG_PUBLISH="${AIJZ_PG_PUBLISH}" \
    $COMPOSE_CMD "$@"
}

export_defaults() {
  export DOCKER_BASE_REGISTRY="${DOCKER_BASE_REGISTRY:-docker.m.daocloud.io/library}"
  export GOPROXY="${GOPROXY:-https://goproxy.cn,direct}"
  export REGISTRY_MIRROR="${REGISTRY_MIRROR:-docker.m.daocloud.io/library/}"
}

# 从 "127.0.0.1:5173" 或 "5173" 取出宿主机端口号
publish_host_port() {
  local pub="$1"
  echo "$pub" | awk -F: '{print $NF}'
}

port_in_use() {
  local port="$1"
  if command -v ss >/dev/null 2>&1; then
    run_sudo ss -tlnH "sport = :$port" 2>/dev/null | grep -q . && return 0
    ss -tlnH "sport = :$port" 2>/dev/null | grep -q . && return 0
    return 1
  fi
  if command -v lsof >/dev/null 2>&1; then
    lsof -iTCP:"$port" -sTCP:LISTEN >/dev/null 2>&1 && return 0
    return 1
  fi
  return 1
}

# 检查 AI 建站将占用的宿主机端口；若已被本项目容器占用则放行
check_aijz_ports() {
  local pairs=(
    "web:$AIJZ_WEB_PUBLISH"
    "gateway:$AIJZ_GATEWAY_PUBLISH"
    "platform:$AIJZ_PLATFORM_PUBLISH"
    "ai:$AIJZ_AI_PUBLISH"
    "postgres:$AIJZ_PG_PUBLISH"
  )
  echo "端口规划核对（相对 yh_web）：AI建站 5173/8180/8888/8001/5432；宇恒 8088/9080/9081 + 宿主机 80/443。"
  local name pub port conflict=0
  local self_running=0
  if compose_cmd ps -q 2>/dev/null | grep -q .; then
    self_running=1
  fi
  for item in "${pairs[@]}"; do
    name="${item%%:*}"
    pub="${item#*:}"
    port="$(publish_host_port "$pub")"
    [ -z "$port" ] && continue
    if port_in_use "$port"; then
      if [ "$self_running" -eq 1 ]; then
        echo "  提示: 端口 $port（$name）已占用，疑为本栈，继续。"
      else
        echo "  错误: 宿主机端口 $port 已被占用（服务 $name ← $pub）。" >&2
        echo "         请改 .env 中对应 AIJZ_*_PUBLISH，或停掉占用进程。" >&2
        conflict=1
      fi
    else
      echo "  OK  $name → $pub"
    fi
  done
  [ "$conflict" -eq 0 ] || exit 1
}

# 构建 web/dist：优先本机 npm；否则用 Docker node 镜像
build_web_dist() {
  echo "构建 web dist ..."
  if command -v npm >/dev/null 2>&1; then
    (
      cd "$ROOT/web"
      if [ -f package-lock.json ]; then
        npm ci --legacy-peer-deps 2>/dev/null || npm install --legacy-peer-deps
      else
        npm install --legacy-peer-deps
      fi
      npm run build
    )
  else
    echo "本机无 npm，使用 Docker node 构建..."
    run_sudo docker run --rm \
      -v "$ROOT/web:/app" \
      -v "$ROOT/web/dist:/app/dist" \
      -w /app \
      "${REGISTRY_MIRROR}node:20-alpine" \
      sh -c "(npm ci --legacy-peer-deps 2>/dev/null || npm install --legacy-peer-deps) && npm run build"
  fi
  if [ ! -f "$ROOT/web/dist/index.html" ]; then
    echo "错误: web/dist/index.html 不存在，构建失败。" >&2
    exit 1
  fi
  # nginx 容器用户需可读；避免 Podman 下 Permission denied → 403
  chmod -R a+rX "$ROOT/web/dist" 2>/dev/null || true
  echo "web/dist 就绪."
}

stack_up() {
  local rebuild="${1:-0}"
  export_defaults
  ensure_runtime_dirs
  ensure_env_file
  apply_public_base_url
  check_aijz_ports
  build_web_dist
  if [ "$rebuild" = "1" ]; then
    compose_cmd up -d --build --force-recreate
  else
    if ! compose_cmd up -d; then
      echo "compose up 失败，尝试 --build ..."
      compose_cmd up -d --build
    fi
  fi
  # Podman DNS 竞态：web 可能因启动时解析不到 gateway 而退出，gateway 就绪后再拉起一次
  sleep 2
  compose_cmd up -d --force-recreate web >/dev/null 2>&1 || compose_cmd up -d web || true
  install_host_nginx_site_conf || true
}

stack_down() {
  # 不吞掉 unbound 等错误；compose down 失败仍继续
  if ! compose_cmd down --remove-orphans; then
    echo "提示: compose down 未完全成功，继续启动..." >&2
  fi
}

healthcheck() {
  local ok=0 i
  local gw_port web_port
  gw_port="$(publish_host_port "${AIJZ_GATEWAY_PUBLISH:-127.0.0.1:8180}")"
  web_port="$(publish_host_port "${AIJZ_WEB_PUBLISH:-127.0.0.1:5173}")"
  echo "等待 gateway(:$gw_port) / web(:$web_port) 及 web→gateway 代理 ..."
  for i in $(seq 1 60); do
    if curl -fsS --max-time 2 "http://127.0.0.1:${gw_port}/gateway/health" >/dev/null 2>&1 \
      && curl -fsS --max-time 2 "http://127.0.0.1:${web_port}/" >/dev/null 2>&1 \
      && curl -fsS --max-time 2 "http://127.0.0.1:${web_port}/gateway/health" >/dev/null 2>&1; then
      ok=1
      break
    fi
    # 中途补拉 web（常见：nginx 启动时 DNS 未就绪已退出）
    if [ $((i % 10)) -eq 0 ]; then
      compose_cmd up -d --force-recreate web >/dev/null 2>&1 || compose_cmd up -d web >/dev/null 2>&1 || true
    fi
    sleep 2
  done
  if [ "$ok" -eq 1 ]; then
    echo "健康检查通过."
  else
    echo "警告: 健康检查超时（含 web 反代 /gateway/health），请查看: docker compose -f $ROOT/docker-compose.yml logs web gateway" >&2
  fi
  compose_cmd exec -T postgres psql -U platform -d platform -c 'ALTER USER platform CREATEDB;' >/dev/null 2>&1 || true
}

print_endpoints() {
  local pub_line=""
  if [ -n "${AIJZ_DOMAIN:-}" ] && [ "${AIJZ_ENABLE_HOST_NGINX:-0}" = "1" ]; then
    pub_line="  Public   https://${AIJZ_DOMAIN}"
  fi
  cat <<EOF

========================================
  Web      http://127.0.0.1:$(publish_host_port "${AIJZ_WEB_PUBLISH:-127.0.0.1:5173}")
  Gateway  http://127.0.0.1:$(publish_host_port "${AIJZ_GATEWAY_PUBLISH:-127.0.0.1:8180}")
${pub_line}
  Account  demo / demo123
  Data     $ROOT/.runtime
  Stop     ./stop.sh
  重载配置 ./reload-config.sh（不整栈 rebuild）
  绑定     默认 127.0.0.1（与 yh_web 同机不抢 80/443/8088）
========================================
EOF
}

# 默认代码源（可用 AIJZ_GIT_URL / GIT_BRANCH 覆盖）
AIJZ_GIT_URL_DEFAULT="https://gitea.yuxindazhineng.com/whm/ai_site.git"

ensure_git_origin() {
  local url="${1:-${AIJZ_GIT_URL:-$AIJZ_GIT_URL_DEFAULT}}"
  if ! command -v git >/dev/null 2>&1; then
    echo "错误: 未安装 git，无法拉取代码。请先安装 git。" >&2
    return 1
  fi
  if ! git rev-parse --is-inside-work-tree >/dev/null 2>&1; then
    echo "当前目录不是 git 仓库，初始化并关联 origin -> $url"
    git init
    git remote add origin "$url"
    return 0
  fi
  if git remote get-url origin >/dev/null 2>&1; then
    local cur
    cur="$(git remote get-url origin)"
    if [ "$cur" != "$url" ]; then
      echo "更新 origin: $cur -> $url"
      git remote set-url origin "$url"
    fi
  else
    echo "添加 origin -> $url"
    git remote add origin "$url"
  fi
}

# 从 Gitea ai_site 拉取并与远程对齐（默认硬重置，保留未跟踪文件如 .env）
# 环境变量：
#   AIJZ_GIT_URL     仓库地址（默认 https://gitea.yuxindazhineng.com/whm/ai_site.git）
#   GIT_BRANCH       分支（默认 main）
#   FORCE_GIT_RESET  1=reset --hard 对齐远程（默认 1）；0=仅快进合并
git_pull_hard() {
  local url="${AIJZ_GIT_URL:-$AIJZ_GIT_URL_DEFAULT}"
  local branch="${GIT_BRANCH:-main}"
  echo "拉取代码（$url  branch=$branch）..."
  ensure_git_origin "$url" || return 1
  if ! git fetch origin --progress; then
    echo "错误: git fetch 失败。私有仓库请配置凭据，或设置 AIJZ_GIT_URL 为带 token 的地址。" >&2
    return 1
  fi
  if ! git rev-parse --verify "origin/$branch" >/dev/null 2>&1; then
    echo "错误: 远程不存在分支 origin/$branch" >&2
    return 1
  fi
  if [ "${FORCE_GIT_RESET:-1}" = "1" ]; then
    # -f：丢弃已跟踪文件的本地改动，避免 checkout 被挡住（.env 等未跟踪文件保留）
    git checkout -f -B "$branch" "origin/$branch"
    git reset --hard "origin/$branch"
  else
    git checkout "$branch" 2>/dev/null || git checkout -b "$branch" "origin/$branch"
    git merge --ff-only "origin/$branch"
  fi
  echo "当前: $(git rev-parse --short HEAD) $(git log -1 --pretty=%s)"
}
