#!/usr/bin/env bash
# ============================================================
# phpshop 一键安装（在【客户机】上执行）
#
#   一条命令装完：
#     curl -fsSL https://dl.dev.glowcloud.cn/install.sh | bash
#
#   它会自动完成：环境检查 → 取最新版本 → 下载部署包（带断点续传/校验）
#                → 解压到安装目录 → 写入私有仓库配置 → 执行 deploy.sh
#
#   可调环境变量（加在 bash 前，写法见下）：
#     INSTALL_DIR=/opt/phpshop              安装目录（默认 /opt/phpshop）
#     PHPSHOP_VERSION=20260912               指定版本（默认 latest）
#     ADMIN_PORT=18081  USER_PORT=18082      对外端口
#     SITE_DOMAIN=http://shop.example.com    前端访问域名（留空自动探测 IP）
#     DB_ROOT_PASSWORD=xxx                   数据库密码（留空则随机生成）
#     REGISTRY / REGISTRY_USER / REGISTRY_PASSWORD   私有仓库（已内置默认值）
#     PHPSHOP_SKIP_DEPLOY=1                  只下载解压，不执行部署
#     PHPSHOP_KEEP_TARBALL=1                 保留下载的压缩包
#
#   示例：
#     curl -fsSL https://dl.dev.glowcloud.cn/install.sh | INSTALL_DIR=/data/app bash
#     curl -fsSL https://dl.dev.glowcloud.cn/install.sh | bash -s -- --version 20260912
# ============================================================
set -euo pipefail

# ------------------------------------------------------------
# 默认配置
# ------------------------------------------------------------
BASE_URL="${PHPSHOP_BASE_URL:-https://dl.dev.glowcloud.cn}"
INSTALL_DIR="${INSTALL_DIR:-/opt/phpshop}"
VERSION="${PHPSHOP_VERSION:-latest}"
KEEP_TARBALL="${PHPSHOP_KEEP_TARBALL:-0}"
SKIP_DEPLOY="${PHPSHOP_SKIP_DEPLOY:-0}"

# 私有镜像仓库（客户机免构建、免公共镜像源，直接从源站拉取）
REGISTRY="${REGISTRY:-registry.dev.glowcloud.cn}"
REGISTRY_USER="${REGISTRY_USER:-phpshop}"
REGISTRY_PASSWORD="${REGISTRY_PASSWORD:-Glowcloud@2026}"
IMAGE_NAMESPACE="${IMAGE_NAMESPACE:-phpshop}"
IMAGE_TAG="${IMAGE_TAG:-latest}"

GREEN='\033[32m'; YELLOW='\033[33m'; RED='\033[31m'; BLUE='\033[36m'; NC='\033[0m'
info() { printf "${GREEN}[✓]${NC} %s\n" "$1"; }
warn() { printf "${YELLOW}[!]${NC} %s\n" "$1"; }
err()  { printf "${RED}[✗]${NC} %s\n" "$1" >&2; }
step() { printf "\n${GREEN}==>${NC} %s\n" "$1"; }
die()  { err "$1"; exit 1; }

# ------------------------------------------------------------
# 参数解析
# ------------------------------------------------------------
usage() {
  cat <<'USAGE'
phpshop 一键安装（客户机执行）

  curl -fsSL https://dl.dev.glowcloud.cn/install.sh | bash
  curl -fsSL https://dl.dev.glowcloud.cn/install.sh | bash -s -- [选项]

选项：
  --version <版本号>   指定版本（默认 latest，如 20260912）
  --dir <目录>         安装目录（默认 /opt/phpshop）
  --skip-deploy        只下载解压与配置，不执行部署
  --base-url <地址>    下载源地址（默认 https://dl.dev.glowcloud.cn）
  -h, --help           显示本帮助

常用环境变量（写在 bash 前面）：
  INSTALL_DIR            安装目录，默认 /opt/phpshop
  ADMIN_PORT/USER_PORT   对外端口，默认 18081 / 18082
  SITE_DOMAIN            前端访问域名，留空自动探测服务器 IP
  DB_ROOT_PASSWORD       数据库密码，留空则随机生成
  REGISTRY / REGISTRY_USER / REGISTRY_PASSWORD   私有镜像仓库（已内置默认值）
  PHPSHOP_KEEP_TARBALL=1 保留下载的压缩包

示例：
  curl -fsSL https://dl.dev.glowcloud.cn/install.sh | INSTALL_DIR=/data/app bash
  curl -fsSL https://dl.dev.glowcloud.cn/install.sh | bash -s -- --version 20260912
USAGE
  exit 0
}
while [ $# -gt 0 ]; do
  case "$1" in
    --version|-v) VERSION="${2:-}"; shift 2 ;;
    --dir|-d)     INSTALL_DIR="${2:-}"; shift 2 ;;
    --skip-deploy) SKIP_DEPLOY=1; shift ;;
    --base-url)   BASE_URL="${2:-}"; shift 2 ;;
    --help|-h)    usage ;;
    *) die "未知参数：$1（用 --help 查看用法）" ;;
  esac
