#!/bin/sh
set -u

tmp="$(mktemp -d)"
export tmp
trap 'rm -rf "$tmp"' EXIT

if ! unidesk_gitea_materialize_payloads "$tmp"; then
  printf '%s\n' '{"ok":false,"error":"gitea-stdin-payload-materialization-failed","valuesPrinted":false}'
  exit 1
fi

json_tail() {
  name="$1"
  limit="${2:-2000}"
  python3 - "$tmp/$name" "$limit" <<'PY'
import json, sys
path, limit = sys.argv[1], int(sys.argv[2])
try:
    data = open(path, encoding="utf-8", errors="replace").read()[-limit:]
except FileNotFoundError:
    data = ""
print(json.dumps(data))
PY
}

capture_json() {
  name="$1"
  shift
  "$@" -o json >"$tmp/$name.json" 2>"$tmp/$name.err"
  rc=$?
  printf '%s' "$rc" >"$tmp/$name.rc"
}

upsert_webhook_sync_secret() {
  webhook_secret_rc=0
  : >"$tmp/webhook-secret.out"
  : >"$tmp/webhook-secret.err"
  if [ "$UNIDESK_GITEA_WEBHOOK_SYNC_ENABLED" != "1" ]; then
    printf '%s\n' "webhook sync disabled" >"$tmp/webhook-secret.err"
    return 0
  fi
  if [ "$UNIDESK_GITEA_DRY_RUN" = "1" ]; then
    printf '%s\n' "dry-run: webhook sync secret apply skipped" >"$tmp/webhook-secret.err"
    return 0
  fi
  github_secret_args=""
  old_ifs="$IFS"
  IFS=','
  for mapping in $UNIDESK_GITEA_GITHUB_SECRET_ENV_MAP; do
    secret_key=${mapping%%=*}
    env_name=${mapping#*=}
    eval "secret_value=\${$env_name-}"
    if [ -z "$secret_value" ]; then
      printf '%s\n' "missing GitHub credential environment for Secret key $secret_key" >"$tmp/webhook-secret.err"
      webhook_secret_rc=1
      break
    fi
    secret_file="$tmp/github-secret-$secret_key"
    printf '%s' "$secret_value" >"$secret_file"
    chmod 600 "$secret_file"
    github_secret_args="$github_secret_args --from-file=$secret_key=$secret_file"
  done
  IFS="$old_ifs"
  if [ "$webhook_secret_rc" -ne 0 ]; then
    return "$webhook_secret_rc"
  fi
  kubectl -n "$UNIDESK_GITEA_NAMESPACE" create secret generic "$UNIDESK_GITEA_WEBHOOK_SECRET_NAME" \
    $github_secret_args \
    --from-literal=gitea-username="$UNIDESK_GITEA_ADMIN_USERNAME" \
    --from-literal=gitea-password="$UNIDESK_GITEA_ADMIN_PASSWORD" \
    --from-literal=github-webhook-secret="$UNIDESK_GITEA_GITHUB_WEBHOOK_SECRET" \
    --dry-run=client -o yaml | kubectl apply --server-side --force-conflicts --field-manager="$UNIDESK_GITEA_FIELD_MANAGER" -f - >"$tmp/webhook-secret.out" 2>"$tmp/webhook-secret.err"
  webhook_secret_rc=$?
  return "$webhook_secret_rc"
}

run_apply() {
  manifest="$tmp/gitea.k8s.yaml"
  if [ "$UNIDESK_GITEA_DRY_RUN" != "1" ]; then
    kubectl create namespace "$UNIDESK_GITEA_NAMESPACE" --dry-run=client -o yaml | kubectl apply --server-side --force-conflicts --field-manager="$UNIDESK_GITEA_FIELD_MANAGER" -f - >"$tmp/namespace.out" 2>"$tmp/namespace.err"
  fi
  upsert_webhook_sync_secret
  webhook_secret_rc=$?
  timeout "$UNIDESK_GITEA_WAIT_TIMEOUT_SECONDS" kubectl apply --server-side --force-conflicts --dry-run=server \
    --field-manager="$UNIDESK_GITEA_FIELD_MANAGER" -f "$manifest" >"$tmp/server-dry-run.out" 2>"$tmp/server-dry-run.err"
  server_dry_run_rc=$?
  if [ "$webhook_secret_rc" -eq 0 ]; then
    if [ "$server_dry_run_rc" -ne 0 ]; then
      apply_rc=1
      : >"$tmp/apply.out"
      printf '%s\n' "target server dry-run failed; manifest apply skipped" >"$tmp/apply.err"
    elif [ "$UNIDESK_GITEA_DRY_RUN" = "1" ]; then
      apply_rc=0
      cp "$tmp/server-dry-run.out" "$tmp/apply.out"
      : >"$tmp/apply.err"
    else
      timeout "$UNIDESK_GITEA_WAIT_TIMEOUT_SECONDS" kubectl apply --server-side --force-conflicts \
        --field-manager="$UNIDESK_GITEA_FIELD_MANAGER" -f "$manifest" >"$tmp/apply.out" 2>"$tmp/apply.err"
      apply_rc=$?
    fi
  else
    apply_rc=1
    : >"$tmp/apply.out"
    printf '%s\n' "webhook secret apply failed; manifest apply skipped" >"$tmp/apply.err"
  fi
  if [ "$apply_rc" -eq 0 ] && [ "$UNIDESK_GITEA_WAIT" = "1" ] && [ "$UNIDESK_GITEA_DRY_RUN" != "1" ]; then
    timeout "$UNIDESK_GITEA_WAIT_TIMEOUT_SECONDS" kubectl -n "$UNIDESK_GITEA_NAMESPACE" rollout status "statefulset/$UNIDESK_GITEA_STATEFULSET_NAME" --timeout="${UNIDESK_GITEA_WAIT_TIMEOUT_SECONDS}s" >"$tmp/rollout.out" 2>"$tmp/rollout.err"
    rollout_rc=$?
  else
    rollout_rc=0
    : >"$tmp/rollout.out"
    if [ "$UNIDESK_GITEA_DRY_RUN" = "1" ]; then
      printf '%s\n' "dry-run: rollout wait skipped" >"$tmp/rollout.err"
    else
      printf '%s\n' "rollout wait not requested" >"$tmp/rollout.err"
    fi
  fi
  if [ "$UNIDESK_GITEA_DRY_RUN" != "1" ]; then
    kubectl -n "$UNIDESK_GITEA_NAMESPACE" delete deployment "$UNIDESK_GITEA_FRPC_DEPLOYMENT_NAME" --ignore-not-found >"$tmp/frpc-cleanup.out" 2>"$tmp/frpc-cleanup.err"
    frpc_cleanup_deploy_rc=$?
    kubectl -n "$UNIDESK_GITEA_NAMESPACE" delete secret "$UNIDESK_GITEA_FRPC_SECRET_NAME" --ignore-not-found >>"$tmp/frpc-cleanup.out" 2>>"$tmp/frpc-cleanup.err"
    frpc_cleanup_secret_rc=$?
    if [ "$frpc_cleanup_deploy_rc" -eq 0 ] && [ "$frpc_cleanup_secret_rc" -eq 0 ]; then
      frpc_cleanup_rc=0
    else
      frpc_cleanup_rc=1
    fi
  else
    frpc_cleanup_rc=0
    : >"$tmp/frpc-cleanup.out"
    if [ "$UNIDESK_GITEA_DRY_RUN" = "1" ]; then
      printf '%s\n' "dry-run: frpc cleanup skipped" >"$tmp/frpc-cleanup.err"
    fi
  fi
  if [ "$apply_rc" -eq 0 ] && [ "$UNIDESK_GITEA_WEBHOOK_SYNC_ENABLED" = "1" ] && [ "$UNIDESK_GITEA_DRY_RUN" != "1" ]; then
    kubectl -n "$UNIDESK_GITEA_NAMESPACE" rollout restart "deployment/$UNIDESK_GITEA_WEBHOOK_DEPLOYMENT" >"$tmp/webhook-rollout.out" 2>"$tmp/webhook-rollout.err"
    webhook_rollout_rc=$?
    if [ "$webhook_rollout_rc" -eq 0 ] && [ "$UNIDESK_GITEA_WAIT" = "1" ]; then
      timeout "$UNIDESK_GITEA_WAIT_TIMEOUT_SECONDS" kubectl -n "$UNIDESK_GITEA_NAMESPACE" rollout status "deployment/$UNIDESK_GITEA_WEBHOOK_DEPLOYMENT" --timeout="${UNIDESK_GITEA_WAIT_TIMEOUT_SECONDS}s" >>"$tmp/webhook-rollout.out" 2>>"$tmp/webhook-rollout.err"
      webhook_rollout_rc=$?
    fi
  else
    webhook_rollout_rc=0
    : >"$tmp/webhook-rollout.out"
    if [ "$UNIDESK_GITEA_DRY_RUN" = "1" ]; then
      printf '%s\n' "dry-run: webhook sync rollout skipped" >"$tmp/webhook-rollout.err"
    elif [ "$UNIDESK_GITEA_WEBHOOK_SYNC_ENABLED" = "1" ]; then
      printf '%s\n' "manifest apply failed; webhook sync rollout skipped" >"$tmp/webhook-rollout.err"
    else
      printf '%s\n' "webhook sync disabled" >"$tmp/webhook-rollout.err"
    fi
  fi
  python3 - "$webhook_secret_rc" "$server_dry_run_rc" "$apply_rc" "$rollout_rc" "$webhook_rollout_rc" "$frpc_cleanup_rc" "$tmp/frpc-cleanup.out" "$tmp/frpc-cleanup.err" "$tmp/webhook-secret.out" "$tmp/webhook-secret.err" "$tmp/server-dry-run.out" "$tmp/server-dry-run.err" "$tmp/apply.out" "$tmp/apply.err" "$tmp/rollout.out" "$tmp/rollout.err" "$tmp/webhook-rollout.out" "$tmp/webhook-rollout.err" <<'PY'
import json, os, sys
webhook_secret_rc, server_dry_run_rc, apply_rc, rollout_rc, webhook_rollout_rc, frpc_cleanup_rc = [int(value) for value in sys.argv[1:7]]
def text(path, limit=3000):
    try:
        return open(path, encoding="utf-8", errors="replace").read()[-limit:]
    except FileNotFoundError:
        return ""
dry = os.environ.get("UNIDESK_GITEA_DRY_RUN") == "1"
payload = {
    "ok": all(value == 0 for value in [webhook_secret_rc, server_dry_run_rc, apply_rc, rollout_rc, webhook_rollout_rc, frpc_cleanup_rc]),
    "target": os.environ.get("UNIDESK_GITEA_TARGET_ID"),
    "route": os.environ.get("UNIDESK_GITEA_ROUTE"),
    "namespace": os.environ.get("UNIDESK_GITEA_NAMESPACE"),
    "mode": "dry-run" if dry else "confirmed",
    "mutation": not dry,
    "image": os.environ.get("UNIDESK_GITEA_IMAGE"),
    "objects": {
        "statefulSet": os.environ.get("UNIDESK_GITEA_STATEFULSET_NAME"),
        "service": os.environ.get("UNIDESK_GITEA_SERVICE_NAME"),
        "networkPolicy": "allow-all",
    },
    "steps": {
        "legacyFrpcCleanup": {"exitCode": frpc_cleanup_rc, "stdoutTail": text(sys.argv[7]), "stderrTail": text(sys.argv[8])},
        "webhookSyncSecret": {"exitCode": webhook_secret_rc, "stdoutTail": text(sys.argv[9]), "stderrTail": text(sys.argv[10])},
        "serverDryRun": {"exitCode": server_dry_run_rc, "stdoutTail": text(sys.argv[11]), "stderrTail": text(sys.argv[12])},
        "apply": {"exitCode": apply_rc, "stdoutTail": text(sys.argv[13]), "stderrTail": text(sys.argv[14])},
        "rollout": {"exitCode": rollout_rc, "stdoutTail": text(sys.argv[15]), "stderrTail": text(sys.argv[16])},
        "webhookSyncRollout": {"exitCode": webhook_rollout_rc, "stdoutTail": text(sys.argv[17]), "stderrTail": text(sys.argv[18])},
    },
    "valuesPrinted": False,
}

print(json.dumps(payload, ensure_ascii=False, indent=2))
sys.exit(0 if payload["ok"] else 1)
PY
}

run_mirror_preflight() {
  python3 - "$tmp/repos.json" <<'PY'
import base64, json, os, subprocess, sys
repos = json.load(open(sys.argv[1], encoding="utf-8"))
rows = []
timeout = int(os.environ.get("UNIDESK_GITEA_WAIT_TIMEOUT_SECONDS", "20"))
for repo in repos:
    credential = repo.get("githubCredential") or {}
    token_env = credential.get("tokenEnv") or os.environ.get("UNIDESK_GITEA_GITHUB_DEFAULT_TOKEN_ENV", "")
    token = os.environ.get(token_env, "")
    clone_url = repo["upstream"]["cloneUrl"]
    branch = repo["upstream"]["branch"]
    if not token:
        rows.append({"key": repo["key"], "repository": repo["upstream"]["repository"], "branch": branch, "ok": False, "code": "github-credential-unavailable", "valuesPrinted": False})
        continue
    basic = base64.b64encode(("x-access-token:" + token).encode()).decode()
    try:
        completed = subprocess.run(
            ["git", "-c", "http.extraHeader=Authorization: Basic " + basic, "ls-remote", "--exit-code", clone_url, "refs/heads/" + branch],
            capture_output=True,
            text=True,
            timeout=timeout,
        )
        error = " ".join(completed.stderr.split())[-800:]
        rows.append({"key": repo["key"], "repository": repo["upstream"]["repository"], "branch": branch, "ok": completed.returncode == 0, "code": "github-upstream-available" if completed.returncode == 0 else "github-repository-not-found-or-no-access", "exitCode": completed.returncode, "errorTail": error, "valuesPrinted": False})
    except subprocess.TimeoutExpired:
        rows.append({"key": repo["key"], "repository": repo["upstream"]["repository"], "branch": branch, "ok": False, "code": "github-upstream-timeout", "valuesPrinted": False})
payload = {"ok": bool(rows) and all(row["ok"] for row in rows), "mutation": False, "repositories": rows, "valuesPrinted": False}
print(json.dumps(payload, ensure_ascii=False))
sys.exit(0 if payload["ok"] else 1)
PY
}

run_status() {
  selector="app.kubernetes.io/name=$UNIDESK_GITEA_APP_NAME,app.kubernetes.io/component=gitea"
  capture_json namespace kubectl get namespace "$UNIDESK_GITEA_NAMESPACE"
  capture_json networkpolicy kubectl -n "$UNIDESK_GITEA_NAMESPACE" get networkpolicy allow-all
  capture_json statefulset kubectl -n "$UNIDESK_GITEA_NAMESPACE" get statefulset "$UNIDESK_GITEA_STATEFULSET_NAME"
  capture_json service kubectl -n "$UNIDESK_GITEA_NAMESPACE" get service "$UNIDESK_GITEA_SERVICE_NAME"
  capture_json endpoints kubectl -n "$UNIDESK_GITEA_NAMESPACE" get endpoints "$UNIDESK_GITEA_SERVICE_NAME"
  capture_json pods kubectl -n "$UNIDESK_GITEA_NAMESPACE" get pods -l "$selector"
  capture_json pvc kubectl -n "$UNIDESK_GITEA_NAMESPACE" get pvc -l "$selector"
  capture_json events kubectl -n "$UNIDESK_GITEA_NAMESPACE" get events --sort-by=.lastTimestamp
  python3 - "$tmp" <<'PY'
import json, os, sys
tmp = sys.argv[1]
def rc(name):
    try:
        return int(open(f"{tmp}/{name}.rc", encoding="utf-8").read() or "1")
    except FileNotFoundError:
        return 1
def load(name):
    try:
        return json.load(open(f"{tmp}/{name}.json", encoding="utf-8"))
    except Exception:
        return None
def items(name):
    data = load(name)
    if isinstance(data, dict) and isinstance(data.get("items"), list):
        return data["items"]
    if isinstance(data, dict) and data.get("kind") != "Status":
        return [data]
    return []
def meta_name(item):
    return ((item or {}).get("metadata") or {}).get("name")
def pod_ready(item):
    status = (item or {}).get("status") or {}
    return any(c.get("type") == "Ready" and c.get("status") == "True" for c in status.get("conditions", []))
def pod_summary(item):
    status = (item or {}).get("status") or {}
    containers = status.get("containerStatuses") or []
    return {
        "name": meta_name(item),
        "phase": status.get("phase"),
        "ready": pod_ready(item),
        "restarts": sum(int(c.get("restartCount") or 0) for c in containers if isinstance(c, dict)),
    }
def service_summary(item):
    spec = (item or {}).get("spec") or {}
    return {
        "name": meta_name(item),
        "type": spec.get("type"),
        "clusterIP": spec.get("clusterIP"),
        "ports": [{"name": p.get("name"), "port": p.get("port"), "targetPort": p.get("targetPort")} for p in spec.get("ports", [])],
    }
def endpoint_ready(item):
    subsets = ((item or {}).get("subsets") or [])
    return any(s.get("addresses") for s in subsets if isinstance(s, dict))
def pvc_summary(item):
    spec = (item or {}).get("spec") or {}
    status = (item or {}).get("status") or {}
    return {
        "name": meta_name(item),
        "phase": status.get("phase"),
        "storageClassName": spec.get("storageClassName"),
        "capacity": (status.get("capacity") or {}).get("storage"),
    }
def event_tail(limit=8):
    result = []
    for item in items("events")[-limit:]:
        result.append({
            "type": item.get("type"),
            "reason": item.get("reason"),
            "object": f"{((item.get('involvedObject') or {}).get('kind') or '-')}/{((item.get('involvedObject') or {}).get('name') or '-')}",
            "message": (item.get("message") or "")[:300],
            "lastTimestamp": item.get("lastTimestamp") or item.get("eventTime"),
        })
    return result
sts = load("statefulset") or {}
sts_spec = sts.get("spec") or {}
sts_status = sts.get("status") or {}
desired = int(sts_spec.get("replicas") or 0)
ready_replicas = int(sts_status.get("readyReplicas") or 0)
pods = [pod_summary(item) for item in items("pods")]
service = service_summary(load("service") or {})
endpoint_ok = endpoint_ready(load("endpoints") or {})
ready = rc("namespace") == 0 and rc("networkpolicy") == 0 and rc("service") == 0 and desired > 0 and ready_replicas >= desired and endpoint_ok
payload = {
    "ok": True,
    "ready": ready,
    "target": os.environ.get("UNIDESK_GITEA_TARGET_ID"),
    "route": os.environ.get("UNIDESK_GITEA_ROUTE"),
    "namespace": os.environ.get("UNIDESK_GITEA_NAMESPACE"),
    "image": os.environ.get("UNIDESK_GITEA_IMAGE"),
    "serviceDns": f"{os.environ.get('UNIDESK_GITEA_SERVICE_NAME')}.{os.environ.get('UNIDESK_GITEA_NAMESPACE')}.svc.cluster.local:{os.environ.get('UNIDESK_GITEA_HTTP_PORT')}",
    "networkPolicy": {"allowAllPresent": rc("networkpolicy") == 0},
    "statefulSet": {"name": meta_name(sts), "desired": desired, "readyReplicas": ready_replicas, "updatedReplicas": sts_status.get("updatedReplicas")},
    "service": service,
    "endpointsReady": endpoint_ok,
    "pods": pods,
    "pvcs": [pvc_summary(item) for item in items("pvc")],
    "eventsTail": event_tail(),
    "valuesPrinted": False,
}
print(json.dumps(payload, ensure_ascii=False, indent=2))
sys.exit(0)
PY
}

run_validate() {
  proxy_path="/api/v1/namespaces/$UNIDESK_GITEA_NAMESPACE/services/http:$UNIDESK_GITEA_SERVICE_NAME:$UNIDESK_GITEA_HTTP_PORT/proxy$UNIDESK_GITEA_HEALTH_PATH"
  kubectl get --raw "$proxy_path" >"$tmp/health.out" 2>"$tmp/health.err"
  health_rc=$?
  capture_json endpoints kubectl -n "$UNIDESK_GITEA_NAMESPACE" get endpoints "$UNIDESK_GITEA_SERVICE_NAME"
  python3 - "$health_rc" "$tmp/health.out" "$tmp/health.err" "$tmp/endpoints.json" "$tmp/endpoints.rc" <<'PY'
import json, os, sys
health_rc = int(sys.argv[1])
def text(path, limit=2000):
    try:
        return open(path, encoding="utf-8", errors="replace").read()[-limit:]
    except FileNotFoundError:
        return ""
body = text(sys.argv[2], 4000)
try:
    parsed = json.loads(body)
except Exception:
    parsed = None
try:
    endpoint_rc = int(open(sys.argv[5], encoding="utf-8").read() or "1")
except Exception:
    endpoint_rc = 1
try:
    endpoints = json.load(open(sys.argv[4], encoding="utf-8"))
except Exception:
    endpoints = {}
subsets = endpoints.get("subsets") if isinstance(endpoints, dict) else []
endpoints_ready = any(isinstance(s, dict) and s.get("addresses") for s in (subsets or []))
health_status = parsed.get("status") if isinstance(parsed, dict) else None
ok = health_rc == 0 and endpoint_rc == 0 and endpoints_ready and health_status == "pass"
payload = {
    "ok": ok,
    "target": os.environ.get("UNIDESK_GITEA_TARGET_ID"),
    "route": os.environ.get("UNIDESK_GITEA_ROUTE"),
    "namespace": os.environ.get("UNIDESK_GITEA_NAMESPACE"),
    "service": os.environ.get("UNIDESK_GITEA_SERVICE_NAME"),
    "serviceProxyPath": f"/api/v1/namespaces/{os.environ.get('UNIDESK_GITEA_NAMESPACE')}/services/http:{os.environ.get('UNIDESK_GITEA_SERVICE_NAME')}:{os.environ.get('UNIDESK_GITEA_HTTP_PORT')}/proxy{os.environ.get('UNIDESK_GITEA_HEALTH_PATH')}",
    "health": {
        "exitCode": health_rc,
        "status": health_status,
        "description": parsed.get("description") if isinstance(parsed, dict) else None,
        "bodyBytes": len(body.encode("utf-8")),
        "stderrTail": text(sys.argv[3]),
    },
    "endpointsReady": endpoints_ready,
    "valuesPrinted": False,
}
print(json.dumps(payload, ensure_ascii=False, indent=2))
sys.exit(0 if ok else 1)
PY
}

service_base_url() {
  kubectl -n "$UNIDESK_GITEA_NAMESPACE" get svc "$UNIDESK_GITEA_SERVICE_NAME" -o json >"$tmp/gitea-svc.json" 2>"$tmp/gitea-svc.err"
  python3 - "$tmp/gitea-svc.json" "$UNIDESK_GITEA_HTTP_PORT" <<'PY'
import json, sys
data = json.load(open(sys.argv[1], encoding="utf-8"))
print(f"http://{data['spec']['clusterIP']}:{sys.argv[2]}")
PY
}

mirror_repos_json() {
  test -f "$tmp/repos.json"
}

admin_basic_b64() {
  python3 - <<'PY'
import base64, os
login = os.environ["UNIDESK_GITEA_ADMIN_USERNAME"]
username = login.split("@", 1)[0] if "@" in login else login
raw = f"{username}:{os.environ['UNIDESK_GITEA_ADMIN_PASSWORD']}".encode()
print(base64.b64encode(raw).decode())
PY
}

admin_cli_username() {
  python3 - <<'PY'
import os
login = os.environ["UNIDESK_GITEA_ADMIN_USERNAME"]
print(login.split("@", 1)[0] if "@" in login else login)
PY
}

run_mirror_bootstrap() {
  mirror_repos_json
  upsert_webhook_sync_secret
  webhook_secret_rc=$?
  if [ "$webhook_secret_rc" -ne 0 ]; then
    python3 - "$webhook_secret_rc" "$tmp/webhook-secret.out" "$tmp/webhook-secret.err" <<'PY'
import json, os, sys
def text(path, limit=1600):
    try:
        return open(path, encoding="utf-8", errors="replace").read()[-limit:]
    except FileNotFoundError:
        return ""
payload = {
    "ok": False,
    "target": os.environ.get("UNIDESK_GITEA_TARGET_ID"),
    "namespace": os.environ.get("UNIDESK_GITEA_NAMESPACE"),
    "steps": {
        "webhookSyncSecret": {"exitCode": int(sys.argv[1]), "stdoutTail": text(sys.argv[2]), "stderrTail": text(sys.argv[3])},
        "apiBootstrap": {"exitCode": None, "skipped": True},
    },
    "repositories": [],
    "valuesPrinted": False,
}
print(json.dumps(payload, ensure_ascii=False, indent=2))
sys.exit(1)
PY
    return 1
  fi
  pod="$(kubectl -n "$UNIDESK_GITEA_NAMESPACE" get pod -l "app.kubernetes.io/name=$UNIDESK_GITEA_APP_NAME,app.kubernetes.io/component=gitea" -o jsonpath='{.items[0].metadata.name}' 2>"$tmp/pod.err")"
  create_rc=1
  change_rc=1
  unset_rc=1
  if [ -n "$pod" ]; then
    admin_username="$(admin_cli_username)"
    admin_email="$UNIDESK_GITEA_ADMIN_USERNAME"
    case "$admin_email" in
      *@*) ;;
      *) admin_email="$admin_username@local" ;;
    esac
    kubectl -n "$UNIDESK_GITEA_NAMESPACE" exec "$pod" -- gitea --work-path /var/lib/gitea --config /etc/gitea/app.ini admin user create --username "$admin_username" --password "$UNIDESK_GITEA_ADMIN_PASSWORD" --email "$admin_email" --admin --must-change-password=false >"$tmp/admin-create.out" 2>"$tmp/admin-create.err"
    create_rc=$?
    if [ "$create_rc" -ne 0 ] && grep -qi 'user already exists' "$tmp/admin-create.err"; then
      create_rc=0
    fi
    kubectl -n "$UNIDESK_GITEA_NAMESPACE" exec "$pod" -- gitea --work-path /var/lib/gitea --config /etc/gitea/app.ini admin user change-password --username "$admin_username" --password "$UNIDESK_GITEA_ADMIN_PASSWORD" --must-change-password=false >"$tmp/admin-pass.out" 2>"$tmp/admin-pass.err"
    change_rc=$?
    kubectl -n "$UNIDESK_GITEA_NAMESPACE" exec "$pod" -- gitea --work-path /var/lib/gitea --config /etc/gitea/app.ini admin user must-change-password --unset "$admin_username" >"$tmp/admin-unset.out" 2>"$tmp/admin-unset.err"
    unset_rc=$?
  else
    printf '%s\n' "gitea pod not found" >"$tmp/admin-create.err"
    : >"$tmp/admin-create.out"
    : >"$tmp/admin-pass.out"
    : >"$tmp/admin-pass.err"
    : >"$tmp/admin-unset.out"
    : >"$tmp/admin-unset.err"
  fi

  base_url="$(service_base_url)"
  basic="$(admin_basic_b64)"
  python3 - "$base_url" "$basic" "$tmp/repos.json" "$tmp/api.json" <<'PY'
