#!/usr/bin/env bash
# 构建 api / player / admin 生产镜像并导出为 tar
# 用法:
#   ./docs/docker/build-and-export-images.sh
#   ./docs/docker/build-and-export-images.sh --service admin
#   ./docs/docker/build-and-export-images.sh --tag v1.2.3
#   ./build-and-export-images.sh --use-cache
#   ./build-and-export-images.sh --export-only

set -euo pipefail

SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)"
cd "$ROOT"

COMPOSE_FILE="docker-compose.prod.yml"
ENV_FILE=".env.docker"
OUTPUT=""
SERVICE="all"
NO_CACHE=1
EXPORT_ONLY=0
TAG="${IMAGE_TAG:-}"

default_tag() { echo "latest"; }

default_output() {
  local svc="$1" image_tag="$2"
  if [[ "$svc" == "all" ]]; then
    echo "thebet365-images-${image_tag}.tar"
  else
    echo "thebet365-${svc}-${image_tag}.tar"
  fi
}

validate_tag() {
  printf '%s' "$1" | grep -Eq '^[A-Za-z0-9_][A-Za-z0-9_.-]{0,127}$' ||
    { echo "错误: 镜像 tag 不合法: $1" >&2; exit 1; }
}

validate_service() {
  case "$1" in
    all|api|player|admin) ;;
    *) echo "错误: --service 无效: $1（可选 api / player / admin / all）" >&2; exit 1 ;;
  esac
}

usage() {
  cat <<'EOF'
用法: docs/docker/build-and-export-images.sh [选项]

选项:
  --service SVC    api | player | admin | all（默认 all）
  --tag TAG        镜像 tag（默认 latest）
  --use-cache      构建时使用 Docker 缓存（默认 --no-cache）
  --export-only    跳过构建，仅导出已有指定 tag 镜像
  --output PATH    导出文件路径
  -h, --help       显示帮助

示例:
  ./docs/docker/build-and-export-images.sh --service admin
  ./docs/docker/build-and-export-images.sh --service admin --export-only
EOF
}

while [[ $# -gt 0 ]]; do
  case "$1" in
    --tag)
      TAG="${2:?缺少 --tag 参数值}"
      shift 2
      ;;
    --service)
      SERVICE="${2:?缺少 --service 参数值}"
      shift 2
      ;;
    --use-cache) NO_CACHE=0; shift ;;
    --export-only) EXPORT_ONLY=1; shift ;;
    --output)
      OUTPUT="${2:?缺少 --output 参数值}"
      shift 2
      ;;
    -h|--help) usage; exit 0 ;;
    *) echo "未知参数: $1" >&2; usage; exit 1 ;;
  esac
done

if [[ ! -f "$COMPOSE_FILE" ]]; then
  echo "错误: 未找到 ${COMPOSE_FILE}（目录: ${ROOT}）" >&2
  exit 1
fi

if [[ ! -f "$ENV_FILE" ]]; then
  if [[ -f ".env.docker.example" ]]; then
    echo "警告: 未找到 ${ENV_FILE}，将使用 .env.docker.example（生产部署请复制为 .env.docker 并修改密钥）"
    ENV_FILE=".env.docker.example"
  else
    echo "错误: 未找到 ${ENV_FILE} 或 .env.docker.example" >&2
    exit 1
  fi
fi

validate_service "$SERVICE"
TAG="${TAG:-$(default_tag)}"
validate_tag "$TAG"
OUTPUT="${OUTPUT:-$(default_output "$SERVICE" "$TAG")}"

if [[ "$SERVICE" == "all" ]]; then
  BUILD_SERVICES=(api player admin)
else
  BUILD_SERVICES=("$SERVICE")
fi

if [[ "$EXPORT_ONLY" -eq 0 ]]; then
  echo "==> 构建镜像: ${BUILD_SERVICES[*]} (tag: $TAG)"
  BUILD_ARGS=(compose -f "$COMPOSE_FILE" --env-file "$ENV_FILE" build)
  if [[ "$NO_CACHE" -eq 1 ]]; then
    BUILD_ARGS+=(--no-cache)
  fi
  BUILD_ARGS+=("${BUILD_SERVICES[@]}")
  IMAGE_TAG="$TAG" docker "${BUILD_ARGS[@]}"
fi

OUTPUT_PATH="$OUTPUT"
if [[ "$OUTPUT" != /* ]] && [[ "$OUTPUT" != [A-Za-z]:* ]]; then
  OUTPUT_PATH="$ROOT/$OUTPUT"
fi

SAVE_ARGS=()
for svc in "${BUILD_SERVICES[@]}"; do
  SAVE_ARGS+=("thebet365-${svc}:$TAG")
done

echo "==> 导出镜像 ($SERVICE) -> $OUTPUT_PATH"
docker save "${SAVE_ARGS[@]}" -o "$OUTPUT_PATH"

MANIFEST_PATH="${OUTPUT_PATH%.tar}.manifest.txt"
BUILT_AT="$(date -u +%Y-%m-%dT%H:%M:%SZ)"
GIT_COMMIT="unknown"
GIT_DIRTY="unknown"
if command -v git >/dev/null 2>&1 && git rev-parse HEAD >/dev/null 2>&1; then
  GIT_COMMIT="$(git rev-parse HEAD)"
  if git diff --quiet && git diff --cached --quiet; then
    GIT_DIRTY="false"
  else
    GIT_DIRTY="true"
  fi
fi

CHECKSUM="unavailable"
if command -v sha256sum >/dev/null 2>&1; then
  CHECKSUM="$(sha256sum "$OUTPUT_PATH" | awk '{print $1}')"
elif command -v shasum >/dev/null 2>&1; then
  CHECKSUM="$(shasum -a 256 "$OUTPUT_PATH" | awk '{print $1}')"
fi

{
  echo "tag=$TAG"
  echo "service=$SERVICE"
  echo "built_at=$BUILT_AT"
  echo "git_commit=$GIT_COMMIT"
  echo "git_dirty=$GIT_DIRTY"
  echo "tar=$(basename "$OUTPUT_PATH")"
  echo "tar_sha256=$CHECKSUM"
  for svc in "${BUILD_SERVICES[@]}"; do
    if [[ "$SERVICE" == "all" ]]; then
      echo "${svc}_image=thebet365-${svc}:$TAG"
      echo "${svc}_image_id=$(docker image inspect "thebet365-${svc}:$TAG" --format '{{.Id}}')"
    else
      echo "image=thebet365-${svc}:$TAG"
      echo "image_id=$(docker image inspect "thebet365-${svc}:$TAG" --format '{{.Id}}')"
    fi
  done
} > "$MANIFEST_PATH"

if command -v du >/dev/null 2>&1; then
  SIZE="$(du -h "$OUTPUT_PATH" | awk '{print $1}')"
  echo "完成: $OUTPUT_PATH ($SIZE)"
else
  echo "完成: $OUTPUT_PATH"
fi
echo "Manifest: $MANIFEST_PATH"

RECREATE="$SERVICE"
if [[ "$SERVICE" == "all" ]]; then
  RECREATE="api player admin"
fi

cat <<EOF

服务器导入并重建:
  docker load -i $(basename "$OUTPUT_PATH")
  docker compose -f docker-compose.prod.yml --env-file .env.docker up -d --force-recreate $RECREATE
EOF
