#!/usr/bin/env bash
# 内网生产：只换 api（presurvey-api:v1.0.4）—— 绝不碰 web/db
# ─────────────────────────────────────────────────────────────────────────────
# 在【内网生产服务器】上、以 root 跑：
#   su -c 'bash server-deploy-api-v1.0.4.sh'        # 或 sudo bash server-deploy-api-v1.0.4.sh
# 默认读当前目录的 tar，也可传路径：
#   bash server-deploy-api-v1.0.4.sh /path/to/presurvey-api-v1.0.4-20260604-002022-arm64.tar
#
# 硬约束（已写进逻辑，请勿改动）：
#   - 本脚本只操作 presurvey-api 容器：stop / rm / run 仅作用于 api。
#   - 绝不出现 web/db 容器的 stop/rm/run，绝不 truncate / docker compose down -v / 跑 init.sql。
#   - Docker 18.09 兼容：只用 docker load / inspect / run / ps，不用 compose、不用 --platform。
#   - 删除老 api 前先探测并沿用 network、端口、DB_* 等连接环境变量，以及 /app/geo_data 挂载。
#   - api 默认只在 Docker 网络内给 web 访问；只有老容器原本暴露宿主端口时才继续 -p。
# ─────────────────────────────────────────────────────────────────────────────
set -euo pipefail

TAR=${1:-./presurvey-api-v1.0.4-20260604-002022-arm64.tar}
IMAGE=presurvey-api:v1.0.4
CTN=presurvey-api

# 探测失败时的回落默认；若现网容器存在，会优先沿用现网探测结果
DEFAULT_NET=presurvey-net

ENV_FILE=$(mktemp /tmp/presurvey-api-env.XXXXXX)
trap 'rm -f "$ENV_FILE"' EXIT

echo "== 0. 检查 tar 文件存在 =="
[ -f "$TAR" ] || { echo "!! tar 不存在：$TAR"; echo "   先拉取：curl -L -O https://img.mangosv5.app/presurvey-api-v1.0.4-20260604-002022-arm64.tar"; exit 1; }
echo "   tar = $TAR"

echo "== 1. 探测现网老 $CTN 的 network / 端口 / DB环境变量 / geo_data挂载（删除前，删了就探不到了）=="
NET=""
HOSTPORT=""
GEO_MOUNT=""
if docker inspect "$CTN" >/dev/null 2>&1; then
    NET=$(docker inspect -f '{{.HostConfig.NetworkMode}}' "$CTN" 2>/dev/null || true)
    HOSTPORT=$(docker inspect -f '{{(index (index .NetworkSettings.Ports "8000/tcp") 0).HostPort}}' "$CTN" 2>/dev/null || true)
    docker inspect -f '{{range .Config.Env}}{{println .}}{{end}}' "$CTN" \
        | grep -E '^(DB_|DATABASE_|POSTGRES_|PRESURVEY_)' > "$ENV_FILE" || true
    GEO_MOUNT=$(docker inspect -f '{{range .Mounts}}{{if eq .Destination "/app/geo_data"}}{{.Source}}:{{.Destination}}:{{.Mode}}{{end}}{{end}}' "$CTN" 2>/dev/null || true)
    echo "   探测到：network=$NET  hostport=$HOSTPORT"
    if [ -s "$ENV_FILE" ]; then
        echo "   探测到 DB/应用环境变量："
        sed 's/=.*/=<hidden>/' "$ENV_FILE" | sed 's/^/     /'
    else
        echo "   ⚠️  未探测到 DB_* / DATABASE_* / POSTGRES_* / PRESURVEY_* 环境变量。"
    fi
    if [ -n "$GEO_MOUNT" ]; then
        echo "   探测到 geo_data 挂载：$GEO_MOUNT"
    else
        echo "   未探测到 /app/geo_data 挂载；将不额外挂载。"
    fi
else
    echo "   ⚠️  老 $CTN 容器不存在，无法探测。会使用回落默认，人工务必核对 DB 连接。"
fi

if [ -z "$NET" ] || [ "$NET" = "default" ] || [ "$NET" = "null" ]; then
    echo "   ⚠️  network 探测无效（值=[$NET]），回落默认：$DEFAULT_NET"
    NET=$DEFAULT_NET
fi
if [ -z "$HOSTPORT" ] || [ "$HOSTPORT" = "null" ]; then
    HOSTPORT=""
    echo "   未探测到宿主端口映射；将保持纯 Docker 网络内访问，不强制 -p，避免占用生产宿主 8000。"
fi
if [ -n "$HOSTPORT" ]; then
    echo "   ▶ 最终采用：network=$NET  端口映射=${HOSTPORT}:8000"
else
    echo "   ▶ 最终采用：network=$NET  端口映射=<none>，web 通过 network-alias api 访问"
fi

echo "== 2. docker load 新镜像 =="
docker load -i "$TAR"

echo "== 3. 校验 $IMAGE 已 load =="
docker images | grep presurvey-api | grep -q "1.0.4" \
    || { echo "!! 未找到 presurvey-api v1.0.4 镜像，load 失败，停。"; docker images | grep presurvey-api || true; exit 1; }
echo "   OK，镜像就绪。"

