#!/bin/ash
#
# Attach canonical git:// remote to Gitea as a pull mirror (no NFS/SSHFS shared folder).
# Run on Alpine BE after gitea_collect_backup.sh (optional reset) and Gitea is running.
#
# Usage:
#   ./gitea_attach_remote.sh
#   ./gitea_attach_remote.sh git://10.7.0.10/android_cast
#   GITEA_MIRROR_URL=git://f0xx.org/android_cast ./gitea_attach_remote.sh
#
# GITEA_ADMIN_TOKEN is optional on BE when run as root: the script can create a repo
# and a short-lived push token via `gitea admin` CLI. Set a token only if CLI fails.
#
# Methods (auto):
#   1) Gitea API /repos/migrate with mirror=true  (ongoing pull sync in UI)
#   2) gitea admin create-repo + git clone --mirror + git push --mirror
#
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_org_lib.sh
. "$SCRIPT_DIR/gitea_org_lib.sh"

GITEA_MIRROR_URL="${GITEA_MIRROR_URL:-git://f0xx.org/android_cast}"
GITEA_OWNER="${GITEA_OWNER:-${GITEA_ORG:-AndroidCast}}"
GITEA_REPO="${GITEA_REPO:-android_cast}"
GITEA_MIRROR_INTERVAL="${GITEA_MIRROR_INTERVAL:-10m}"
GITEA_PRIVATE="${GITEA_PRIVATE:-false}"
WORK_DIR="${WORK_DIR:-}"

# First positional argument = mirror URL (convenience).
if [ $# -ge 1 ] && [ -n "$1" ]; then
  case "$1" in
    -h|--help)
      sed -n '2,20p' "$0"
      exit 0
      ;;
    *)
      GITEA_MIRROR_URL="$1"
      ;;
  esac
fi

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

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

# Public clone URL (via ROOT_URL + subpath). Local :3000 git/API omit subpath (nginx strips it).
gitea_public_repo_url() {
  _root="$GITEA_ROOT_URL"
  case "$_root" in
    */) _root="${_root%/}" ;;
  esac
  printf '%s/%s/%s.git' "$_root" "$GITEA_OWNER" "$GITEA_REPO"
}

gitea_local_git_url() {
  printf 'http://127.0.0.1:%s/%s/%s.git' "${GITEA_HTTP_PORT:-3000}" "$GITEA_OWNER" "$GITEA_REPO"
}

gitea_api_base() {
  printf '%s' "${GITEA_LOCAL_API:-http://127.0.0.1:3000/api/v1}"
}

test_remote_reachable() {
  _url="$1"
  log "probing remote: $_url"
  if command -v git >/dev/null 2>&1; then
    if git ls-remote "$_url" HEAD >/dev/null 2>&1; then
      log "remote reachable (git ls-remote HEAD)"
      return 0
    fi
    warn "git ls-remote failed for $_url"
  fi
  return 1
}

resolve_gitea_owner() {
  if [ "${GITEA_SKIP_ORG_SETUP:-0}" -eq 0 ]; then
    gitea_org_ensure_androidcast "$GITEA_ORG" 2>/dev/null || true
    GITEA_OWNER="${GITEA_OWNER:-$GITEA_ORG}"
  fi
  if gitea_owner_exists "$GITEA_OWNER"; then
    log "Gitea repo owner: $GITEA_OWNER"
    return 0
  fi
  warn "Gitea has no owner '$GITEA_OWNER' (user or org — not Linux/FE login)"
  log "Gitea users on this instance:"
  gitea_list_usernames | while read -r _u; do
    [ -n "$_u" ] && log "  - $_u"
  done
  _fallback="$(gitea_pick_owner_username)"
  if [ -z "$_fallback" ]; then
    die "no Gitea users in DB — complete first-run at $GITEA_ROOT_URL or: sudo -u $GITEA_RUN_USER gitea -c $GITEA_APP_INI admin user create --username admin --password '...' --email admin@local --admin"
  fi
  warn "using Gitea user '$_fallback' as repo owner (override: GITEA_OWNER=...)"
  GITEA_OWNER="$_fallback"
}