import json, sys, traceback, urllib.error, urllib.request
base_url, basic, repos_path, out_path = sys.argv[1:5]
def request(method, path, payload=None, expected=(200, 201, 204), tolerate=()):
    data = None if payload is None else json.dumps(payload).encode()
    req = urllib.request.Request(base_url + path, data=data, method=method)
    req.add_header("Authorization", "Basic " + basic)
    if payload is not None:
        req.add_header("Content-Type", "application/json")
    try:
        with urllib.request.urlopen(req, timeout=20) as resp:
            body = resp.read().decode("utf-8", errors="replace")
            return {"ok": resp.status in expected, "status": resp.status, "body": body[:1000]}
    except urllib.error.HTTPError as exc:
        body = exc.read().decode("utf-8", errors="replace")
        return {"ok": exc.code in tolerate, "status": exc.code, "body": body[:1000]}
try:
    repos = json.load(open(repos_path, encoding="utf-8"))
    orgs = {}
    repositories = []
    auth = request("GET", "/api/v1/user")
    for repo in repos:
        owner = repo["gitea"]["owner"]
        if owner not in orgs:
            orgs[owner] = {
                "create": request("POST", "/api/v1/orgs", {"username": owner, "full_name": "UniDesk internal mirrors", "visibility": "public"}, expected=(201,), tolerate=(409, 422)),
                "patch": request("PATCH", f"/api/v1/orgs/{owner}", {"full_name": "UniDesk internal mirrors", "visibility": "public"}, expected=(200,), tolerate=()),
            }
        repositories.append({
            "key": repo["key"],
            "owner": owner,
            "name": repo["gitea"]["name"],
            "create": request("POST", f"/api/v1/orgs/{owner}/repos", {"name": repo["gitea"]["name"], "private": not bool(repo["gitea"].get("publicRead")), "auto_init": False, "description": f"UniDesk controlled mirror for {repo['upstream']['repository']}"}, expected=(201,), tolerate=(409, 422)),
            "patch": request("PATCH", f"/api/v1/repos/{owner}/{repo['gitea']['name']}", {"private": not bool(repo["gitea"].get("publicRead")), "description": f"UniDesk controlled mirror for {repo['upstream']['repository']}"}, expected=(200,), tolerate=()),
        })
    payload = {"auth": auth, "orgs": orgs, "repositories": repositories}
    ok = auth.get("ok") and all(v["create"].get("ok") and v["patch"].get("ok") for v in orgs.values()) and all(item["create"].get("ok") and item["patch"].get("ok") for item in repositories)