echo "== 4. 停删老 api（幂等，只动 api，不碰 web/db）=="
docker stop "$CTN" 2>/dev/null || true
docker rm   "$CTN" 2>/dev/null || true

echo "== 5. 起新 api（Docker 18.09 docker run，沿用探测到的 network/端口/环境/挂载）=="
# web/nginx.conf 反代到 http://api:8000，生产依赖 api 这个 Docker 网络别名。
# api 健康验证也走 Docker 网络内的 api:8000，因此没有老宿主端口映射时不加 -p，避免端口冲突导致换容器失败。
RUN_ARGS=(-d --name "$CTN" --network "$NET" --network-alias api)
if [ -n "$HOSTPORT" ]; then
    RUN_ARGS+=(-p "${HOSTPORT}:8000")
fi
if [ -s "$ENV_FILE" ]; then
    RUN_ARGS+=(--env-file "$ENV_FILE")
fi
if [ -n "$GEO_MOUNT" ] && [ "$GEO_MOUNT" != "::" ]; then
    RUN_ARGS+=(-v "$GEO_MOUNT")
fi

if ! docker run "${RUN_ARGS[@]}" "$IMAGE"; then
    echo "!! docker run 失败。供人工核对的探测值："
    echo "     network   = $NET"
    echo "     端口      = ${HOSTPORT:+${HOSTPORT}:8000}${HOSTPORT:-<none>}"
    echo "     alias     = api"
    echo "     geo_data  = ${GEO_MOUNT:-<none>}"
    echo "     env file  = $ENV_FILE（脚本退出后会删除；请重跑脚本重新探测）"
    echo "     镜像      = $IMAGE"
    echo "   排查：端口被占用？network 不存在？DB_* 是否缺失？docker network ls / docker ps -a / docker logs $CTN 看一下。"
    echo "   兜底：若 api 日志出现 \"can't start new thread\" 或 getaddrinfo 相关错误，重跑时追加启动参数覆盖 CMD：uvicorn main:app --host 0.0.0.0 --port 8000 --loop asyncio"
    exit 1
fi

echo "== 6. 验证 =="
sleep 3
if docker ps | grep -q "$CTN"; then
    echo "   容器运行中：$(docker ps --filter name=$CTN --format '{{.Names}}  {{.Status}}  {{.Ports}}')"
else
    echo "!! 容器未在运行，看日志：docker logs $CTN"
    docker ps -a | grep "$CTN" || true
    docker logs "$CTN" 2>/dev/null || true
    exit 1
fi

HEALTH_CHECKER=""
if docker images | grep -E '^curlimages/curl[[:space:]]' >/dev/null 2>&1; then
    HEALTH_CHECKER="curlimages/curl"
elif docker images | grep -E '^busybox[[:space:]]' >/dev/null 2>&1; then
    HEALTH_CHECKER="busybox"
fi

if [ "$HEALTH_CHECKER" = "curlimages/curl" ]; then
    docker run --rm --network "$NET" curlimages/curl -s -w '\n%{http_code}' http://api:8000/health > /tmp/presurvey-api-health.raw 2>/tmp/presurvey-api-health.err \
        && CODE=$(tail -n 1 /tmp/presurvey-api-health.raw) || CODE=000
    sed '$d' /tmp/presurvey-api-health.raw > /tmp/presurvey-api-health.out 2>/dev/null || true
elif [ "$HEALTH_CHECKER" = "busybox" ]; then
    docker run --rm --network "$NET" busybox wget -qO- http://api:8000/health > /tmp/presurvey-api-health.out 2>/tmp/presurvey-api-health.err \
        && CODE=200 || CODE=000
else
    CODE=$(docker exec "$CTN" python -c 'import urllib.request; r=urllib.request.urlopen("http://api:8000/health", timeout=5); print(r.status); print(r.read().decode())' > /tmp/presurvey-api-health.pyout 2>/tmp/presurvey-api-health.err \
        && sed -n '1p' /tmp/presurvey-api-health.pyout || echo "000")
    sed -n '2,$p' /tmp/presurvey-api-health.pyout > /tmp/presurvey-api-health.out 2>/dev/null || true
fi
echo "   Docker 网络内访问 http://api:8000/health  ->  HTTP $CODE"
cat /tmp/presurvey-api-health.out 2>/dev/null || true
echo ""
case "$CODE" in
    200)
        if [ -n "$HOSTPORT" ]; then
            echo "✅ api 已换到 v1.0.4 并就绪（network=$NET  alias=api  端口=${HOSTPORT}:8000）。web/db 未触碰。"
        else
            echo "✅ api 已换到 v1.0.4 并就绪（network=$NET  alias=api  无宿主端口映射）。web/db 未触碰。"
        fi
        ;;
    *)
        echo "⚠️  HTTP $CODE 非预期。看日志：docker logs $CTN"
        echo "   老容器探测值供人工核对：network=$NET  端口=${HOSTPORT:+${HOSTPORT}:8000}${HOSTPORT:-<none>}  geo_data=${GEO_MOUNT:-<none>}"
        echo "   兜底：若 api 日志出现 \"can't start new thread\" 或 getaddrinfo 相关错误，重跑时追加启动参数覆盖 CMD：uvicorn main:app --host 0.0.0.0 --port 8000 --loop asyncio"
        exit 1
        ;;
esac