resolve_push_token() {
  gitea_resolve_push_token
}

create_repo_via_cli() {
  log "creating empty repo $GITEA_OWNER/$GITEA_REPO via gitea admin CLI..."
  if gitea_cli "admin" "user" "create-repo" \
    "--username" "$GITEA_OWNER" \
    "--name" "$GITEA_REPO" \
    "--private=$GITEA_PRIVATE" 2>/dev/null; then
    log "repo created (admin user create-repo)"
    return 0
  fi
  if gitea_cli "admin" "create-repository" \
    "--owner" "$GITEA_OWNER" \
    "--name" "$GITEA_REPO" \
    "--private=$GITEA_PRIVATE" 2>/dev/null; then
    log "repo created (admin create-repository)"
    return 0
  fi
  warn "gitea admin create-repo failed — user '$GITEA_OWNER' must exist (create in Gitea UI first)"
  return 1
}

create_repo_via_api() {
  [ -n "${GITEA_ADMIN_TOKEN:-}" ] || return 1
  need_cmd curl
  _api="$(gitea_api_base)"
  _resp="$(mktemp /tmp/gitea-create-repo-XXXXXX.json)"
  _http="$(curl -sS -o "$_resp" -w '%{http_code}' \
    -X POST "$_api/admin/users/$GITEA_OWNER/repos" \
    -H "Authorization: token $GITEA_ADMIN_TOKEN" \
    -H "Content-Type: application/json" \
    -d "{\"name\":\"$GITEA_REPO\",\"private\":$GITEA_PRIVATE}")"
  _rc=1
  if [ "$_http" = "201" ] || [ "$_http" = "200" ]; then
    log "repo created via API"
    _rc=0
  elif [ "$_http" = "409" ]; then
    log "repo already exists (API 409)"
    _rc=0
  else
    warn "API create repo HTTP $_http"
    cat "$_resp" >&2 2>/dev/null || true
  fi
  gitea_rm_path "$_resp"
  return "$_rc"
}

gitea_owner_uid() {
  gitea_entity_uid_for "$GITEA_OWNER"
}

migrate_via_api() {
  need_cmd curl
  _token="${GITEA_ADMIN_TOKEN:-}"
  [ -n "$_token" ] || _token="$(resolve_push_token)" || return 1

  _uid="$(gitea_owner_uid)" || {
    warn "API migrate skipped: no uid for owner $GITEA_OWNER"
    return 1
  }

  _api="$(gitea_api_base)"
  _payload=$(cat <<EOF
{
  "clone_addr": "$GITEA_MIRROR_URL",
  "repo_name": "$GITEA_REPO",
  "uid": $_uid,
  "mirror": true,
  "mirror_interval": "$GITEA_MIRROR_INTERVAL",
  "private": $GITEA_PRIVATE,
  "wiki": true,
  "issues": false,
  "milestones": false,
  "labels": false,
  "releases": true,
  "description": "Pull mirror of $GITEA_MIRROR_URL"
}
EOF
)

  log "API migrate (pull mirror) uid=$_uid via $_api ..."
  _resp="$(mktemp /tmp/gitea-migrate-XXXXXX.json)"
  _http="$(curl -sS -o "$_resp" -w '%{http_code}' \
    -X POST "$_api/repos/migrate" \
    -H "Authorization: token $_token" \
    -H "Content-Type: application/json" \
    -d "$_payload")"

  _rc=1
  if [ "$_http" = "201" ] || [ "$_http" = "200" ]; then
    log "mirror created via API (HTTP $_http)"
    cat "$_resp"
    printf '\n'
    _rc=0
  else
    if [ "$_http" = "409" ] || [ "$_http" = "422" ]; then
      warn "migrate rejected HTTP $_http — repo may already exist; use mirror settings or delete repo first"
    fi
    warn "API migrate failed HTTP $_http:"
    cat "$_resp" >&2
  fi
  gitea_rm_path "$_resp"
  return "$_rc"
}