except Exception as exc:
    payload = {
        "auth": {"ok": False},
        "orgs": {},
        "repositories": [],
        "error": str(exc),
        "traceTail": traceback.format_exc()[-1600:],
    }
    ok = False
json.dump(payload, open(out_path, "w", encoding="utf-8"), ensure_ascii=False, indent=2)
sys.exit(0 if ok else 1)
PY
  api_rc=$?
  python3 - "$webhook_secret_rc" "$create_rc" "$change_rc" "$unset_rc" "$api_rc" "$tmp/webhook-secret.out" "$tmp/webhook-secret.err" "$tmp/admin-create.out" "$tmp/admin-create.err" "$tmp/admin-pass.out" "$tmp/admin-pass.err" "$tmp/admin-unset.out" "$tmp/admin-unset.err" "$tmp/api.json" <<'PY'
import json, sys
def text(path, limit=1600):
    try:
        return open(path, encoding="utf-8", errors="replace").read()[-limit:]
    except FileNotFoundError:
        return ""
webhook_secret_rc, create_rc, change_rc, unset_rc, api_rc = map(int, sys.argv[1:6])
try:
    api = json.load(open(sys.argv[14], encoding="utf-8"))
except Exception:
    api = {}
payload = {
    "ok": webhook_secret_rc == 0 and create_rc == 0 and change_rc == 0 and unset_rc == 0 and api_rc == 0,
    "target": __import__("os").environ.get("UNIDESK_GITEA_TARGET_ID"),
    "namespace": __import__("os").environ.get("UNIDESK_GITEA_NAMESPACE"),
    "steps": {
        "webhookSyncSecret": {"exitCode": webhook_secret_rc, "stdoutTail": text(sys.argv[6]), "stderrTail": text(sys.argv[7])},
        "adminCreate": {"exitCode": create_rc, "stdoutTail": text(sys.argv[8]), "stderrTail": text(sys.argv[9])},
        "adminPassword": {"exitCode": change_rc, "stdoutTail": text(sys.argv[10]), "stderrTail": text(sys.argv[11])},
        "adminMustChangePasswordUnset": {"exitCode": unset_rc, "stdoutTail": text(sys.argv[12]), "stderrTail": text(sys.argv[13])},
        "apiBootstrap": {"exitCode": api_rc},
    },
    "api": api,
    "repositories": api.get("repositories", []),
    "valuesPrinted": False,
}
print(json.dumps(payload, ensure_ascii=False, indent=2))
sys.exit(0 if payload["ok"] else 1)
PY
}