done
[ -n "$VERSION" ] || die "--version 不能为空"

printf "\n${BLUE}phpshop 一键安装${NC}\n"
printf "  下载源：%s\n" "$BASE_URL"
printf "  版本：  %s\n" "$VERSION"
printf "  目录：  %s\n" "$INSTALL_DIR"

# ------------------------------------------------------------
# 0. 需要 root（或可免密使用 docker）；不是 root 时自动用 sudo 重跑
# ------------------------------------------------------------
if [ "$(id -u)" != "0" ]; then
  if ! docker info >/dev/null 2>&1; then
    if [ "${PHPSHOP_SUDO_REEXEC:-0}" = "1" ]; then
      die "已用 sudo 重试仍无法访问 Docker。请确认 docker 已启动：systemctl start docker"
    fi
    if ! command -v sudo >/dev/null 2>&1; then
      die "需要 root 权限（或能免 sudo 使用 docker）。请以 root 身份重新执行。"
    fi
    warn "当前非 root 且无法直接访问 Docker，自动切换 sudo 重新执行..."
    SELF="$(mktemp /tmp/phpshop-install.XXXXXX.sh)"
    if command -v curl >/dev/null 2>&1; then
      curl -fsSL "$BASE_URL/install.sh" -o "$SELF" || die "重新下载 install.sh 失败"
    else
      wget -qO "$SELF" "$BASE_URL/install.sh" || die "重新下载 install.sh 失败"
    fi
    chmod +x "$SELF"
    export PHPSHOP_SUDO_REEXEC=1
    REEXEC_ARGS=(--base-url "$BASE_URL" --version "$VERSION" --dir "$INSTALL_DIR")
    if [ "$SKIP_DEPLOY" = "1" ]; then
      REEXEC_ARGS+=(--skip-deploy)
    fi
    exec sudo -E bash "$SELF" "${REEXEC_ARGS[@]}"
  fi
fi

# ------------------------------------------------------------
step "1/6 检查运行环境"
# ------------------------------------------------------------
DOWNLOADER=""
if command -v curl >/dev/null 2>&1; then
  DOWNLOADER="curl"
elif command -v wget >/dev/null 2>&1; then
  DOWNLOADER="wget"
else
  die "缺少 curl 或 wget。Ubuntu/Debian: apt-get install -y curl ；CentOS: yum install -y curl"
fi
info "下载工具：${DOWNLOADER}"

command -v tar >/dev/null 2>&1 || die "缺少 tar 命令"
info "解压工具：tar"

if ! command -v docker >/dev/null 2>&1; then
  err "未检测到 Docker。请先安装后重试："
  echo "    curl -fsSL https://get.docker.com | sh && systemctl enable --now docker"
  exit 1
fi
if docker compose version >/dev/null 2>&1; then
  COMPOSE="docker compose"
elif command -v docker-compose >/dev/null 2>&1; then
  COMPOSE="docker-compose"
else
  die "未检测到 docker compose。请安装 docker-compose-plugin 后重试。"
fi
info "docker: $(docker --version 2>/dev/null | cut -d, -f1) / compose: $($COMPOSE version --short 2>/dev/null || echo ok)"

if command -v sha256sum >/dev/null 2>&1; then
  SHA_CMD="sha256sum"
elif command -v shasum >/dev/null 2>&1; then
  SHA_CMD="shasum -a 256"
else
  SHA_CMD=""
  warn "缺少 sha256 工具，将跳过大包校验"
fi

# ------------------------------------------------------------
step "2/6 获取版本信息"
# ------------------------------------------------------------
fetch_text() {  # $1=URL
  if [ "$DOWNLOADER" = "curl" ]; then
    curl -fsSL --max-time 30 "$1"
  else
    wget -qO- --timeout=30 "$1"
  fi
}

