#!/bin/ash
# Shared pull-mirror helpers for Gitea BE scripts.
# shellcheck shell=sh
# Requires: . "$SCRIPT_DIR/gitea_ini.sh" && gitea_load_config

gitea_public_repo_url_for() {
  _owner="$1"
  _repo="$2"
  _root="$GITEA_ROOT_URL"
  case "$_root" in
    */) _root="${_root%/}" ;;
  esac
  printf '%s/%s/%s.git' "$_root" "$_owner" "$_repo"
}

gitea_local_git_url_for() {
  printf 'http://127.0.0.1:%s/%s/%s.git' "${GITEA_HTTP_PORT:-3000}" "$1" "$2"
}

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

# Remove abandoned gitea-* temp dirs/files (e.g. after kill -9). Default: older than 60 min.
gitea_prune_stale_tmp() {
  _min="${GITEA_TMP_MAX_AGE_MIN:-60}"
  command -v find >/dev/null 2>&1 || return 0
  find /tmp -maxdepth 1 -type d \
    \( -name 'gitea-migrate-*' -o -name 'gitea-submodules-*' \
       -o -name 'gitea-attach-*' -o -name 'gitea-mirror-*' \) \
    -mmin "+$_min" -exec rm -rf {} + 2>/dev/null || true
  find /tmp -maxdepth 1 -type f -name 'gitea-*.json' \
    -mmin "+$_min" -delete 2>/dev/null || true
}

gitea_make_work_dir() {
  _prefix="${1:-gitea-work}"
  mktemp -d "/tmp/${_prefix}-XXXXXX"
}

gitea_rm_path() {
  rm -rf "$1" 2>/dev/null || true
}

# First numeric "id" from Gitea JSON (handles "id":2 and "id":"2").
gitea_json_extract_id() {
  printf '%s' "$1" | grep -o '"id":[0-9]*' | head -1 | grep -o '[0-9]*'
}

gitea_resolve_push_token() {
  if [ -n "${GITEA_ADMIN_TOKEN:-}" ]; then
    printf '%s' "$GITEA_ADMIN_TOKEN"
    return 0
  fi
  # One PAT per script run (re-generating with the same --token-name fails).
  if [ -n "${_GITEA_RESOLVED_TOKEN:-}" ]; then
    printf '%s' "$_GITEA_RESOLVED_TOKEN"
    return 0
  fi
  _admin="$(gitea_pick_owner_username)"
  [ -n "$_admin" ] || return 1
  _token=""
  _token="$(gitea_cli "admin" "user" "generate-access-token" \
    "--username" "$_admin" \
    "--token-name" "mirror-$$-$(date +%s)-${RANDOM:-0}" \
    "--scopes" "all" \
    "--raw" 2>/dev/null)" && [ -n "$_token" ] || return 1
  _GITEA_RESOLVED_TOKEN="$_token"
  export _GITEA_RESOLVED_TOKEN GITEA_ADMIN_TOKEN="$_token"
  printf '%s' "$_token"
}

gitea_owner_uid_for() {
  gitea_entity_uid_for "$1"
}

# UID for migrate/API owner — user or organization (not the same as /users/{org}).
gitea_entity_uid_for() {
  _owner="$1"
  command -v curl >/dev/null 2>&1 || return 1
  _token="${GITEA_ADMIN_TOKEN:-}"
  [ -n "$_token" ] || _token="$(gitea_resolve_push_token)" || return 1
  _api="$(gitea_api_base)"
  _json=""
  if curl -sS -o /dev/null -w '%{http_code}' "$_api/orgs/$_owner" \
    -H "Authorization: token $_token" | grep -q '^200'; then
    _json="$(curl -sS "$_api/orgs/$_owner" -H "Authorization: token $_token")"
  else
    _json="$(curl -sS "$_api/users/$_owner" -H "Authorization: token $_token")"
  fi
  _uid="$(gitea_json_extract_id "$_json")"
  [ -n "$_uid" ] || return 1
  printf '%s' "$_uid"
}

gitea_owner_is_org() {
  gitea_org_exists "$1" 2>/dev/null
}

gitea_create_repo_cli() {
  _owner="$1"
  _repo="$2"
  _private="${3:-false}"
  if gitea_cli "admin" "user" "create-repo" \
    "--username" "$_owner" \
    "--name" "$_repo" \
    "--private=$_private" 2>/dev/null; then
    return 0
  fi
  gitea_cli "admin" "create-repository" \
    "--owner" "$_owner" \
    "--name" "$_repo" \
    "--private=$_private" 2>/dev/null
}