run_mirror_sync() {
  mirror_repos_json
  base_url="$(service_base_url)"
  basic="$(admin_basic_b64)"
  python3 - "$tmp/repos.json" "$tmp/sync-plan.sh" "$tmp/repo-meta.json" "$base_url" <<'PY'
import json, shlex, sys, urllib.parse
repos = json.load(open(sys.argv[1], encoding="utf-8"))
base_url = sys.argv[4].rstrip("/")
script = []
meta = []
for i, repo in enumerate(repos):
    key = repo["key"]
    source_branch = repo["upstream"]["branch"]
    gitops_branch = repo["gitops"]["branch"]
    sync_gitops_from_upstream = repo["gitops"].get("flushDisposition") != "gitea-writeback"
    branches = [source_branch] + ([] if not sync_gitops_from_upstream or gitops_branch == source_branch or gitops_branch == "not-a-gitops-branch" else [gitops_branch])
    work = f"$tmp/repo-{i}.git"
    token_env = (repo.get("githubCredential") or {}).get("tokenEnv") or os.environ.get("UNIDESK_GITEA_GITHUB_DEFAULT_TOKEN_ENV", "")
    gitea_write_url = base_url + urllib.parse.urlparse(repo["gitea"]["readUrl"]).path
    script += [
      f"mkdir -p {shlex.quote(work)}",
      f"git init --bare {shlex.quote(work)} >$tmp/{key}.init.out 2>$tmp/{key}.init.err; printf '%s' \"$?\" >$tmp/{key}.init.rc",
      f"repo_token=$(printenv {shlex.quote(token_env)}); repo_basic=$(printf 'x-access-token:%s' \"$repo_token\" | base64 | tr -d '\\n'); git -C {shlex.quote(work)} -c http.extraHeader=\"Authorization: Basic $repo_basic\" fetch --prune {shlex.quote(repo['upstream']['cloneUrl'])} " + " ".join(shlex.quote(f"+refs/heads/{b}:refs/remotes/github/{b}") for b in branches) + f" >$tmp/{key}.fetch.out 2>$tmp/{key}.fetch.err; fetch_rc=$?; unset repo_token repo_basic; printf '%s' \"$fetch_rc\" >$tmp/{key}.fetch.rc",
      f"if [ \"$fetch_rc\" -eq 0 ]; then sha=$(git -C {shlex.quote(work)} rev-parse refs/remotes/github/{shlex.quote(source_branch)} 2>$tmp/{key}.revparse.err); revparse_rc=$?; printf '%s\\n' \"$sha\" >$tmp/{key}.revparse.out; else sha=''; revparse_rc=1; : >$tmp/{key}.revparse.out; printf '%s\\n' 'fetch failed; revparse skipped' >$tmp/{key}.revparse.err; fi; printf '%s' \"$revparse_rc\" >$tmp/{key}.revparse.rc",
      f"if [ \"$revparse_rc\" -eq 0 ]; then snapshot_ref={shlex.quote(repo['snapshot']['prefix'])}/$sha; git -C {shlex.quote(work)} -c http.extraHeader=\"$GITEA_AUTH_HEADER\" push --force {shlex.quote(gitea_write_url)} $sha:refs/heads/{shlex.quote(source_branch)} $sha:$snapshot_ref >$tmp/{key}.push.out 2>$tmp/{key}.push.err; push_rc=$?; else snapshot_ref=''; push_rc=1; : >$tmp/{key}.push.out; printf '%s\\n' 'revparse failed; push skipped' >$tmp/{key}.push.err; fi; printf '%s' \"$push_rc\" >$tmp/{key}.push.rc",
    ]
    if sync_gitops_from_upstream and gitops_branch != source_branch and gitops_branch != "not-a-gitops-branch":
      script.append(f"if [ \"$fetch_rc\" -eq 0 ]; then gitops_sha=$(git -C {shlex.quote(work)} rev-parse refs/remotes/github/{shlex.quote(gitops_branch)} 2>/dev/null || true); else gitops_sha=''; fi")
      script.append(f"if [ -n \"$gitops_sha\" ] && [ \"$push_rc\" -eq 0 ]; then git -C {shlex.quote(work)} -c http.extraHeader=\"$GITEA_AUTH_HEADER\" push --force {shlex.quote(gitea_write_url)} $gitops_sha:refs/heads/{shlex.quote(gitops_branch)} >>$tmp/{key}.push.out 2>>$tmp/{key}.push.err; gitops_push_rc=$?; else gitops_push_rc=0; fi; printf '%s' \"$gitops_push_rc\" >$tmp/{key}.gitops-push.rc")
    else:
      script.append(f"printf '%s' '0' >$tmp/{key}.gitops-push.rc")
    script.append(f"if [ \"$push_rc\" -eq 0 ]; then printf '%s %s\\n' \"$sha\" \"$snapshot_ref\" >$tmp/{key}.bundle; fi")
    legacy = repo.get("legacyGitMirror") or {}
    meta.append({"key": key, "sourceBranch": source_branch, "gitopsBranch": gitops_branch, "gitopsUpstreamSync": sync_gitops_from_upstream, "readUrl": repo["gitea"]["readUrl"], "writeUrlHost": urllib.parse.urlparse(gitea_write_url).netloc, "snapshotPrefix": repo["snapshot"]["prefix"], "legacyReadUrl": legacy.get("readUrl"), "legacyDisposition": legacy.get("disposition", "native-gitea-only")})
open(sys.argv[2], "w", encoding="utf-8").write("\n".join(script) + "\n")
json.dump(meta, open(sys.argv[3], "w", encoding="utf-8"), ensure_ascii=False, indent=2)
PY
  GITEA_AUTH_HEADER="Authorization: Basic $basic"
  export GITEA_AUTH_HEADER
  unset GIT_SSH GIT_SSH_COMMAND SSH_AUTH_SOCK
  export GIT_TERMINAL_PROMPT=0
  export GIT_CONFIG_NOSYSTEM=1
  export GIT_CONFIG_GLOBAL=/dev/null
  if [ "$UNIDESK_GITEA_GITHUB_PROXY_ENABLED" = "1" ]; then
    export https_proxy="$UNIDESK_GITEA_GITHUB_PROXY_URL"
    export http_proxy="$UNIDESK_GITEA_GITHUB_PROXY_URL"
    export no_proxy="$UNIDESK_GITEA_NO_PROXY"
  fi
  sh "$tmp/sync-plan.sh" >"$tmp/sync.out" 2>"$tmp/sync.err"
  sync_rc=$?
  python3 - "$sync_rc" "$tmp/repo-meta.json" "$tmp" <<'PY'
import json, os, sys
sync_rc = int(sys.argv[1])
meta = json.load(open(sys.argv[2], encoding="utf-8"))
tmp = sys.argv[3]
def text(path, limit=1200):
    try:
        return open(path, encoding="utf-8", errors="replace").read()[-limit:]
    except FileNotFoundError:
        return ""
def rc(path):
    try:
        return int(open(path, encoding="utf-8").read() or "1")
    except Exception:
        return 1
repos = []
bundles = []
for repo in meta:
    bundle_path = os.path.join(tmp, f"{repo['key']}.bundle")
    source_commit = None
    snapshot_ref = None
    try:
        parts = open(bundle_path, encoding="utf-8").read().strip().split()
        source_commit, snapshot_ref = parts[0], parts[1]
    except Exception:
        pass
    repos.append({
        **repo,
        "sourceCommit": source_commit,
        "snapshotRef": snapshot_ref,
        "syncOk": source_commit is not None and snapshot_ref is not None and rc(os.path.join(tmp, f"{repo['key']}.gitops-push.rc")) == 0,
        "initRc": rc(os.path.join(tmp, f"{repo['key']}.init.rc")),
        "fetchRc": rc(os.path.join(tmp, f"{repo['key']}.fetch.rc")),
        "revparseRc": rc(os.path.join(tmp, f"{repo['key']}.revparse.rc")),
        "pushRc": rc(os.path.join(tmp, f"{repo['key']}.push.rc")),
        "gitopsPushRc": rc(os.path.join(tmp, f"{repo['key']}.gitops-push.rc")),
        "fetchStdoutTail": text(os.path.join(tmp, f"{repo['key']}.fetch.out")),
        "fetchTail": text(os.path.join(tmp, f"{repo['key']}.fetch.err")),
        "pushStdoutTail": text(os.path.join(tmp, f"{repo['key']}.push.out")),
        "pushTail": text(os.path.join(tmp, f"{repo['key']}.push.err")),
        "revparseTail": text(os.path.join(tmp, f"{repo['key']}.revparse.err")),
    })
    if source_commit and snapshot_ref:
        bundles.append({"key": repo["key"], "sourceCommit": source_commit, "snapshotRef": snapshot_ref, "readUrl": repo["readUrl"], "sourceAuthority": "gitea"})
payload = {"ok": sync_rc == 0 and all(r["syncOk"] for r in repos), "repositories": repos, "sourceBundles": bundles, "valuesPrinted": False}
print(json.dumps(payload, ensure_ascii=False, indent=2))
sys.exit(0 if payload["ok"] else 1)
PY
}

