#!/bin/ash
#
# Collect full Gitea backup on Alpine BE (parse /etc/gitea/app.ini).
# Run as root on the BE where Gitea is installed.
#
# Usage:
#   ./gitea_collect_backup.sh
#   OUT_BASE=/root/gitea-backups ./gitea_collect_backup.sh
#   GITEA_INI=/etc/gitea/app.ini ./gitea_collect_backup.sh
#
# Does NOT touch the main working tree:
#   /var/www/localhost/htdocs/apps/app/androidcast_project/android_cast
#
set -eu

SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
# shellcheck source=gitea_ini.sh
. "$SCRIPT_DIR/gitea_ini.sh"

OUT_BASE="${OUT_BASE:-/var/backups/gitea}"
STAMP="$(date +%Y%m%d-%H%M%S)"
OUT_DIR="$OUT_BASE/gitea-collect-$STAMP"
MANIFEST="$OUT_DIR/MANIFEST.txt"

# Paths that must never be deleted or overwritten by the optional reset step.
PROTECTED_MAIN_REPO="${PROTECTED_MAIN_REPO:-/var/www/localhost/htdocs/apps/app/androidcast_project/android_cast}"

log() { printf '[gitea-collect] %s\n' "$*"; }
warn() { printf '[gitea-collect] WARN: %s\n' "$*" >&2; }
die() { printf '[gitea-collect] ERROR: %s\n' "$*" >&2; exit 1; }

need_cmd() {
  command -v "$1" >/dev/null 2>&1 || die "missing command: $1"
}

copy_tree() {
  _src="$1"
  _dst="$2"
  if [ ! -e "$_src" ]; then
    warn "skip missing: $_src"
    return 0
  fi
  mkdir -p "$(dirname "$_dst")"
  if cp -a "$_src" "$_dst" 2>/dev/null; then
    log "copied: $_src -> $_dst"
    return 0
  fi
  # BusyBox cp may lack -a; try tar pipe.
  mkdir -p "$_dst"
  (cd "$_src" && tar cf - .) | (cd "$_dst" && tar xf -)
  log "archived (tar): $_src -> $_dst"
}

dump_mysql() {
  _out="$1"
  need_cmd mysqldump
  _args=""
  if [ -n "$GITEA_DB_HOST" ]; then
    case "$GITEA_DB_HOST" in
      /*) _args="--socket=$GITEA_DB_HOST" ;;
      *) _args="-h $GITEA_DB_HOST" ;;
    esac
  fi
  # shellcheck disable=SC2086
  mysqldump $_args -u"$GITEA_DB_USER" ${GITEA_DB_PASS:+-p"$GITEA_DB_PASS"} \
    --single-transaction --routines --triggers "$GITEA_DB_NAME" > "$_out"
  log "mysqldump -> $_out"
}

dump_postgres() {
  _out="$1"
  need_cmd pg_dump
  PGPASSWORD="$GITEA_DB_PASS" pg_dump -h "${GITEA_DB_HOST:-127.0.0.1}" -U "$GITEA_DB_USER" "$GITEA_DB_NAME" > "$_out"
  log "pg_dump -> $_out"
}

write_manifest() {
  {
    echo "Gitea collect backup"
    echo "created_at=$STAMP"
    echo "hostname=$(hostname 2>/dev/null || echo unknown)"
    echo "gitea_ini=$GITEA_APP_INI"
    echo "repo_root=$GITEA_REPO_ROOT"
    echo "app_data=$GITEA_APP_DATA"
    echo "root_url=$GITEA_ROOT_URL"
    echo "db_type=$GITEA_DB_TYPE"
    echo "db_name=$GITEA_DB_NAME"
    echo "lfs_path=$GITEA_LFS_PATH"
    echo "log_root=$GITEA_LOG_ROOT"
    echo "protected_main_repo=$PROTECTED_MAIN_REPO"
    echo ""
    echo "--- gitea version ---"
    gitea_cli "-v" 2>/dev/null || echo "gitea -v unavailable"
    echo ""
    echo "--- repo directories ---"
    if [ -d "$GITEA_REPO_ROOT" ]; then
      find "$GITEA_REPO_ROOT" -maxdepth 3 -type d -name '*.git' 2>/dev/null || true
    else
      echo "(repo root missing)"
    fi
  } > "$MANIFEST"
}

list_gitea_repos() {
  if [ ! -d "$GITEA_REPO_ROOT" ]; then
    return 0
  fi
  find "$GITEA_REPO_ROOT" -mindepth 2 -maxdepth 2 -type d -name '*.git' 2>/dev/null \
    | while read -r gitdir; do
        owner="$(basename "$(dirname "$gitdir")")"
        repo="$(basename "$gitdir" .git)"
        printf '%s/%s\n' "$owner" "$repo"
      done
}

delete_gitea_repo_records() {
  _owner="$1"
  _repo="$2"
  if gitea_cli "admin" "repo" "delete" "$_owner/$_repo" 2>/dev/null; then
    log "gitea admin repo delete $_owner/$_repo"
    return 0
  fi
  if gitea_cli "admin" "repo-delete" "$_owner/$_repo" 2>/dev/null; then
    log "gitea admin repo-delete $_owner/$_repo"
    return 0
  fi
  warn "could not delete $_owner/$_repo via gitea CLI (will still remove bare files)"
  return 1
}

reset_gitea_repos_only() {
  if ! gitea_path_is_safe_to_wipe "$GITEA_REPO_ROOT"; then
    die "refusing reset: repo root not under safe prefix: $GITEA_REPO_ROOT"
  fi
  case "$PROTECTED_MAIN_REPO" in
    "$GITEA_REPO_ROOT"|"$GITEA_REPO_ROOT"/*)
      die "refusing reset: Gitea repo root overlaps protected main repo path"
      ;;
  esac

  log "stopping gitea (if running)..."
  if command -v rc-service >/dev/null 2>&1; then
    rc-service gitea stop 2>/dev/null || true
  fi

  log "removing Gitea repo registrations via CLI..."
  _repos="$(list_gitea_repos)"
  if [ -n "$_repos" ]; then
    echo "$_repos" | while read -r slug; do
      [ -n "$slug" ] || continue
      _o="${slug%%/*}"
      _r="${slug#*/}"
      delete_gitea_repo_records "$_o" "$_r" || true
    done
  fi

  if [ -d "$GITEA_REPO_ROOT" ]; then
    log "clearing bare repos under $GITEA_REPO_ROOT (not touching $PROTECTED_MAIN_REPO)"
    find "$GITEA_REPO_ROOT" -mindepth 1 -maxdepth 1 -type d -exec rm -rf {} +
  fi
  mkdir -p "$GITEA_REPO_ROOT"

  log "starting gitea..."
  if command -v rc-service >/dev/null 2>&1; then
    rc-service gitea start 2>/dev/null || warn "rc-service gitea start failed — start manually"
  fi
  log "Gitea repository store reset to empty (Gitea DB may still need doctor if UI shows ghosts)"
}

