#!/usr/bin/env bash
#
# 构建离线 Docker 交付包：包含应用镜像、pgvector(PostgreSQL)、Redis 及统一的 docker-compose
# 使用方式：
#   chmod +x build-docker-bundle.sh
#   ./build-docker-bundle.sh                # 使用默认版本号（时间戳）
#   VERSION=v1.2.3 ./build-docker-bundle.sh # 指定版本号

set -euo pipefail

ROOT_DIR="$(cd "$(dirname "$0")" && pwd)"
TIMESTAMP="$(date +%Y%m%d_%H%M%S)"
APP_NAME="gdyd-zhpb-zgf"
VERSION="${VERSION:-${TIMESTAMP}}"
OUTPUT_DIR="${ROOT_DIR}/docker-bundle-${VERSION}"
IMAGES_DIR="${OUTPUT_DIR}/images"
CONFIG_DIR="${OUTPUT_DIR}/config"
INIT_DIR="${OUTPUT_DIR}/pgvector-init"

APP_IMAGE="${APP_NAME}-app:${VERSION}"
PGVECTOR_IMAGE="${PGVECTOR_IMAGE:-pgvector/pgvector:pg16-trixie}"
REDIS_IMAGE="${REDIS_IMAGE:-redis:7-alpine}"

# 默认账号密码，可在执行脚本前通过环境变量覆盖
POSTGRES_DB="${POSTGRES_DB:-vector_db}"
POSTGRES_USER="${POSTGRES_USER:-vectoruser}"
POSTGRES_PASSWORD="${POSTGRES_PASSWORD:-vectorpass}"
POSTGRES_PORT="${POSTGRES_PORT:-5433}"

REDIS_PASSWORD="${REDIS_PASSWORD:-TNh8jiy8WpEweGLZ}"
REDIS_PORT="${REDIS_PORT:-26739}"

APP_PORT="${APP_PORT:-8000}"

print_step() {
  printf '\n\033[1;34m==> %s\033[0m\n' "$1"
}

print_info() {
  printf '   %s\n' "$1"
}

abort() {
  printf '\033[1;31m[错误]\033[0m %s\n' "$1" >&2
  exit 1
}

check_command() {
  command -v "$1" >/dev/null 2>&1 || abort "未找到命令：$1，请先安装。"
}

APT_UPDATED=0
SUDO_CMD=""
if command -v sudo >/dev/null 2>&1 && [[ ${EUID:-0} -ne 0 ]]; then
  SUDO_CMD="sudo"
fi

update_apt_once() {
  if [[ ${APT_UPDATED} -eq 0 ]]; then
    print_step "更新 apt 软件源"
    ${SUDO_CMD} env DEBIAN_FRONTEND=noninteractive apt-get update -y
    APT_UPDATED=1
  fi
}

install_with_apt() {
  local package="$1"
  local friendly="$2"
  if ! command -v apt-get >/dev/null 2>&1; then
    abort "缺少 ${friendly}，但当前环境不支持自动安装（未检测到 apt-get）。"
  fi
  update_apt_once
  print_step "安装 ${friendly}"
  ${SUDO_CMD} env DEBIAN_FRONTEND=noninteractive apt-get install -y "${package}"
}

ensure_maven() {
  if ! command -v mvn >/dev/null 2>&1; then
    install_with_apt "maven" "Maven"
  fi
}

ensure_java21() {
  local major=""
  if command -v java >/dev/null 2>&1; then
    local version_line
    version_line="$(java -version 2>&1 | head -n 1 | cut -d'"' -f2)"
    major="${version_line%%.*}"
    if [[ "${major}" == "1" ]]; then
      major="$(echo "${version_line}" | cut -d'.' -f2)"
    fi
    if [[ -n "${major}" && "${major}" -ge 21 ]]; then
      return
    fi
    print_info "检测到 Java 版本 ${major:-unknown}，低于 21，准备自动安装 OpenJDK 21。"
  else
    print_info "未检测到 Java，准备自动安装 OpenJDK 21。"
  fi
  install_with_apt "openjdk-21-jdk" "OpenJDK 21"
}

ensure_clean_dir() {
  if [[ -d "$1" ]]; then
    rm -rf "$1"
  fi
  mkdir -p "$1"
}

ensure_java21
ensure_maven
check_command docker

print_step "编译 Spring Boot 应用"
mvn -q -U -DskipTests clean package

JAR_FILE="$(find "${ROOT_DIR}/target" -maxdepth 1 -type f -name "*.jar" ! -name "*-sources.jar" ! -name "*-javadoc.jar" | head -n 1)"
[[ -n "${JAR_FILE}" ]] || abort "未找到可用的 JAR 文件，请检查 Maven 构建结果。"
print_info "找到 JAR：${JAR_FILE}"

print_step "构建应用镜像 ${APP_IMAGE}"
docker build \
  --build-arg VERSION="${VERSION}" \
  --build-arg BUILD_TIME="$(date -u +"%Y-%m-%dT%H:%M:%SZ")" \
  -t "${APP_IMAGE}" \
  "${ROOT_DIR}"

print_step "拉取依赖镜像"
docker pull "${PGVECTOR_IMAGE}"
docker pull "${REDIS_IMAGE}"

print_step "生成离线交付包目录 ${OUTPUT_DIR}"
ensure_clean_dir "${OUTPUT_DIR}"
mkdir -p "${IMAGES_DIR}" "${CONFIG_DIR}" "${INIT_DIR}"