run_mirror_status() {
  mirror_repos_json
  base_url="$(service_base_url)"
  basic="$(admin_basic_b64)"
  python3 - "$tmp/repos.json" "$tmp/status-plan.sh" "$tmp/repo-meta.json" "$base_url" <<'PY'
import json, shlex, sys, urllib.parse
repos = json.load(open(sys.argv[1], encoding="utf-8"))
base_url = sys.argv[4].rstrip("/")
script = []
meta = []
for repo in repos:
    key = repo["key"]
    branch = repo["upstream"]["branch"]
    gitops = repo["gitops"]["branch"]
    gitea_probe_url = base_url + urllib.parse.urlparse(repo["gitea"]["readUrl"]).path
    script.append(f"git -c http.extraHeader=\"$GITEA_AUTH_HEADER\" ls-remote {shlex.quote(gitea_probe_url)} refs/heads/{shlex.quote(branch)} '{shlex.quote(repo['snapshot']['prefix'])}/*' refs/heads/{shlex.quote(gitops)} >$tmp/{key}.ls.out 2>$tmp/{key}.ls.err")
    legacy = repo.get("legacyGitMirror") or {}
    meta.append({"key": key, "branch": branch, "gitopsBranch": gitops, "readUrl": repo["gitea"]["readUrl"], "probeUrlHost": urllib.parse.urlparse(gitea_probe_url).netloc, "snapshotPrefix": repo["snapshot"]["prefix"], "legacyReadUrl": legacy.get("readUrl"), "legacyDisposition": legacy.get("disposition", "native-gitea-only")})
open(sys.argv[2], "w", encoding="utf-8").write("\n".join(script) + "\n")
json.dump(meta, open(sys.argv[3], "w", encoding="utf-8"), ensure_ascii=False)
PY
  GITEA_AUTH_HEADER="Authorization: Basic $basic"
  export GITEA_AUTH_HEADER
  sh "$tmp/status-plan.sh" >"$tmp/status.out" 2>"$tmp/status.err"
  status_rc=$?
  python3 - "$status_rc" "$tmp/repo-meta.json" "$tmp" <<'PY'
import json, os, sys
sys.path.insert(0, sys.argv[3])
from platform_infra_gitea_status_evaluator import parse_ls_remote_lines, select_snapshot
status_rc = int(sys.argv[1])
meta = json.load(open(sys.argv[2], encoding="utf-8"))
tmp = sys.argv[3]
def text(path, limit=1200):
    try:
        return open(path, encoding="utf-8", errors="replace").read()[-limit:]
    except FileNotFoundError:
        return ""
repos = []
for repo in meta:
    try:
        with open(os.path.join(tmp, f"{repo['key']}.ls.out"), encoding="utf-8", errors="replace") as handle:
            refs = parse_ls_remote_lines(handle)
    except FileNotFoundError:
        refs = {}
    branch_ref = f"refs/heads/{repo['branch']}"
    branch_commit = refs.get(branch_ref)
    selected_snapshot = select_snapshot(branch_commit, repo["snapshotPrefix"], refs)
    current_snapshot_ready = selected_snapshot["sourceBundleReady"]
    repos.append({
        **repo,
        "branchCommit": branch_commit,
        "expectedSnapshotRef": selected_snapshot["expectedSnapshotRef"],
        "snapshotRef": selected_snapshot["snapshotRef"],
        "snapshotCommit": selected_snapshot["snapshotCommit"],
        "sourceBundleReady": current_snapshot_ready,
        "mirrorState": "ready" if current_snapshot_ready else "missing-ref",
        "errorTail": text(os.path.join(tmp, f"{repo['key']}.ls.err")),
    })
payload = {"ok": status_rc == 0 and all(r["sourceBundleReady"] for r in repos), "repositories": repos, "valuesPrinted": False}
print(json.dumps(payload, ensure_ascii=False, indent=2))
sys.exit(0 if payload["ok"] else 1)
PY
}