ensure_gitea_repo_exists() {
  create_repo_via_cli || create_repo_via_api || true
}

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

  _mirror="$WORK_DIR/${GITEA_REPO}.mirror.git"
  _rc=1

  log "git clone --mirror $GITEA_MIRROR_URL"
  gitea_rm_path "$_mirror"
  if ! git clone --mirror "$GITEA_MIRROR_URL" "$_mirror"; then
    gitea_rm_path "$WORK_DIR"
    return 1
  fi

  ensure_gitea_repo_exists

  _push_base="$(gitea_local_git_url)"
  if _token="$(resolve_push_token)"; then
    _auth_url="${_push_base#http://}"
    _push_url="http://${_token}@${_auth_url}"
    log "git push --mirror -> $_push_base (localhost, no subpath)"
    if git -C "$_mirror" push --mirror "$_push_url"; then
      _rc=0
      log "one-time mirror push complete"
    fi
  else
    die "need credentials: export GITEA_ADMIN_TOKEN=... or run as root with gitea CLI"
  fi

  warn "for automatic updates, re-run with API migrate or enable pull mirror in Gitea UI"
  gitea_rm_path "$WORK_DIR"
  trap - EXIT INT HUP TERM
  return "$_rc"
}

enable_mirror_sync_cron_hint() {
  cat <<EOF

Ongoing sync (no shared mount):
  • Pull mirror (recommended): Gitea → Repo → Settings → Mirror → Pull mirror
    Remote: $GITEA_MIRROR_URL
    Interval: $GITEA_MIRROR_INTERVAL
  • Or cron on BE (as git user):
      */15 * * * * gitea -c $GITEA_APP_INI admin repo-sync-mirrors

Clone URL (public):
  $(gitea_public_repo_url)

EOF
}

print_token_help() {
  cat <<'EOF'

About GITEA_ADMIN_TOKEN (optional on BE):
  • What: a Personal Access Token for your Gitea admin/owner user.
  • Where: log in to Gitea UI → top-right avatar → Settings → Applications
           → Generate New Token → name it e.g. "attach-script" → scopes: all (or repo write).
  • Why: HTTP git push and REST API require authentication. Without a token the script
         tries `gitea admin user generate-access-token` on the server (works when run as root).
  • You do NOT need a token if `gitea admin create-repo` + generate-access-token succeed.

EOF
}

# --- main ---

gitea_load_config || exit 1
gitea_ensure_cli_access || exit 1
resolve_gitea_owner

log "config ini=$GITEA_APP_INI  RUN_USER=$GITEA_RUN_USER"
log "Gitea ROOT_URL=$GITEA_ROOT_URL (public)"
log "local API=$(gitea_api_base)  local git=$(gitea_local_git_url)"
log "target repo: $GITEA_OWNER/$GITEA_REPO"
log "mirror from: $GITEA_MIRROR_URL"

if ! test_remote_reachable "$GITEA_MIRROR_URL"; then
  warn "remote not reachable from this host — check git:// daemon / firewall"
fi

if command -v rc-service >/dev/null 2>&1; then
  if ! rc-service gitea status 2>/dev/null | grep -qi started; then
    warn "gitea service may not be running — starting..."
    rc-service gitea start 2>/dev/null || true
  fi
fi

if migrate_via_api; then
  enable_mirror_sync_cron_hint
  log "done (API pull mirror)"
  exit 0
fi

log "falling back to git clone --mirror + push --mirror"
if ! mirror_via_git; then
  print_token_help
  exit 1
fi
enable_mirror_sync_cron_hint
log "done (git mirror push)"