gitea_migrate_mirror_api() {
  _owner="$1"
  _repo="$2"
  _clone_url="$3"
  _interval="${4:-10m}"
  _private="${5:-false}"
  _desc="${6:-Pull mirror of $_clone_url}"

  command -v curl >/dev/null 2>&1 || return 1
  _token="${GITEA_ADMIN_TOKEN:-}"
  [ -n "$_token" ] || _token="$(gitea_resolve_push_token)" || return 1
  _uid="$(gitea_entity_uid_for "$_owner")" || return 1

  _api="$(gitea_api_base)"
  _payload=$(cat <<EOF
{
  "clone_addr": "$_clone_url",
  "repo_name": "$_repo",
  "uid": $_uid,
  "mirror": true,
  "mirror_interval": "$_interval",
  "private": $_private,
  "wiki": false,
  "issues": false,
  "milestones": false,
  "labels": false,
  "releases": true,
  "description": "$_desc"
}
EOF
)
  _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")"

  if [ "$_http" = "201" ] || [ "$_http" = "200" ]; then
    gitea_rm_path "$_resp"
    return 0
  fi
  if [ "$_http" = "409" ] || [ "$_http" = "422" ]; then
    gitea_rm_path "$_resp"
    return 2
  fi
  if [ -r "$_resp" ]; then
    printf '[gitea-mirror] migrate HTTP %s: ' "$_http" >&2
    head -c 400 "$_resp" >&2 || true
    printf '\n' >&2
  fi
  gitea_rm_path "$_resp"
  return 1
}

gitea_mirror_push_git() {
  _owner="$1"
  _repo="$2"
  _clone_url="$3"
  _work_dir="$4"

  command -v git >/dev/null 2>&1 || return 1
  _mirror="$_work_dir/${_repo}.mirror.git"
  _rc=1

  gitea_rm_path "$_mirror"
  if ! git clone --mirror "$_clone_url" "$_mirror"; then
    gitea_rm_path "$_mirror"
    return 1
  fi

  gitea_create_repo_cli "$_owner" "$_repo" "${GITEA_PRIVATE:-false}" || true
  _push_base="$(gitea_local_git_url_for "$_owner" "$_repo")"
  if _token="$(gitea_resolve_push_token)"; then
    _auth_url="${_push_base#http://}"
    if git -C "$_mirror" push --mirror "http://${_token}@${_auth_url}"; then
      _rc=0
    fi
  fi

  gitea_rm_path "$_mirror"
  return "$_rc"
}

gitea_repo_exists_api() {
  _owner="$1"
  _repo="$2"
  command -v curl >/dev/null 2>&1 || return 1
  _token="${GITEA_ADMIN_TOKEN:-}"
  [ -n "$_token" ] || _token="$(gitea_resolve_push_token)" || return 1
  _api="$(gitea_api_base)"
  _http="$(curl -sS -o /dev/null -w '%{http_code}' \
    "$_api/repos/$_owner/$_repo" \
    -H "Authorization: token $_token")"
  [ "$_http" = "200" ]
}

gitea_list_repos_api() {
  _owner="$1"
  command -v curl >/dev/null 2>&1 || return 1
  _token="${GITEA_ADMIN_TOKEN:-}"
  [ -n "$_token" ] || _token="$(gitea_resolve_push_token)" || return 1
  _api="$(gitea_api_base)"
  _list_url="$_api/users/$_owner/repos?limit=50"
  _http="$(curl -sS -o /dev/null -w '%{http_code}' "$_api/orgs/$_owner" \
    -H "Authorization: token $_token")"
  if [ "$_http" = "200" ]; then
    _list_url="$_api/orgs/$_owner/repos?limit=50"
  fi
  curl -sS "$_list_url" \
    -H "Authorization: token $_token" \
    | awk -F'"' '
      /"name":/ && !/"owner":/ && !/"full_name":/ {
        for (i = 1; i <= NF; i++) {
          if ($i == "\"name\":" && $(i+1) ~ /^"/) {
            gsub(/^"/, "", $(i+1)); gsub(/",?$/, "", $(i+1))
            if ($(i+1) != "" && $(i+1) != "null") print $(i+1)
            break
          }
        }
      }
    '
}

gitea_repo_get_api() {
  _owner="$1"
  _repo="$2"
  command -v curl >/dev/null 2>&1 || return 1
  _token="${GITEA_ADMIN_TOKEN:-}"
  [ -n "$_token" ] || _token="$(gitea_resolve_push_token)" || return 1
  _api="$(gitea_api_base)"
  curl -sS "$_api/repos/$_owner/$_repo" \
    -H "Authorization: token $_token"
}

