#!/usr/bin/env bash
# Phase 1 cron — 清理 retention 过期的 DESTROYED apps
#
# 触发：retention_until < now() AND status='DESTROYED'
# 清理：
#   1. Gitea: DELETE /repos/FFAIApps/{emp}-{app}（仓库 + 历史）
#   2. MinIO: rm -r internal-apps-backups/{emp}/{app}/（Litestream 全备份）
#   3. 本地: rm -rf /srv/internal-apps/{repos,data}/{emp}/{app}/
#   4. DB:    DELETE FROM internal_apps（FK CASCADE 自动删 env_vars / deployments）
#
# 用法：
#   bash sweep-expired-apps.sh             # dry-run（默认，列计划但不执行）
#   bash sweep-expired-apps.sh --execute   # 真删
#   bash sweep-expired-apps.sh --execute --max-age-days 30   # 自定义 retention
#
# 退出码：0 成功 / 0 没找到要清的（dry-run 或 execute 都成立）；非 0 = 局部失败
#
# 安全设计：
# - DB 查询用 retention_until < now() 严格过滤，不删活的
# - Gitea DELETE 限定 FFAIApps org（不会跨 org）
# - MinIO 路径限定 internal-apps-backups bucket
# - 本地路径限定 /srv/internal-apps/{repos,data}/ 下
# - flock 防并发（cron 重复触发也只跑一份）
# - 全程 jsonl 日志到 /var/log/ffoa-sweep.jsonl

set -uo pipefail

# ============================================================================
# 配置
# ============================================================================
DRY_RUN=1
MAX_AGE_DAYS=30
LOG_FILE="${FFOA_SWEEP_LOG:-/var/log/ffoa-sweep-expired-apps.jsonl}"
LOCK_FILE="${FFOA_SWEEP_LOCK:-/var/lock/ffoa-sweep-expired-apps.lock}"
ENV_PLATFORM="${INTERNAL_APP_ENV_PLATFORM:-/srv/internal-apps/.env.platform}"
ENV_MINIO="${INTERNAL_APP_ENV_MINIO:-/srv/internal-apps/.env.minio}"
ENV_BACKEND_DEPS="${INTERNAL_APP_ENV_BACKEND_DEPS:-/srv/internal-apps/.env.backend-deps}"
REPO_ROOT="${INTERNAL_APP_REPO_ROOT:-/srv/internal-apps/repos}"
DATA_ROOT="${INTERNAL_APP_DATA_ROOT:-/srv/internal-apps/data}"
GITEA_ORG="${INTERNAL_APP_GITEA_ORG:-FFAIApps}"
PG_CONTAINER="${INTERNAL_APP_PG_CONTAINER:-ffoa-testserver-postgres}"
MINIO_CONTAINER="${INTERNAL_APP_MINIO_CONTAINER:-ffoa-internal-apps-minio}"
MINIO_BUCKET="${INTERNAL_APP_MINIO_BUCKET:-internal-apps-backups}"

# 解析参数
while [[ $# -gt 0 ]]; do
  case "$1" in
    --execute) DRY_RUN=0; shift ;;
    --max-age-days) MAX_AGE_DAYS="$2"; shift 2 ;;
    --help|-h)
      sed -n '2,30p' "$0"
      exit 0
      ;;
    *) echo "unknown arg: $1" >&2; exit 64 ;;
  esac
done

# 加载凭据
[[ -r "$ENV_PLATFORM" ]] && { set -a; . "$ENV_PLATFORM"; set +a; }
[[ -r "$ENV_MINIO" ]]    && { set -a; . "$ENV_MINIO"; set +a; }
[[ -r "$ENV_BACKEND_DEPS" ]] && { set -a; . "$ENV_BACKEND_DEPS"; set +a; }

GITEA_TOKEN="${INTERNAL_APP_GITEA_API_TOKEN:-}"
GITEA_BASE="${INTERNAL_APP_GITEA_BASE_URL:-http://43.130.59.228}"
MINIO_USER="${MINIO_ROOT_USER:-}"
MINIO_PASS="${MINIO_ROOT_PASSWORD:-}"
PG_USER="${POSTGRES_USER:-ffoa}"
PG_DB="${POSTGRES_DB:-ffoa}"

# ============================================================================
# helpers
# ============================================================================
log_event() {
  # log_event <level> <event> [json-attrs...]
  local level="$1" event="$2"; shift 2
  local ts; ts=$(date -u +%Y-%m-%dT%H:%M:%SZ)
  local extras="$*"
  printf '{"ts":"%s","level":"%s","event":"%s","dry_run":%s%s%s}\n' \
    "$ts" "$level" "$event" "$DRY_RUN" "${extras:+,}" "$extras" \
    | tee -a "$LOG_FILE" >&2
}