github_api_env() {
  if [ "$UNIDESK_GITEA_GITHUB_PROXY_ENABLED" = "1" ]; then
    export https_proxy="$UNIDESK_GITEA_GITHUB_PROXY_URL"
    export http_proxy="$UNIDESK_GITEA_GITHUB_PROXY_URL"
    export no_proxy="$UNIDESK_GITEA_NO_PROXY"
  fi
}

run_mirror_webhook_apply() {
  mirror_repos_json
  github_api_env
  python3 - "$tmp/repos.json" <<'PY'
import json, os, sys, urllib.error, urllib.request
repos = json.load(open(sys.argv[1], encoding="utf-8"))
url = os.environ["UNIDESK_GITEA_WEBHOOK_PUBLIC_URL"]
secret = os.environ["UNIDESK_GITEA_GITHUB_WEBHOOK_SECRET"]
events = [item for item in os.environ.get("UNIDESK_GITEA_WEBHOOK_EVENTS", "push").split(",") if item]
def request(method, api_path, payload=None, expected=(200, 201, 204), tolerate=()):
    data = None if payload is None else json.dumps(payload).encode()
    req = urllib.request.Request("https://api.github.com" + api_path, data=data, method=method)
    req.add_header("Authorization", "Bearer " + token)
    req.add_header("Accept", "application/vnd.github+json")
    req.add_header("X-GitHub-Api-Version", "2022-11-28")
    if payload is not None:
        req.add_header("Content-Type", "application/json")
    try:
        with urllib.request.urlopen(req, timeout=30) as resp:
            body = resp.read().decode("utf-8", errors="replace")
            return {"ok": resp.status in expected, "status": resp.status, "body": body}
    except urllib.error.HTTPError as exc:
        body = exc.read().decode("utf-8", errors="replace")
        return {"ok": exc.code in tolerate, "status": exc.code, "body": body[:1000]}
rows = []
for repo in repos:
    repository = repo["upstream"]["repository"]
    token_env = (repo.get("githubCredential") or {}).get("tokenEnv") or os.environ["UNIDESK_GITEA_GITHUB_DEFAULT_TOKEN_ENV"]
    token = os.environ[token_env]
    list_result = request("GET", f"/repos/{repository}/hooks")
    hooks = []
    try:
        hooks = json.loads(list_result.get("body") or "[]")
    except Exception:
        hooks = []
    matches = [item for item in hooks if (item.get("config") or {}).get("url") == url]
    body = {"name": "web", "active": True, "events": events, "config": {"url": url, "content_type": "json", "secret": secret, "insecure_ssl": "0"}}
    if matches:
        hook_id = matches[0].get("id")
        result = request("PATCH", f"/repos/{repository}/hooks/{hook_id}", body)
        for duplicate in matches[1:]:
            duplicate_id = duplicate.get("id")
            if duplicate_id:
                request("DELETE", f"/repos/{repository}/hooks/{duplicate_id}", expected=(204,), tolerate=(404,))
    else:
        result = request("POST", f"/repos/{repository}/hooks", body, expected=(201,))
        try:
            hook_id = json.loads(result.get("body") or "{}").get("id")
        except Exception:
            hook_id = None
    rows.append({"key": repo["key"], "repository": repository, "hookId": hook_id, "hookReady": result.get("ok") is True, "status": result.get("status"), "error": None if result.get("ok") else result.get("body", "")[:500], "valuesPrinted": False})
payload = {"ok": all(row["hookReady"] for row in rows), "repositories": rows, "url": url, "valuesPrinted": False}
print(json.dumps(payload, ensure_ascii=False, indent=2))
sys.exit(0 if payload["ok"] else 1)
PY
}