PKG_FILE=""
PKG_SHA=""
if [ "$VERSION" = "latest" ]; then
  MANIFEST="$(fetch_text "$BASE_URL/latest.txt" || true)"
  if [ -n "$MANIFEST" ]; then
    mget() { printf '%s\n' "$MANIFEST" | grep -E "^$1=" | head -1 | cut -d= -f2- | tr -d '\r'; }
    VERSION="$(mget version)"
    PKG_FILE="$(mget file)"
    PKG_SHA="$(mget sha256)"
    REL_DATE="$(mget date)"
  fi
  if [ -z "$PKG_FILE" ]; then
    warn "未取到版本清单，退回约定路径"
    PKG_FILE="phpshop-deploy-latest.tar.gz"
  fi
  info "最新版本：${VERSION:-unknown}${REL_DATE:+（${REL_DATE}）}"
else
  PKG_FILE="releases/phpshop-deploy-${VERSION}.tar.gz"
  PKG_SHA="$(fetch_text "${BASE_URL}/releases/phpshop-deploy-${VERSION}.tar.gz.sha256" 2>/dev/null | awk '{print $1}' || true)"
  info "指定版本：${VERSION}"
fi

PKG_URL="${BASE_URL}/${PKG_FILE}"
info "安装包：${PKG_URL}"

# ------------------------------------------------------------
step "3/6 下载部署包"
# ------------------------------------------------------------
mkdir -p "$INSTALL_DIR/.cache"
TARBALL="$INSTALL_DIR/.cache/$(basename "$PKG_FILE")"

# 已有完整包且校验通过则跳过下载
need_dl=1
if [ -f "$TARBALL" ] && [ -n "$PKG_SHA" ] && [ -n "$SHA_CMD" ]; then
  if [ "$($SHA_CMD "$TARBALL" 2>/dev/null | awk '{print $1}')" = "$PKG_SHA" ]; then
    info "已存在完整安装包，跳过下载"
    need_dl=0
  fi
fi

if [ "$need_dl" = "1" ]; then
  printf "    下载中（支持断点续传，约 600MB，视带宽需数分钟）...\n"
  if [ "$DOWNLOADER" = "curl" ]; then
    curl -fL -C - --retry 3 --retry-delay 3 --connect-timeout 20 \
         --progress-bar -o "$TARBALL" "$PKG_URL" || die "下载失败：$PKG_URL"
  else
    wget -c --tries=3 --timeout=30 -O "$TARBALL" "$PKG_URL" || die "下载失败：$PKG_URL"
  fi
  info "下载完成：$(du -h "$TARBALL" 2>/dev/null | cut -f1)"
else
  info "复用缓存：$(du -h "$TARBALL" 2>/dev/null | cut -f1)"
fi

# 校验
if [ -n "$PKG_SHA" ] && [ -n "$SHA_CMD" ]; then
  GOT_SHA="$($SHA_CMD "$TARBALL" | awk '{print $1}')"
  if [ "$GOT_SHA" != "$PKG_SHA" ]; then
    err "安装包校验不通过（可能下载不完整）"
    printf "    期望 %s\n    实际 %s\n" "$PKG_SHA" "$GOT_SHA"
    rm -f "$TARBALL"
    die "已删除损坏文件，请重新执行本脚本"
  fi
  info "SHA256 校验通过"
else
  warn "跳过 SHA256 校验"
fi

# ------------------------------------------------------------
step "4/6 解压到 ${INSTALL_DIR}"
# ------------------------------------------------------------
# 归档内顶层目录为 deploy/，strip 一层铺平
if ! tar -xzf "$TARBALL" -C "$INSTALL_DIR" --strip-components=1 \
      --exclude '._*' --exclude '*/._*' --exclude '.DS_Store' 2>/dev/null; then
  err "解压失败（归档结构可能不符预期）"
  die "请手动检查：tar -tzf $TARBALL | head"
fi
[ -f "$INSTALL_DIR/deploy.sh" ] || die "解压后缺少 deploy.sh，归档可能已损坏"
info "已解压：$(find "$INSTALL_DIR" -maxdepth 1 -type f | wc -l | tr -d ' ') 个脚本 + $(du -sh "$INSTALL_DIR/app" 2>/dev/null | cut -f1) 源码"

if [ "$KEEP_TARBALL" = "1" ]; then
  info "保留压缩包：$TARBALL"
else
  rm -f "$TARBALL"
fi

# ------------------------------------------------------------
step "5/6 写入配置（.env）"
# ------------------------------------------------------------
cd "$INSTALL_DIR"
if [ ! -f .env ]; then
  [ -f .env.example ] || die "缺少 .env.example，部署包不完整"
  cp .env.example .env
  info "已由模板生成 .env"
else
  info "沿用已有 .env（仅更新本次指定的项）"
fi