# --- main ---

[ "$(id -u)" -eq 0 ] || die "run as root (needed for service stop, file reads, mysqldump)"

gitea_load_config || exit 1
need_cmd awk
need_cmd mkdir
need_cmd date

mkdir -p "$OUT_DIR"
log "backup directory: $OUT_DIR"

write_manifest

log "copy app.ini"
mkdir -p "$OUT_DIR/config"
copy_tree "$GITEA_APP_INI" "$OUT_DIR/config/app.ini"

log "copy APP_DATA_PATH (includes caches, indexes, attachments, custom/)"
copy_tree "$GITEA_APP_DATA" "$OUT_DIR/app_data"

log "copy repository ROOT (bare repos)"
copy_tree "$GITEA_REPO_ROOT" "$OUT_DIR/gitea-repositories"

if [ -n "$GITEA_LFS_PATH" ] && [ "$GITEA_LFS_PATH" != "$GITEA_APP_DATA/lfs" ]; then
  log "copy LFS path"
  copy_tree "$GITEA_LFS_PATH" "$OUT_DIR/lfs"
fi

if [ -n "$GITEA_LOG_ROOT" ] && [ -d "$GITEA_LOG_ROOT" ]; then
  log "copy log directory"
  copy_tree "$GITEA_LOG_ROOT" "$OUT_DIR/logs"
fi

log "database dump"
mkdir -p "$OUT_DIR/database"
case "$(echo "$GITEA_DB_TYPE" | tr '[:upper:]' '[:lower:]')" in
  mysql)
    dump_mysql "$OUT_DIR/database/gitea-mysql.sql"
    ;;
  postgres|postgresql)
    dump_postgres "$OUT_DIR/database/gitea-postgres.sql"
    ;;
  sqlite3|sqlite)
    if [ -n "$GITEA_DB_PATH" ] && [ -f "$GITEA_DB_PATH" ]; then
      copy_tree "$GITEA_DB_PATH" "$OUT_DIR/database/gitea.sqlite3"
    elif [ -f "$GITEA_APP_DATA/gitea.db" ]; then
      copy_tree "$GITEA_APP_DATA/gitea.db" "$OUT_DIR/database/gitea.sqlite3"
    else
      warn "sqlite DB file not found (database PATH / gitea.db)"
    fi
    ;;
  *)
    warn "unknown DB_TYPE=$GITEA_DB_TYPE — copy app_data only"
    ;;
esac

if command -v gitea >/dev/null 2>&1; then
  if gitea_cli "dump" "--file" "$OUT_DIR/gitea-built-in-dump.zip" 2>/dev/null; then
    log "gitea built-in dump -> gitea-built-in-dump.zip"
  else
    warn "gitea dump subcommand unavailable or failed (backup copies above are still valid)"
  fi
fi

_bytes="$(du -sh "$OUT_DIR" 2>/dev/null | awk '{print $1}')"
log "done. size≈${_bytes:-?}  manifest=$MANIFEST"
log "protected working tree (never touched): $PROTECTED_MAIN_REPO"

printf '\nWould you like to reset Gitea repositories state to the default (empty)? [y/N] '
read -r _reset_answer || _reset_answer=""
case "$(echo "$_reset_answer" | tr '[:upper:]' '[:lower:]')" in
  y|yes)
    log "reset confirmed"
    reset_gitea_repos_only
    ;;
  *)
    log "reset skipped"
    ;;
esac

log "backup complete: $OUT_DIR"