if [[ -f "${ROOT_DIR}/src/main/resources/application.yml" ]]; then
  cp "${ROOT_DIR}/src/main/resources/application.yml" "${CONFIG_DIR}/application.yml"
  print_info "已复制默认 application.yml 到 ${CONFIG_DIR}/application.yml"
else
  print_info "未找到 src/main/resources/application.yml，可手动放置到 ${CONFIG_DIR}/application.yml"
fi

cat > "${OUTPUT_DIR}/.env" <<EOF
# PostgreSQL (pgvector)
POSTGRES_DB=${POSTGRES_DB}
POSTGRES_USER=${POSTGRES_USER}
POSTGRES_PASSWORD=${POSTGRES_PASSWORD}
POSTGRES_PORT=${POSTGRES_PORT}

# Redis
REDIS_PASSWORD=${REDIS_PASSWORD}
REDIS_PORT=${REDIS_PORT}

# 应用
APP_PORT=${APP_PORT}
EOF

cat > "${INIT_DIR}/001-create-vector-extension.sql" <<'EOF'
CREATE EXTENSION IF NOT EXISTS vector;
EOF

cat > "${OUTPUT_DIR}/docker-compose.yml" <<EOF
version: "3.8"

services:
  pgvector:
    image: ${PGVECTOR_IMAGE}
    container_name: ${APP_NAME}-pgvector
    restart: unless-stopped
    environment:
      POSTGRES_DB: \${POSTGRES_DB}
      POSTGRES_USER: \${POSTGRES_USER}
      POSTGRES_PASSWORD: \${POSTGRES_PASSWORD}
      PGDATA: /var/lib/postgresql/data/pgdata
    ports:
      - "\${POSTGRES_PORT}:5432"
    volumes:
      - pgvector_data:/var/lib/postgresql/data
      - ./pgvector-init:/docker-entrypoint-initdb.d
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U \${POSTGRES_USER} -d \${POSTGRES_DB}"]
      interval: 20s
      timeout: 5s
      retries: 5

  redis:
    image: ${REDIS_IMAGE}
    container_name: ${APP_NAME}-redis
    restart: unless-stopped
    command: ["redis-server", "--appendonly", "yes", "--requirepass", "\${REDIS_PASSWORD}"]
    ports:
      - "\${REDIS_PORT}:6379"
    volumes:
      - redis_data:/data
    healthcheck:
      test: ["CMD", "redis-cli", "-a", "\${REDIS_PASSWORD}", "ping"]
      interval: 20s
      timeout: 5s
      retries: 5

  app:
    image: ${APP_IMAGE}
    container_name: ${APP_NAME}-app
    restart: unless-stopped
    depends_on:
      pgvector:
        condition: service_healthy
      redis:
        condition: service_healthy
    ports:
      - "\${APP_PORT}:8000"
    environment:
      SPRING_DATASOURCE_URL: jdbc:postgresql://pgvector:5432/\${POSTGRES_DB}
      SPRING_DATASOURCE_USERNAME: \${POSTGRES_USER}
      SPRING_DATASOURCE_PASSWORD: \${POSTGRES_PASSWORD}
      SPRING_DATA_REDIS_HOST: redis
      SPRING_DATA_REDIS_PORT: 6379
      SPRING_DATA_REDIS_PASSWORD: \${REDIS_PASSWORD}
      SERVER_PORT: 8000
    volumes:
      - ./config/application.yml:/app/config/application.yml:ro
      - app_uploads:/app/uploads
      - app_extracts:/app/extracts
      - app_logs:/app/logs

volumes:
  pgvector_data:
  redis_data:
  app_uploads:
  app_extracts:
  app_logs:
EOF

cat > "${OUTPUT_DIR}/README.md" <<EOF
# Docker 离线部署包（${APP_NAME} ${VERSION}）

## 包含内容
- 应用镜像：${APP_IMAGE}
- pgvector 数据库镜像：${PGVECTOR_IMAGE}
- Redis 缓存镜像：${REDIS_IMAGE}
- docker-compose.yml / .env / config / pgvector-init 目录

## 使用步骤
1. 将整个目录拷贝到目标服务器并进入该目录：
   \`\`\`
   cd $(basename "${OUTPUT_DIR}")
   \`\`\`
2. 导入离线镜像：
   \`\`\`
   docker load -i images/app-${VERSION}.tar
   docker load -i images/pgvector.tar
   docker load -i images/redis.tar
   \`\`\`
3. 根据需要修改 \`.env\`、\`config/application.yml\`。
4. 启动：
   \`\`\`
   docker compose up -d
   \`\`\`
5. 查看状态：
   \`\`\`
   docker compose ps
   docker compose logs -f app
   \`\`\`

缺省账号密码可通过执行脚本前设置环境变量覆盖，如：
\`\`\`
POSTGRES_PASSWORD=StrongPass REDIS_PASSWORD=Secret ./build-docker-bundle.sh
\`\`\`
EOF

print_step "保存离线镜像到 ${IMAGES_DIR}"
docker save "${APP_IMAGE}" -o "${IMAGES_DIR}/app-${VERSION}.tar"
docker save "${PGVECTOR_IMAGE}" -o "${IMAGES_DIR}/pgvector.tar"
docker save "${REDIS_IMAGE}" -o "${IMAGES_DIR}/redis.tar"

print_step "打包完成"
print_info "输出目录：${OUTPUT_DIR}"
print_info "镜像文件：${IMAGES_DIR}"
print_info "可根据需要编辑 ${OUTPUT_DIR}/.env 与 ${CONFIG_DIR}/application.yml 后，执行 docker compose up -d 启动。"