die() {
  log_event error "$1" "\"detail\":\"${2:-}\""
  exit 1
}

# ============================================================================
# 主流程
# ============================================================================

# 防并发
exec 9>"$LOCK_FILE" || die lock_open_failed
flock -n 9 || { log_event warn "another_sweep_running"; exit 0; }

log_event info sweep_started "\"max_age_days\":$MAX_AGE_DAYS"

# 1. 找过期 apps
EXPIRED=$(docker exec -i "$PG_CONTAINER" psql -U "$PG_USER" -d "$PG_DB" -t -A -F '|' <<SQL
SELECT employee_slug, app_slug, id, retention_until
FROM platform_internal_apps.internal_apps
WHERE status = 'DESTROYED'
  AND retention_until IS NOT NULL
  AND retention_until < now() - interval '0 days'
ORDER BY retention_until ASC;
SQL
)

if [[ -z "$EXPIRED" ]]; then
  log_event info no_expired_apps
  exit 0
fi

COUNT=$(echo "$EXPIRED" | grep -c .)
log_event info expired_apps_found "\"count\":$COUNT"

# 2. 逐个清
FAILS=0
while IFS='|' read -r EMP APP APP_ID RETENTION; do
  [[ -z "$EMP" || -z "$APP" ]] && continue
  REPO="${GITEA_ORG}/${EMP}-${APP}"

  log_event info purge_target \
    "\"employee_slug\":\"$EMP\",\"app_slug\":\"$APP\",\"app_id\":\"$APP_ID\",\"retention_until\":\"$RETENTION\""

  if [[ $DRY_RUN -eq 1 ]]; then
    log_event info dry_run_skip "\"app\":\"$EMP/$APP\""
    continue
  fi

  # 2a. Gitea DELETE
  if [[ -n "$GITEA_TOKEN" ]]; then
    HTTP=$(curl -s -o /dev/null -w '%{http_code}' \
      -X DELETE -H "Authorization: token $GITEA_TOKEN" \
      "$GITEA_BASE/api/v1/repos/$REPO")
    if [[ "$HTTP" == "204" || "$HTTP" == "404" ]]; then
      log_event info gitea_repo_deleted "\"repo\":\"$REPO\",\"http\":$HTTP"
    else
      log_event error gitea_delete_failed "\"repo\":\"$REPO\",\"http\":$HTTP"
      FAILS=$((FAILS+1))
      continue   # 中止本 app，留下 DB 行下次重试
    fi
  else
    log_event warn no_gitea_token_skip_repo_delete
  fi

  # 2b. MinIO 清备份
  if [[ -n "$MINIO_USER" && -n "$MINIO_PASS" ]]; then
    if docker run --rm --network ffoa-network --entrypoint mc minio/mc:latest \
        alias set sweep "http://${MINIO_CONTAINER}:9000" "$MINIO_USER" "$MINIO_PASS" >/dev/null 2>&1 && \
       docker run --rm --network ffoa-network --entrypoint mc minio/mc:latest \
        rm --recursive --force "sweep/${MINIO_BUCKET}/${EMP}/${APP}/" >/dev/null 2>&1; then
      log_event info minio_purged "\"path\":\"${EMP}/${APP}/\""
    else
      log_event warn minio_purge_failed "\"path\":\"${EMP}/${APP}/\""
      # 不阻塞后续：MinIO 残留无外部副作用，DB 已确定要删
    fi
  fi

  # 2c. 本地目录
  rm -rf "${REPO_ROOT}/${EMP}/${APP}" "${DATA_ROOT}/${EMP}/${APP}" 2>/dev/null
  log_event info local_dirs_removed "\"emp\":\"$EMP\",\"app\":\"$APP\""

  # 2d. DB DELETE（最后一步——只有所有副作用清完才动 DB；FK CASCADE 自动删 env_vars / deployments）
  if docker exec -i "$PG_CONTAINER" psql -U "$PG_USER" -d "$PG_DB" -c \
      "DELETE FROM platform_internal_apps.internal_apps WHERE id = '$APP_ID';" >/dev/null 2>&1; then
    log_event info db_row_deleted "\"app_id\":\"$APP_ID\""
  else
    log_event error db_delete_failed "\"app_id\":\"$APP_ID\""
    FAILS=$((FAILS+1))
  fi
done <<< "$EXPIRED"

log_event info sweep_completed "\"fails\":$FAILS"
exit $FAILS