gitea_repo_set_default_branch_api() {
  _owner="$1"
  _repo="$2"
  _branch="$3"
  command -v curl >/dev/null 2>&1 || return 1
  _token="${GITEA_ADMIN_TOKEN:-}"
  [ -n "$_token" ] || _token="$(gitea_resolve_push_token)" || return 1
  _api="$(gitea_api_base)"
  _payload="{\"default_branch\":\"$_branch\"}"
  _http="$(curl -sS -o /dev/null -w '%{http_code}' \
    -X PATCH "$_api/repos/$_owner/$_repo" \
    -H "Authorization: token $_token" \
    -H "Content-Type: application/json" \
    -d "$_payload")"
  case "$_http" in
    200|204) return 0 ;;
    *) return 1 ;;
  esac
}

gitea_sync_mirror_api() {
  _owner="$1"
  _repo="$2"
  command -v curl >/dev/null 2>&1 || return 1
  _token="${GITEA_ADMIN_TOKEN:-}"
  [ -n "$_token" ] || _token="$(gitea_resolve_push_token)" || return 1
  _api="$(gitea_api_base)"
  curl -sS -o /dev/null -w '%{http_code}' \
    -X POST "$_api/repos/$_owner/$_repo/mirror-sync" \
    -H "Authorization: token $_token" | grep -q '^20'
}

gitea_mirror_set_interval_api() {
  _owner="$1"
  _repo="$2"
  _interval="$3"
  command -v curl >/dev/null 2>&1 || return 1
  _token="${GITEA_ADMIN_TOKEN:-}"
  [ -n "$_token" ] || _token="$(gitea_resolve_push_token)" || return 1
  _api="$(gitea_api_base)"
  _payload="{\"mirror_interval\":\"$_interval\"}"
  _resp="$(mktemp /tmp/gitea-mirror-interval-XXXXXX.json)"
  _http="$(curl -sS -o "$_resp" -w '%{http_code}' \
    -X PATCH "$_api/repos/$_owner/$_repo" \
    -H "Authorization: token $_token" \
    -H "Content-Type: application/json" \
    -d "$_payload")"
  gitea_rm_path "$_resp"
  case "$_http" in
    200|204) return 0 ;;
    *) return 1 ;;
  esac
}

# True if branch exists on any git remote URL (canonical FE git, upstream, etc.).
gitea_git_remote_has_branch() {
  _url="$1"
  _branch="$2"
  command -v git >/dev/null 2>&1 || return 1
  git ls-remote "$_url" "refs/heads/$_branch" 2>/dev/null | grep -q .
}

# True if commit SHA is reachable on the mirror (after sync).
gitea_mirror_has_commit() {
  _owner="$1"
  _repo="$2"
  _sha="$3"
  command -v git >/dev/null 2>&1 || return 1
  _url="$(gitea_local_git_url_for "$_owner" "$_repo")"
  git ls-remote "$_url" "$_sha" 2>/dev/null | grep -q "$_sha"
}

# Mirror one upstream into Gitea (API migrate, else git push). Returns 0 on success.
gitea_mirror_pull_repo() {
  _clone_url="$1"
  _repo="$2"
  _owner="${3:-$GITEA_OWNER}"
  _interval="${4:-${GITEA_MIRROR_INTERVAL:-10m}}"
  _local_work=""
  if [ $# -ge 5 ] && [ -n "$5" ]; then
    _work_dir="$5"
  else
    _local_work="$(gitea_make_work_dir gitea-mirror)"
    _work_dir="$_local_work"
  fi

  _rc=0
  gitea_migrate_mirror_api "$_owner" "$_repo" "$_clone_url" "$_interval" "${GITEA_PRIVATE:-false}" \
    && return 0
  _api_rc=$?
  if [ "$_api_rc" -eq 2 ]; then
    if gitea_repo_exists_api "$_owner" "$_repo"; then
      gitea_mirror_set_interval_api "$_owner" "$_repo" "$_interval" 2>/dev/null || true
      gitea_sync_mirror_api "$_owner" "$_repo" && return 0
      return 0
    fi
    printf '[gitea-mirror] migrate rejected for %s/%s but repo missing — trying git push\n' \
      "$_owner" "$_repo" >&2
  fi
  if gitea_owner_is_org "$_owner"; then
    printf '[gitea-mirror] API migrate failed for org %s/%s — git push fallback disabled (orgs cannot push-to-create)\n' \
      "$_owner" "$_repo" >&2
    printf '[gitea-mirror] Set GITEA_ADMIN_TOKEN or run as root; check [migrations] in app.ini\n' >&2
    [ -n "$_local_work" ] && gitea_rm_path "$_local_work"
    return 1
  fi
  mkdir -p "$_work_dir"
  gitea_mirror_push_git "$_owner" "$_repo" "$_clone_url" "$_work_dir" || _rc=1
  gitea_rm_path "$_work_dir/${_repo}.mirror.git"
  [ -n "$_local_work" ] && gitea_rm_path "$_local_work"
  return "$_rc"
}
