#!/bin/ash
#
# Server-side protection for master and next on Gitea (android_cast).
# Blocks force-push for everyone; push/merge limited to org Owners (admin, foxx).
# Side branches (feature/*, fix/*) are unaffected — no wildcard rule.
#
# Usage (BE as root):
#   sudo ./gitea_protect_branches.sh
#   GITEA_OWNER=AndroidCast GITEA_REPO=android_cast sudo ./gitea_protect_branches.sh
#   sudo ./gitea_protect_branches.sh --list
#
# Note: canonical git on FE (git://10.7.0.10/android_cast) needs its own hook —
#       see examples/git/hooks/pre-receive.android_cast.example
#
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"

GITEA_OWNER="${GITEA_OWNER:-${GITEA_ORG:-AndroidCast}}"
GITEA_REPO="${GITEA_REPO:-android_cast}"
# Space-separated branches to protect (exact names only).
GITEA_PROTECT_BRANCHES="${GITEA_PROTECT_BRANCHES:-master next}"
# Users allowed to push (fast-forward / merge) to protected branches.
GITEA_PROTECT_PUSH_USERS="${GITEA_PROTECT_PUSH_USERS:-admin foxx}"
# Org team with push access (AndroidCast Owners).
GITEA_PROTECT_PUSH_TEAM="${GITEA_PROTECT_PUSH_TEAM:-Owners}"

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

gitea_api_token() {
  _t="${GITEA_ADMIN_TOKEN:-}"
  [ -n "$_t" ] || _t="$(gitea_resolve_push_token)" || return 1
  printf '%s' "$_t"
}

gitea_list_branch_protections() {
  command -v curl >/dev/null 2>&1 || return 1
  _token="$(gitea_api_token)" || return 1
  _api="$(gitea_api_base)"
  curl -sS "$_api/repos/$GITEA_OWNER/$GITEA_REPO/branch_protections" \
    -H "Authorization: token $_token"
}

gitea_protection_exists() {
  _branch="$1"
  gitea_list_branch_protections 2>/dev/null | grep -q "\"branch_name\":\"$_branch\""
}

gitea_protect_payload() {
  _branch="$1"
  _prio="$2"
  _users_json=""
  for _u in $GITEA_PROTECT_PUSH_USERS; do
    [ -n "$_u" ] || continue
    _users_json="$_users_json\"$_u\","
  done
  _users_json="${_users_json%,}"

  cat <<EOF
{
  "rule_name": "$_branch",
  "branch_name": "$_branch",
  "priority": $_prio,
  "enable_push": true,
  "enable_push_whitelist": true,
  "push_whitelist_usernames": [$_users_json],
  "push_whitelist_teams": ["$GITEA_PROTECT_PUSH_TEAM"],
  "enable_force_push": false,
  "enable_force_push_allowlist": false,
  "block_admin_merge_override": true
}
EOF
}

gitea_apply_protection() {
  _branch="$1"
  _prio="$2"
  command -v curl >/dev/null 2>&1 || return 1
  _token="$(gitea_api_token)" || return 1
  _api="$(gitea_api_base)"
  _payload="$(gitea_protect_payload "$_branch" "$_prio")"
  _resp="$(mktemp /tmp/gitea-protect-XXXXXX.json)"

  if gitea_protection_exists "$_branch"; then
    log "update protection: $GITEA_OWNER/$GITEA_REPO::$_branch"
    _http="$(curl -sS -o "$_resp" -w '%{http_code}' \
      -X PATCH "$_api/repos/$GITEA_OWNER/$GITEA_REPO/branch_protections/$_branch" \
      -H "Authorization: token $_token" \
      -H "Content-Type: application/json" \
      -d "$_payload")"
  else
    log "create protection: $GITEA_OWNER/$GITEA_REPO::$_branch"
    _http="$(curl -sS -o "$_resp" -w '%{http_code}' \
      -X POST "$_api/repos/$GITEA_OWNER/$GITEA_REPO/branch_protections" \
      -H "Authorization: token $_token" \
      -H "Content-Type: application/json" \
      -d "$_payload")"
  fi

  case "$_http" in
    200|201|204) gitea_rm_path "$_resp"; return 0 ;;
    *)
      warn "HTTP $_http for $_branch:"
      head -c 500 "$_resp" >&2 2>/dev/null || true
      printf '\n' >&2
      gitea_rm_path "$_resp"
      return 1
      ;;
  esac
}

list_protections() {
  gitea_load_config || exit 1
  gitea_ensure_cli_access || exit 1
  log "branch protections: $GITEA_OWNER/$GITEA_REPO"
  gitea_list_branch_protections | tr ',' '\n' | grep -E 'branch_name|enable_force_push|enable_push' || true
}

# --- main ---

case "${1:-}" in
  -h|--help|help)
    sed -n '2,16p' "$0"
    exit 0
    ;;
  --list|list)
    list_protections
    exit 0
    ;;
esac

gitea_load_config || exit 1
gitea_ensure_cli_access || exit 1

if ! gitea_repo_exists_api "$GITEA_OWNER" "$GITEA_REPO"; then
  die "repo $GITEA_OWNER/$GITEA_REPO not found — run gitea_polish_project.sh first"
fi

log "repo=$GITEA_OWNER/$GITEA_REPO  branches=$GITEA_PROTECT_BRANCHES"
log "push allowlist: users=$GITEA_PROTECT_PUSH_USERS team=$GITEA_PROTECT_PUSH_TEAM"
log "force-push: disabled for all | side branches: unprotected"

_prio=1
_fail=0
for _b in $GITEA_PROTECT_BRANCHES; do
  gitea_apply_protection "$_b" "$_prio" || _fail=1
  _prio=$((_prio + 1))
done

cat <<EOF

Gitea rules applied. Verify in UI:
  $GITEA_OWNER/$GITEA_REPO → Settings → Branches

Protected: master, next only
  • No force-push (anyone)
  • Push/merge: $GITEA_PROTECT_PUSH_USERS + team $GITEA_PROTECT_PUSH_TEAM
  • Admins cannot bypass (block_admin_merge_override)

IMPORTANT — canonical origin on FE is separate:
  Install examples/git/hooks/pre-receive.android_cast.example on the bare repo
  at git://10.7.0.10/android_cast (hooks/pre-receive).

EOF

[ "$_fail" -eq 0 ]