# 安全写 key=value（转义 & 与 | ）
setkv() {
  local key="$1" val="$2"
  local esc
  esc="$(printf '%s' "$val" | sed -e 's/[&|]/\\&/g')"
  if grep -qE "^${key}=" .env; then
    sed -i.bak "s|^${key}=.*|${key}=${esc}|" .env && rm -f .env.bak
  else
    printf '%s=%s\n' "$key" "$val" >> .env
  fi
}

# 私有仓库（必写）
setkv REGISTRY          "$REGISTRY"
setkv REGISTRY_USER     "$REGISTRY_USER"
setkv REGISTRY_PASSWORD "$REGISTRY_PASSWORD"
setkv IMAGE_NAMESPACE   "$IMAGE_NAMESPACE"
setkv IMAGE_TAG         "$IMAGE_TAG"

# 可选透传项：仅当调用方显式提供时才写入
for kv in "ADMIN_PORT:${ADMIN_PORT:-}" "USER_PORT:${USER_PORT:-}" \
          "SITE_DOMAIN:${SITE_DOMAIN:-}" "DB_NAME:${DB_NAME:-}" \
          "DB_USER:${DB_USER:-}" "DB_ROOT_PASSWORD:${DB_ROOT_PASSWORD:-}" \
          "TZ:${TZ:-}" "ADMIN_HTTPS:${ADMIN_HTTPS:-}" "USER_HTTPS:${USER_HTTPS:-}" \
          "YII_DEBUG:${YII_DEBUG:-}" "NOTICE_AS_ERROR:${NOTICE_AS_ERROR:-}"; do
  k="${kv%%:*}"; v="${kv#*:}"
  if [ -n "$v" ]; then setkv "$k" "$v"; fi
done

info "镜像来源：${REGISTRY}/${IMAGE_NAMESPACE}/*:${IMAGE_TAG}"

# 数据库密码：未指定且 .env 中为空时随机生成
# （.env 已由模板建出，deploy.sh 的自动生成不会触发，这里必须补上）
DB_PW="$(grep -E '^DB_ROOT_PASSWORD=' .env | head -1 | cut -d= -f2- || true)"
if [ -z "$DB_PW" ]; then
  if command -v openssl >/dev/null 2>&1; then
    DB_PW="$(openssl rand -hex 12)"
  else
    DB_PW="$(head -c 24 /dev/urandom | od -An -tx1 | tr -d ' \n' | cut -c1-24)"
  fi
  setkv DB_ROOT_PASSWORD "$DB_PW"
  info "已生成随机数据库密码"
fi

# 落盘一份凭据（仅 root 可读），便于日后查阅
umask 077
cat > .credentials <<EOF
# phpshop 部署凭据（由 install.sh 生成，请妥善保管）
数据库名：${DB_NAME:-qqdev2}
数据库用户：${DB_USER:-root}
数据库密码：${DB_PW}
镜像仓库：${REGISTRY}
仓库账号：${REGISTRY_USER}
仓库口令：${REGISTRY_PASSWORD}
安装时间：$(date '+%Y-%m-%d %H:%M:%S')
EOF
umask 022
chmod 600 .credentials 2>/dev/null || true

# 便利命令：phpshop ps / logs / restart ...
if [ -d /usr/local/bin ] && [ -w /usr/local/bin ]; then
  cat > /usr/local/bin/phpshop <<EOF
#!/usr/bin/env bash
cd ${INSTALL_DIR} && exec docker compose "\$@"
EOF
  chmod +x /usr/local/bin/phpshop
  info "已创建便捷命令：phpshop（如 phpshop ps / phpshop logs -f php）"
fi

# ------------------------------------------------------------
step "6/6 执行部署"
# ------------------------------------------------------------
if [ "$SKIP_DEPLOY" = "1" ]; then
  warn "已跳过部署（PHPSHOP_SKIP_DEPLOY=1）。手动部署：cd ${INSTALL_DIR} && bash deploy.sh"
  exit 0
fi

bash deploy.sh

cat <<EOF

============================================================
 安装完成 🎉

   安装目录   ${INSTALL_DIR}
   数据库密码 ${DB_PW}
   凭据文件   ${INSTALL_DIR}/.credentials（仅 root 可读）
   便捷命令   phpshop ps | phpshop logs -f php | phpshop restart
   重装/更新  再次执行本安装脚本即可（数据卷会保留）

 提示：镜像来自私有仓库 ${REGISTRY}
       如拉取失败，请核对 .env 中 REGISTRY_USER / REGISTRY_PASSWORD
============================================================
EOF
