#!/bin/ash
#
# One-shot Gitea migration for android_cast:
#   1) Pull-mirror main repo from FE git (git://10.7.0.10/android_cast) — fast interval
#   2) Discover external git submodules (.gitmodules + pinned SHA at REF)
#   3) Pull-mirror each upstream as read-only repo; verify project commit exists
#
# Canonical .gitmodules stays unchanged (upstream URLs). Use gitea_submodule_local_rewrite.sh
# locally if clones should fetch submodules via Gitea.
#
# Usage (Alpine BE, as root):
#   sudo ./gitea_migrate_project.sh
#   sudo ./gitea_migrate_project.sh /var/www/.../androidcast_project/android_cast
#   GITEA_MIRROR_URL=git://10.7.0.10/android_cast GITEA_MAIN_MIRROR_INTERVAL=1m sudo ./gitea_migrate_project.sh
#
set -eu

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

GITEA_OWNER="${GITEA_OWNER:-${GITEA_ORG:-AndroidCast}}"
GITEA_REPO="${GITEA_REPO:-android_cast}"
GITEA_MIRROR_URL="${GITEA_MIRROR_URL:-git://10.7.0.10/android_cast}"
GITEA_MAIN_MIRROR_INTERVAL="${GITEA_MAIN_MIRROR_INTERVAL:-1m}"
GITEA_SUBMODULE_MIRROR_INTERVAL="${GITEA_SUBMODULE_MIRROR_INTERVAL:-24h}"
GITEA_PRIVATE="${GITEA_PRIVATE:-false}"
GITEA_REF="${GITEA_REF:-HEAD}"
GITEA_SKIP_MAIN="${GITEA_SKIP_MAIN:-0}"
GITEA_SKIP_SUBMODULES="${GITEA_SKIP_SUBMODULES:-0}"
GITEA_SKIP_ORG_SETUP="${GITEA_SKIP_ORG_SETUP:-0}"
GITEA_SUBMODULES_CONF="${GITEA_SUBMODULES_CONF:-$SCRIPT_DIR/submodules.defaults.conf}"
WORK_DIR="${WORK_DIR:-}"

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

# Sets GITEA_REPO_DIR and GITEA_GITMODULES_FILE.
resolve_repo_inputs() {
  _arg="${1:-}"
  GITEA_REPO_DIR=""
  GITEA_GITMODULES_FILE=""

  if [ -n "$_arg" ]; then
    case "$_arg" in
      *.gitmodules)
        [ -r "$_arg" ] || die "cannot read $_arg"
        GITEA_GITMODULES_FILE="$(cd "$(dirname "$_arg")" && pwd)/$(basename "$_arg")"
        _parent="$(dirname "$GITEA_GITMODULES_FILE")"
        if [ -d "$_parent/.git" ] || [ -f "$_parent/.git" ]; then
          GITEA_REPO_DIR="$(cd "$_parent" && pwd)"
        else
          GITEA_REPO_DIR="$_parent"
        fi
        return 0
        ;;
      *)
        [ -d "$_arg" ] || die "not a directory: $_arg"
        GITEA_REPO_DIR="$(cd "$_arg" && pwd)"
        GITEA_GITMODULES_FILE="$GITEA_REPO_DIR/.gitmodules"
        return 0
        ;;
    esac
  fi
  if [ -d "./.git" ] || [ -f "./.git" ]; then
    GITEA_REPO_DIR="$(pwd)"
    GITEA_GITMODULES_FILE="$GITEA_REPO_DIR/.gitmodules"
    return 0
  fi
  die "pass android_cast checkout path or .gitmodules (needs .gitmodules)"
}

migrate_main_repo() {
  log "main mirror: $GITEA_OWNER/$GITEA_REPO <- $GITEA_MIRROR_URL (interval $GITEA_MAIN_MIRROR_INTERVAL)"
  if git ls-remote "$GITEA_MIRROR_URL" HEAD >/dev/null 2>&1; then
    log "  upstream reachable"
  else
    warn "  upstream not reachable from BE — check FE git daemon / [migrations] ALLOW_LOCALNETWORKS"
  fi

  if gitea_mirror_pull_repo "$GITEA_MIRROR_URL" "$GITEA_REPO" "$GITEA_OWNER" \
    "$GITEA_MAIN_MIRROR_INTERVAL" "$WORK_DIR"; then
    gitea_mirror_set_interval_api "$GITEA_OWNER" "$GITEA_REPO" "$GITEA_MAIN_MIRROR_INTERVAL" \
      2>/dev/null || true
    gitea_sync_mirror_api "$GITEA_OWNER" "$GITEA_REPO" || true
    log "  OK -> $(gitea_public_repo_url_for "$GITEA_OWNER" "$GITEA_REPO")"
    return 0
  fi
  warn "  main mirror FAILED"
  return 1
}