run_mirror_webhook_status() {
  mirror_repos_json
  github_api_env
  bridge_ready=$(kubectl -n "$UNIDESK_GITEA_NAMESPACE" get deploy "$UNIDESK_GITEA_WEBHOOK_DEPLOYMENT" -o jsonpath='{.status.readyReplicas}/{.spec.replicas}' 2>/dev/null || echo "0/0")
  service_exists=$(kubectl -n "$UNIDESK_GITEA_NAMESPACE" get service "$UNIDESK_GITEA_WEBHOOK_SERVICE" -o name 2>/dev/null || true)
  run_mirror_status >"$tmp/mirror-status.json" 2>"$tmp/mirror-status.err"
  mirror_status_rc=$?
  kubectl -n "$UNIDESK_GITEA_NAMESPACE" logs "deployment/$UNIDESK_GITEA_WEBHOOK_DEPLOYMENT" --tail=80 >"$tmp/bridge.log" 2>"$tmp/bridge.err"
  bridge_logs_rc=$?
  kubectl -n "$UNIDESK_GITEA_NAMESPACE" exec -i "deployment/$UNIDESK_GITEA_WEBHOOK_DEPLOYMENT" -- node - "$UNIDESK_GITEA_WEBHOOK_SERVICE_PORT" >"$tmp/inbox-status.json" 2>"$tmp/inbox-status.err" <<'NODE'
const port = process.argv[2];
fetch(`http://127.0.0.1:${port}/status`)
  .then(async (response) => process.stdout.write(await response.text()))
  .catch((error) => {
    process.stderr.write(String(error?.message || error));
    process.exitCode = 1;
  });
NODE
  inbox_status_rc=$?
  python3 - "$tmp/repos.json" "$bridge_ready" "$service_exists" "$mirror_status_rc" "$bridge_logs_rc" "$inbox_status_rc" "$tmp/mirror-status.json" "$tmp/mirror-status.err" "$tmp/bridge.log" "$tmp/bridge.err" "$tmp/inbox-status.json" "$tmp/inbox-status.err" "$tmp/hook-topology.json" <<'PY'
import hashlib, json, os, sys, urllib.error, urllib.parse, urllib.request
sys.path.insert(0, os.environ["tmp"])
from platform_infra_gitea_status_evaluator import evaluate_repository, github_hook_url, parse_github_hooks_response, select_committed_head_record, select_target_delivery
repos = json.load(open(sys.argv[1], encoding="utf-8"))
bridge_ready = sys.argv[2]
service_exists = bool(sys.argv[3])
mirror_status_rc = int(sys.argv[4])
bridge_logs_rc = int(sys.argv[5])
inbox_status_rc = int(sys.argv[6])
mirror_status_path = sys.argv[7]
mirror_status_err_path = sys.argv[8]
bridge_log_path = sys.argv[9]
bridge_log_err_path = sys.argv[10]
inbox_status_path = sys.argv[11]
inbox_status_err_path = sys.argv[12]
hook_topology_path = sys.argv[13]
url = os.environ["UNIDESK_GITEA_WEBHOOK_PUBLIC_URL"]
try:
    hook_topology = json.load(open(hook_topology_path, encoding="utf-8"))
except Exception:
    hook_topology = {}
managed_url_prefix = hook_topology.get("managedUrlPrefix") or url
desired_events = hook_topology.get("events") if isinstance(hook_topology.get("events"), list) else ["push"]
topology_repositories = hook_topology.get("repositories") if isinstance(hook_topology.get("repositories"), list) else []
def url_hash(value):
    return "sha256:" + hashlib.sha256(value.encode("utf-8")).hexdigest()[:16]
def hook_url(item):
    return github_hook_url(item)
def is_managed_url(value):
    return value == managed_url_prefix or value.startswith(managed_url_prefix.rstrip("/") + "/")
def hook_config_matches(item):
    config = item.get("config")
    if isinstance(config, str):
        return item.get("name") == "web" and item.get("active") is True and set(item.get("events") or []) == set(desired_events)
    if not isinstance(config, dict):
        return False
    return item.get("name") == "web" and item.get("active") is True and set(item.get("events") or []) == set(desired_events) and config.get("content_type") == "json" and str(config.get("insecure_ssl", "0")) == "0"
def topology_observation(repository, hooks):
    spec = next((item for item in topology_repositories if isinstance(item, dict) and item.get("repository") == repository), {})
    desired_urls = spec.get("desiredUrls") if isinstance(spec.get("desiredUrls"), list) else [url]
    managed = [item for item in hooks if item.get("name") == "web" and is_managed_url(hook_url(item))]
    desired_set = set(desired_urls)
    drift_types = []
    for desired_url in desired_urls:
        exact = [item for item in managed if hook_url(item) == desired_url]
        if not exact:
            drift_types.append("desired-url-missing")
        elif len(exact) > 1:
            drift_types.append("desired-url-duplicate")
        if exact and not hook_config_matches(exact[0]):
            drift_types.append("desired-hook-config-mismatch")
    if any(hook_url(item) not in desired_set for item in managed):
        drift_types.append("stale-managed-url")
    return {
        "ready": not drift_types,
        "desiredUrlSha256": url_hash(url),
        "desiredTopologyUrlSha256": sorted(url_hash(item) for item in desired_urls),
        "observedManagedUrlSha256": sorted(url_hash(hook_url(item)) for item in managed),
        "driftTypes": sorted(set(drift_types)),
    }
def text(path, limit=1600):
    try:
        return open(path, encoding="utf-8", errors="replace").read()[-limit:]
    except FileNotFoundError:
        return ""
def request(api_path):
    req = urllib.request.Request("https://api.github.com" + api_path, method="GET")
    req.add_header("Authorization", "Bearer " + token)
    req.add_header("Accept", "application/vnd.github+json")
    req.add_header("X-GitHub-Api-Version", "2022-11-28")
    try:
        with urllib.request.urlopen(req, timeout=30) as resp:
            return {"ok": True, "status": resp.status, "body": resp.read().decode("utf-8", errors="replace"), "link": resp.headers.get("Link")}
    except urllib.error.HTTPError as exc:
        return {"ok": False, "status": exc.code, "body": exc.read().decode("utf-8", errors="replace")[:1000], "link": exc.headers.get("Link") if exc.headers else None}
def github_head(repository, branch):
    result = request(f"/repos/{repository}/git/ref/heads/{urllib.parse.quote(branch, safe='')}")
    try:
        payload = json.loads(result.get("body") or "{}")
    except Exception:
        payload = {}
    return {"ok": result.get("ok"), "status": result.get("status"), "sha": payload.get("object", {}).get("sha"), "error": None if result.get("ok") else result.get("body", "")[:300]}
def hook_delivery_detail(repository, hook_id, delivery):
    delivery_id = delivery.get("id") if isinstance(delivery, dict) else None
    if not delivery_id:
        return {"ok": False, "error": "delivery id missing"}
    result = request(f"/repos/{repository}/hooks/{hook_id}/deliveries/{delivery_id}")
    try:
        body = json.loads(result.get("body") or "{}")
    except Exception:
        body = {}
    request_payload = body.get("request", {}).get("payload", {}) if isinstance(body.get("request"), dict) else {}
    payload_repository = request_payload.get("repository", {}) if isinstance(request_payload.get("repository"), dict) else {}
    return {
        "ok": result.get("ok"),
        "repository": payload_repository.get("full_name"),
        "ref": request_payload.get("ref"),
        "requestedCommit": request_payload.get("after"),
        "error": None if result.get("ok") else result.get("body", "")[:300],
    }
def next_delivery_cursor(link):
    if not isinstance(link, str):
        return None
    for part in link.split(","):
        if 'rel="next"' not in part:
            continue
        start = part.find("<")
        end = part.find(">", start + 1)
        if start < 0 or end < 0:
            continue
        query = urllib.parse.parse_qs(urllib.parse.urlparse(part[start + 1:end]).query)
        values = query.get("cursor") or []
        if values:
            return values[0]
    return None
def hook_deliveries(repository, expected_ref, github_head, head_delivery_id, hook_id):
    if not hook_id:
        return {"ok": False, "status": None, "latest": None, "latestPush": None, "targetPush": None, "rows": [], "error": "hook not found"}
    def compact(item):
        if not isinstance(item, dict):
            return None
        return {
            "id": str(item.get("id")) if item.get("id") is not None else None,
            "guid": str(item.get("guid")) if item.get("guid") is not None else None,
            "deliveryId": str(item.get("guid") or item.get("id") or ""),
            "event": item.get("event"),
            "status": item.get("status"),
            "statusCode": item.get("status_code"),
            "deliveredAt": item.get("delivered_at"),
            "duration": item.get("duration"),
        }
    compact_rows = []
    cursor = None
    list_error = None
    last_status = None
    history_has_more = False
    max_pages = 10 if head_delivery_id else 1
    for _ in range(max_pages):
        query = "?per_page=100" + ("&cursor=" + urllib.parse.quote(cursor, safe="") if cursor else "")
        result = request(f"/repos/{repository}/hooks/{hook_id}/deliveries{query}")
        last_status = result.get("status")
        if not result.get("ok"):
            list_error = result.get("body", "")[:300]
            break
        try:
            page_rows = json.loads(result.get("body") or "[]")
        except Exception:
            page_rows = []
        compact_rows.extend(item for item in (compact(value) for value in page_rows if isinstance(value, dict)) if item is not None)
        if head_delivery_id and any(str(item.get("deliveryId")) == str(head_delivery_id) for item in compact_rows):
            break
        cursor = next_delivery_cursor(result.get("link"))
        history_has_more = cursor is not None
        if cursor is None:
            break
    latest = compact_rows[0] if compact_rows else None
    latest_push = next((item for item in compact_rows if item.get("event") == "push"), None)
    target_push = None
    detail_error = None
    if head_delivery_id:
        delivery_candidates = [item for item in compact_rows if str(item.get("deliveryId")) == str(head_delivery_id)]
        if not delivery_candidates:
            detail_error = "committed head delivery guid not found in bounded github history"
    else:
        delivery_candidates = [item for item in compact_rows if item.get("event") == "push"][:100]
    detailed_candidates = []
    for delivery in delivery_candidates:
        detail = hook_delivery_detail(repository, hook_id, delivery)
        if not detail.get("ok"):
            detail_error = detail.get("error") or "delivery detail unavailable"
            break
        detailed = {**delivery, **{key: detail.get(key) for key in ("repository", "ref", "requestedCommit")}}
        detailed_candidates.append(detailed)
        target_push = select_target_delivery(
            detailed_candidates,
            repository,
            expected_ref,
            github_head if head_delivery_id else None,
            head_delivery_id,
        )
        if target_push is not None:
            break
    if head_delivery_id is None and target_push is None and detail_error is None:
        detail_error = "target branch delivery not found in bounded github history"
    return {
        "ok": list_error is None and detail_error is None,
        "status": last_status,
        "latest": latest,
        "latestPush": latest_push,
        "targetPush": target_push,
        "rows": compact_rows,
        "historyHasMore": history_has_more,
        "error": detail_error or list_error,
    }
def newest_delivery(rows):
    return max(rows, key=lambda item: (str(item.get("deliveredAt") or ""), str(item.get("id") or "")), default=None)
try:
    mirror_status = json.load(open(mirror_status_path, encoding="utf-8"))
except Exception:
    mirror_status = {}
mirror_by_key = {item.get("key"): item for item in mirror_status.get("repositories", []) if isinstance(item, dict)}
try:
    inbox_status = json.load(open(inbox_status_path, encoding="utf-8"))
except Exception:
    inbox_status = {}
inbox_records = [item for item in inbox_status.get("deliveries", []) if isinstance(item, dict)]
bridge_events = []
for line in text(bridge_log_path, 12000).splitlines():
    try:
        event = json.loads(line)
    except Exception:
        continue
    if isinstance(event, dict) and str(event.get("event", "")).startswith("github-to-gitea"):
        bridge_events.append({
            "event": event.get("event"),
            "repo": event.get("repo"),
            "deliveryId": event.get("deliveryId"),
            "repository": event.get("repository"),
            "ref": event.get("ref"),
            "requestedCommit": event.get("requestedCommit"),
            "ok": event.get("ok"),
            "sourceCommit": event.get("sourceCommit"),
            "authorityCommit": event.get("authorityCommit"),
            "snapshotRef": event.get("snapshotRef"),
            "disposition": event.get("disposition"),
            "errorType": event.get("errorType"),
            "error": event.get("error"),
            "syncAttempt": event.get("syncAttempt"),
            "terminal": event.get("terminal"),
            "elapsedMs": event.get("elapsedMs"),
        })
rows = []
for repo in repos:
    repository = repo["upstream"]["repository"]
    topology_spec = next((item for item in topology_repositories if isinstance(item, dict) and item.get("repository") == repository), {})
    credential_spec = repo.get("githubCredential") or {}
    token_env = credential_spec.get("tokenEnv") or topology_spec.get("githubTokenEnv") or os.environ["UNIDESK_GITEA_GITHUB_DEFAULT_TOKEN_ENV"]
    token = os.environ[token_env]
    branch = repo["upstream"]["branch"]
    result = request(f"/repos/{repository}/hooks")
    hooks_result = parse_github_hooks_response(result.get("ok"), result.get("status"), result.get("body"))
    hooks = hooks_result["hooks"]
    matches = [item for item in hooks if hook_url(item) == url]
    topology = topology_observation(repository, hooks)
    head = github_head(repository, branch)
    head_record = select_committed_head_record(repo["key"], head.get("sha"), inbox_records)
    head_delivery_id = head_record.get("deliveryId") if isinstance(head_record, dict) else None
    observations = []
    for match in matches:
        hook_id = match.get("id")
        delivery = hook_deliveries(repository, f"refs/heads/{branch}", head.get("sha"), head_delivery_id, hook_id)
        observations.append({"hook": match, "delivery": delivery})
    all_deliveries = [
        {**item, "hookId": observation["hook"].get("id")}
        for observation in observations
        for item in observation["delivery"].get("rows", [])
    ]
    push_deliveries = [item for item in all_deliveries if item.get("event") == "push"]
    latest_delivery = newest_delivery(all_deliveries)
    latest_push_delivery = newest_delivery(push_deliveries)
    target_push_deliveries = [
        {**observation["delivery"]["targetPush"], "hookId": observation["hook"].get("id")}
        for observation in observations
        if observation["delivery"].get("targetPush") is not None
    ]
    selected_push_delivery = newest_delivery(target_push_deliveries)
    selected_hook_id = selected_push_delivery.get("hookId") if selected_push_delivery else (matches[0].get("id") if matches else None)
    selected_match = next((item for item in matches if item.get("id") == selected_hook_id), None)
    topology_ready = len(matches) == 1 and matches[0].get("active") is True
    topology_reason = None if topology_ready else (
        "github-hook-missing" if len(matches) == 0
        else "github-hook-duplicate-exact-url" if len(matches) > 1
        else "github-hook-inactive"
    )
    delivery_errors = [observation["delivery"].get("error") for observation in observations if observation["delivery"].get("error")]
    mirror = mirror_by_key.get(repo["key"], {})
    hook = {
        "hookId": selected_hook_id,
        "hookReady": topology_ready,
        "active": selected_match.get("active") if selected_match else None,
        "status": result.get("status"),
        "latestDelivery": latest_delivery,
        "latestPushDelivery": latest_push_delivery,
        "selectedPushDelivery": selected_push_delivery,
        "deliverySelectionReason": ("committed-head-guid-repository-branch-after" if head_delivery_id else "latest-repository-branch-push-delivery") if selected_push_delivery else "target-branch-push-delivery-unavailable",
        "matchingHookCount": len(matches),
        "matchingHookIds": [item.get("id") for item in matches],
        "hookTopologyReason": topology_reason,
        "hookTopology": topology,
        "latest": latest_delivery,
        "latestPush": latest_push_delivery,
        "selectedPush": selected_push_delivery,
        "error": hooks_result["error"] or (delivery_errors[0] if delivery_errors else topology_reason),
    }
    rows.append(evaluate_repository(repo, hook, head, mirror, bridge_events, inbox_records, inbox_status.get("journal")))
bridge = {
    "deployment": os.environ["UNIDESK_GITEA_WEBHOOK_DEPLOYMENT"],
    "ready": bridge_ready not in ("0/0", "/") and bridge_ready.split("/")[0] == bridge_ready.split("/")[-1],
    "readyReplicas": bridge_ready,
    "serviceExists": service_exists,
    "durableInbox": inbox_status,
}
failed_delivery_recovery = inbox_status.get("failedDeliveryRecovery") if isinstance(inbox_status.get("failedDeliveryRecovery"), dict) else {"enabled": False, "ready": True}
payload = {
    "ok": bridge["ready"] and service_exists and mirror_status_rc == 0 and inbox_status_rc == 0 and inbox_status.get("ready") is True and (failed_delivery_recovery.get("enabled") is not True or failed_delivery_recovery.get("ready") is True) and all(row["hookReady"] and row.get("hookTopology", {}).get("ready") is True and row["authorityMatches"] and not row["bridgeEventStale"] for row in rows),
    "bridge": bridge,
    "repositories": rows,
    "url": url,
    "mirrorStatus": {"exitCode": mirror_status_rc, "errorTail": text(mirror_status_err_path)},
    "bridgeLogs": {"exitCode": bridge_logs_rc, "events": bridge_events[-12:], "errorTail": text(bridge_log_err_path)},
    "inboxStatus": {"exitCode": inbox_status_rc, "errorTail": text(inbox_status_err_path)},
    "valuesPrinted": False,
}
print(json.dumps(payload, ensure_ascii=False, indent=2))
sys.exit(0 if payload["ok"] else 1)
PY
}

case "$UNIDESK_GITEA_ACTION" in
  apply) run_apply ;;
  status) run_status ;;
  validate) run_validate ;;
  mirror-preflight) run_mirror_preflight ;;
  mirror-bootstrap) run_mirror_bootstrap ;;
  mirror-sync) run_mirror_sync ;;
  mirror-status) run_mirror_status ;;
  mirror-webhook-apply) run_mirror_webhook_apply ;;
  mirror-webhook-status) run_mirror_webhook_status ;;
  *) printf '{"ok":false,"error":"unsupported-gitea-remote-action"}\n'; exit 2 ;;
esac