migrate_submodule() {
  _line="$1"
  _repo="${_line%%|*}"
  _rest="${_line#*|}"
  _upstream="${_rest%%|*}"
  _rest2="${_rest#*|}"
  _path="${_rest2%%|*}"
  _rest3="${_rest2#*|}"
  _sha="${_rest3%%|*}"
  _branch="${_rest3#*|}"
  case "$_branch" in
    "$_sha") _branch="" ;;
  esac

  log "submodule $_path ($GITEA_OWNER/$_repo)"
  log "  upstream: $_upstream"
  if [ "$_sha" = "-" ]; then
    log "  pinned @ $GITEA_REF: (unknown — no gitlink in checkout; mirroring upstream anyway)"
  else
    log "  pinned @ $GITEA_REF: $_sha"
    if git ls-remote "$_upstream" "$_sha" >/dev/null 2>&1; then
      log "  commit on upstream"
    else
      warn "  commit $_sha not on upstream (mirror may still fetch via branch tip)"
    fi
  fi

  if ! gitea_mirror_pull_repo "$_upstream" "$_repo" "$GITEA_OWNER" \
    "$GITEA_SUBMODULE_MIRROR_INTERVAL" "$WORK_DIR"; then
    warn "  mirror create/sync FAILED for $_repo"
    return 1
  fi

  gitea_mirror_set_interval_api "$GITEA_OWNER" "$_repo" "$GITEA_SUBMODULE_MIRROR_INTERVAL" \
    2>/dev/null || true
  gitea_sync_mirror_api "$GITEA_OWNER" "$_repo" || true

  if [ "$_sha" = "-" ]; then
    log "  OK mirror -> $(gitea_public_repo_url_for "$GITEA_OWNER" "$_repo")"
    return 0
  fi

  if gitea_mirror_has_commit "$GITEA_OWNER" "$_repo" "$_sha"; then
    log "  OK pinned commit present -> $(gitea_public_repo_url_for "$GITEA_OWNER" "$_repo")"
    return 0
  fi

  warn "  mirror ok but pinned $_sha not visible yet — retry: $SCRIPT_DIR/gitea_sync_mirror.sh $_repo"
  return 2
}

print_gitea_repo_inventory() {
  log "Gitea repos for $GITEA_OWNER (API):"
  _names="$(gitea_list_repos_api "$GITEA_OWNER" 2>/dev/null)" || _names=""
  if [ -z "$_names" ]; then
    warn "  could not list repos via API (token/CLI)"
    return 1
  fi
  _n=0
  echo "$_names" | while read -r _r; do
    [ -n "$_r" ] || continue
    log "  - $GITEA_OWNER/$_r"
    _n=$((_n + 1))
  done
  _count="$(echo "$_names" | grep -c . 2>/dev/null || echo 0)"
  log "total listed: $_count (expect android_cast + libvpx + opus + speex under $GITEA_ORG)"
}

print_fast_sync_guide() {
  _sync_sh="$SCRIPT_DIR/gitea_sync_mirror.sh"
  _hook="$SCRIPT_DIR/fe-android_cast-post-receive.example"
  cat <<EOF

Fast sync for local main mirror ($GITEA_REPO only)
-------------------------------------------------
Gitea default minimum mirror interval is 10m. For 1m polling set in live app.ini:

  [mirror]
  MIN_INTERVAL = 1m

  [cron.update_mirrors]
  SCHEDULE = @every 1m
  RUN_AT_START = true

  sudo rc-service gitea restart

Per-repo interval for $GITEA_REPO is already set to $GITEA_MAIN_MIRROR_INTERVAL by this script.

Immediate sync (push-triggered or cron every minute on BE):
  sudo $_sync_sh $GITEA_REPO

Sync all mirrors:
  sudo $_sync_sh --all

FE git post-receive hook (git on 10.7.0.10 → trigger BE sync):
  See $_hook
  Copy to FE bare repo hooks/post-receive and set GITEA_MIRROR_SYNC_CMD (ssh or curl).

Submodule mirrors use interval $GITEA_SUBMODULE_MIRROR_INTERVAL; pinned SHAs live in
android_cast gitlinks — mirrors hold full upstream history so those commits are fetchable.

Local clone rewrite (optional, .gitmodules unchanged):
  $SCRIPT_DIR/gitea_submodule_local_rewrite.sh --apply

EOF
}

# --- main ---

_repo_arg=""
while [ $# -gt 0 ]; do
  case "$1" in
    -h|--help)
      sed -n '2,18p' "$0"
      exit 0
      ;;
    --skip-main) GITEA_SKIP_MAIN=1; shift ;;
    --skip-submodules) GITEA_SKIP_SUBMODULES=1; shift ;;
    --skip-org) GITEA_SKIP_ORG_SETUP=1; shift ;;
    --org-setup-only)
      gitea_load_config || exit 1
      gitea_ensure_cli_access || exit 1
      gitea_org_ensure_androidcast "$GITEA_ORG" || exit 1
      exit 0
      ;;
    --ref)
      shift
      GITEA_REF="${1:-HEAD}"
      shift
      ;;
    -*)
      die "unknown option: $1"
      ;;
    *)
      _repo_arg="$1"
      shift
      ;;
  esac
done

gitea_load_config || exit 1
gitea_ensure_cli_access || exit 1

if [ "$GITEA_SKIP_ORG_SETUP" -eq 0 ]; then
  log "org setup: $GITEA_ORG (owners: $GITEA_ORG_OWNERS)"
  gitea_org_ensure_androidcast "$GITEA_ORG" || warn "org setup incomplete — continue with owner=$GITEA_OWNER"
  GITEA_OWNER="${GITEA_OWNER:-$GITEA_ORG}"
fi

if ! gitea_owner_exists "$GITEA_OWNER"; then
  _fb="$(gitea_pick_owner_username)"
  [ -n "$_fb" ] || die "no Gitea owner; set GITEA_OWNER=$GITEA_ORG or create org in UI"
  warn "owner $GITEA_OWNER not found; using $_fb"
  GITEA_OWNER="$_fb"
fi

if command -v rc-service >/dev/null 2>&1; then
  rc-service gitea status 2>/dev/null | grep -qi started || rc-service gitea start 2>/dev/null || true
fi

gitea_prune_stale_tmp
if [ -n "$WORK_DIR" ]; then
  mkdir -p "$WORK_DIR"
else
  WORK_DIR="$(gitea_make_work_dir gitea-migrate)"
fi
trap 'gitea_rm_path "$WORK_DIR"' EXIT INT HUP TERM

log "ini=$GITEA_APP_INI  owner=$GITEA_OWNER  ref=$GITEA_REF"

_main_ok=0
_main_fail=0
_sub_ok=0
_sub_warn=0
_sub_fail=0

if [ "$GITEA_SKIP_MAIN" -eq 0 ]; then
  if migrate_main_repo; then
    _main_ok=1
  else
    _main_fail=1
  fi
else
  log "skipping main mirror (--skip-main)"
fi

if [ "$GITEA_SKIP_SUBMODULES" -eq 0 ]; then
  resolve_repo_inputs "$_repo_arg"
  log "submodule inputs: repo=$GITEA_REPO_DIR  gitmodules=$GITEA_GITMODULES_FILE  ref=$GITEA_REF"
  _sub_list="$WORK_DIR/submodules.list"
  gitea_submodule_collect_entries "$GITEA_REPO_DIR" "$GITEA_REF" \
    "$GITEA_GITMODULES_FILE" "$GITEA_SUBMODULES_CONF" > "$_sub_list" || true
  if [ ! -s "$_sub_list" ]; then
    die "no external submodules found — check .gitmodules on BE or $GITEA_SUBMODULES_CONF"
  fi
  log "submodules to mirror:"
  while IFS= read -r _preview; do
    [ -n "$_preview" ] || continue
    log "  $_preview"
  done < "$_sub_list"
  while IFS= read -r _line; do
      [ -n "$_line" ] || continue
      gitea_submodule_entry_valid "$_line" || continue
      _rc=0
      migrate_submodule "$_line" || _rc=$?
      case "$_rc" in
        0) _sub_ok=$((_sub_ok + 1)) ;;
        2) _sub_warn=$((_sub_warn + 1)) ;;
        *) _sub_fail=$((_sub_fail + 1)) ;;
      esac
    done < "$_sub_list"
else
  log "skipping submodule mirrors (--skip-submodules)"
fi

log "summary: main ok=$_main_ok fail=$_main_fail | submodules ok=$_sub_ok warn=$_sub_warn fail=$_sub_fail"
print_gitea_repo_inventory
print_fast_sync_guide

_sub_migrated=$((_sub_ok + _sub_warn))
[ "$_main_fail" -eq 0 ] && [ "$_sub_fail" -eq 0 ] \
  && { [ "$GITEA_SKIP_SUBMODULES" -eq 1 ] || [ "$_sub_migrated" -gt 0 ]; }
