#!/bin/bash
# deploy.sh — ReBreak Multi-Platform Release Pipeline
#
# SUBCOMMANDS:
#   ./deploy.sh                    default: all (testflight + mdm + android)
#   ./deploy.sh testflight         iOS TestFlight via App Store Connect
#   ./deploy.sh mdm                iOS Ad-Hoc IPA + scp Upload zu MDM-Server
#   ./deploy.sh android            Android APK/AAB via Gradle + Play Console
#   ./deploy.sh all                Alle drei Targets
#
# FLAGS:
#   --no-bump                      Build-Number NICHT bumpen (Default: bump an)
#   --version X.Y.Z                Explizite Version setzen
#   --build N                      Explizite iOS Build-Nummer
#   --android-version-code N       Override Android versionCode
#   --notes "text"                  Release-Notes für diese Version (TestFlight + Play Console)
#   --skip-clean                   clean-ios.sh überspringen (iOS)
#   --skip-pods                    nur prebuild + pod install überspringen (clean-ios.sh läuft sonst)
#   --skip-validate                altool --validate-app überspringen (TF)
#   --skip-submit                  Play-Console-Submit überspringen (Android)
#   --keep-build                   Build-Artefakte NICHT löschen (Default: cleanup nach Submit)
#   --dry-run                      Alles simulieren, nichts ausführen
#   -h, --help                     Diese Hilfe anzeigen
#
# BEISPIELE:
#   # Full Release (alle Plattformen — bumpt + cleanup automatisch):
#   ./deploy.sh
#
#   # Nur Android build (kein Submit, Build behalten):
#   ./deploy.sh android --skip-submit --keep-build
#
#   # Nur iOS TestFlight mit expliziter Version (ohne Bump):
#   ./deploy.sh testflight --no-bump --version 0.4.0 --build 26
#
#   # Dry-Run zum Testen:
#   ./deploy.sh all --dry-run
#
# CREDENTIALS:
#   Persistenz (empfohlen): siehe .deploy-secrets.local.example
#     cp .deploy-secrets.local.example .deploy-secrets.local   # gitignored
#     # einmalig editieren — deploy.sh source'd das automatisch
#
#   iOS TestFlight / Ad-Hoc:
#     - ASC_API_KEY_PATH + ASC_API_KEY_ID + ASC_API_KEY_ISSUER (Pflicht)
#   iOS MDM:
#     - SSH-Access zu rebreak-mdm Server
#   Android:
#     - android/key.properties (signing)
#     - android/app/*.keystore (release keystore)
#     - PLAY_SERVICE_ACCOUNT_JSON (für --submit)

set -euo pipefail

# ERR-Trap: zeigt die exakte Zeile + Command der set -e ausgelöst hat
trap 'rc=$?; set +u; echo "" >&2; echo "✗ deploy.sh aborted (rc=$rc)" >&2; echo "   line $LINENO: $BASH_COMMAND" >&2; if [[ -n "${FUNCNAME+x}" && ${#FUNCNAME[@]} -gt 0 ]]; then echo "   call stack:" >&2; for ((i=0;i<${#FUNCNAME[@]};i++)); do echo "     #$i ${FUNCNAME[$i]} (${BASH_SOURCE[$i]}:${BASH_LINENO[$i]})" >&2; done; fi; set -u' ERR

# Ctrl+C / SIGTERM: kill background children (xcodebuild etc.) cleanly
cleanup_children() {
  local jobs_pids
  jobs_pids=$(jobs -p 2>/dev/null || true)
  if [[ -n "$jobs_pids" ]]; then
    echo "" >&2
    echo "⚠  Abbruch — beende laufende Build-Prozesse..." >&2
    # shellcheck disable=SC2086
    kill $jobs_pids 2>/dev/null || true
    sleep 0.5
    # shellcheck disable=SC2086
    kill -9 $jobs_pids 2>/dev/null || true
  fi
  exit 130
}
trap cleanup_children INT TERM

SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
APP_CONFIG="$SCRIPT_DIR/app.config.ts"
PACKAGE_JSON="$SCRIPT_DIR/package.json"
LOG_DIR="$SCRIPT_DIR/tmp/deploy-logs"
TIMESTAMP=$(date +%Y%m%d-%H%M%S)

# ═══════════════════════════════════════════════════════════════════════════
# Color Output (brew-style)
# ═══════════════════════════════════════════════════════════════════════════

if [[ -t 1 ]]; then
  BOLD=$(tput bold)
  GREEN=$(tput setaf 2)
  YELLOW=$(tput setaf 3)
  RED=$(tput setaf 1)
  BLUE=$(tput setaf 4)
  RESET=$(tput sgr0)
else
  BOLD="" GREEN="" YELLOW="" RED="" BLUE="" RESET=""
fi

log()   { echo "${BLUE}==>${RESET} ${BOLD}$*${RESET}"; }
ok()    { echo "${GREEN}✓${RESET} $*"; }
warn()  { echo "${YELLOW}⚠${RESET}  $*" >&2; }
error() { echo "${RED}✗${RESET} ${BOLD}$*${RESET}" >&2; }
die()   { error "$*"; exit 1; }

section() {
  echo ""
  echo "${BOLD}────────────────────────────────────────────────────────────${RESET}"
  echo "${BOLD}$*${RESET}"
  echo "${BOLD}────────────────────────────────────────────────────────────${RESET}"
}

run() {
  if $DRY_RUN; then
    echo "${YELLOW}[DRY-RUN]${RESET} $*"
    return 0
  else
    "$@"
  fi
}

# Runtime-Cache für Progress-Bar (lernt Dauer pro Step über Runs hinweg)
RUNTIME_CACHE="$SCRIPT_DIR/tmp/.deploy-runtimes"
mkdir -p "$(dirname "$RUNTIME_CACHE")"
runtime_lookup() {
  local label="$1"
  [[ -f "$RUNTIME_CACHE" ]] || return 1
  grep -aE "^$(printf '%s' "$label" | sed 's/[][\.*^$/]/\\&/g')\|" "$RUNTIME_CACHE" 2>/dev/null \
    | tail -1 | cut -d'|' -f2
}
runtime_save() {
  local label="$1" duration="$2"
  # Keep only last entry per label
  if [[ -f "$RUNTIME_CACHE" ]]; then
    grep -avE "^$(printf '%s' "$label" | sed 's/[][\.*^$/]/\\&/g')\|" "$RUNTIME_CACHE" > "$RUNTIME_CACHE.tmp" || true
    mv "$RUNTIME_CACHE.tmp" "$RUNTIME_CACHE"
  fi
  echo "$label|$duration" >> "$RUNTIME_CACHE"
}

# Render brew-style SINGLE-LINE progress bar:
# Layout: ==> 42% ██████░░░  (1m23s/~3m) Label ↳ subtitle
# Critical metrics (%, time) stay LEFT so they're never truncated.
render_progress() {
  local elapsed="$1" expected="$2" label="$3" subtitle="$4"
  local width=15 pct filled empty bar elapsed_h expected_h line
  if (( expected > 0 )); then
    pct=$(( elapsed * 100 / expected ))
    (( pct > 99 )) && pct=99
    filled=$(( elapsed * width / expected ))
    (( filled > width )) && filled=$width
  else
    # No baseline — animated indeterminate bar position
    pct=0
    filled=$(( RUN_QUIET_I % (width * 2) ))
    (( filled > width )) && filled=$(( width * 2 - filled ))
  fi
  empty=$(( width - filled ))
  bar=$(printf '%*s' "$filled" '' | tr ' ' '█')$(printf '%*s' "$empty" '' | tr ' ' '░')
  elapsed_h=$(format_duration "$elapsed")
  if (( expected > 0 )); then
    expected_h=$(format_duration_rounded "$expected")
    line=$(printf '%s==>%s %s%3d%%%s %s (%s/~%s) %s' \
      "$BLUE" "$RESET" "$YELLOW" "$pct" "$RESET" "$bar" "$elapsed_h" "$expected_h" "$label")
  else
    line=$(printf '%s==>%s %s(%s)%s %s %s' \
      "$BLUE" "$RESET" "$YELLOW" "$elapsed_h" "$RESET" "$bar" "$label")
  fi
  if [[ -n "$subtitle" ]]; then
    line="$line  ↳ $subtitle"
  fi
  # Truncate to terminal width to avoid line-wrap garbling the redraw
  local cols=${COLUMNS:-$(tput cols 2>/dev/null || echo 120)}
  printf '\r\033[K%s' "${line:0:cols}" >&2
}

format_duration() {
  local s="$1"
  if (( s < 60 )); then
    printf '%ds' "$s"
  else
    printf '%dm%02ds' $((s / 60)) $((s % 60))
  fi
}

# Rounded format for ETA display: nearest minute when >= 90s, half-minute when 60-89s
format_duration_rounded() {
  local s="$1"
  if (( s < 60 )); then
    printf '%ds' "$s"
  elif (( s < 90 )); then
    printf '1m'
  else
    # Round to nearest minute (>=30s rounds up)
    local m=$(( (s + 30) / 60 ))
    printf '%dm' "$m"
  fi
}

# run_quiet "Label" <log-file> <cmd...>
# Runs cmd silently with a brew-style progress bar (time-based, learns durations
# across runs). On error dumps last 40 log lines and exits. With --verbose / non-TTY:
# streams full output normally.
run_quiet() {
  local label="$1"; shift
  local logfile="$1"; shift
  if $DRY_RUN; then
    echo "${YELLOW}[DRY-RUN]${RESET} $label: $*"
    return 0
  fi
  # Debug / verbose mode: stream output directly via tee (no subshell, no spinner)
  if $VERBOSE || [[ "${RUN_QUIET_DEBUG:-0}" = "1" ]] || [[ ! -t 1 ]]; then
    log "$label"
    set +e
    "$@" 2>&1 | tee "$logfile"
    local prc=${PIPESTATUS[0]}
    set -e
    if [[ $prc -eq 0 ]]; then
      ok "$label"
    else
      error "$label fehlgeschlagen (exit $prc) — voller Log: $logfile"
      exit $prc
    fi
    return 0
  fi
  local start=$SECONDS
  local expected pid elapsed subtitle rc
  expected=$(runtime_lookup "$label" || echo 0)
  expected=${expected:-0}
  RUN_QUIET_I=0
  # Disable set -e/pipefail around backgrounding so wait can capture rc cleanly
  # (bash 3.2 on macOS aborts unexpectedly with the subshell+wait pattern under set -e)
  set +e
  "$@" >"$logfile" 2>&1 &
  pid=$!
  while kill -0 "$pid" 2>/dev/null; do
    elapsed=$((SECONDS - start))
    RUN_QUIET_I=$((RUN_QUIET_I + 1))
    subtitle=""
    if [[ -f "$logfile" ]]; then
      # Primary: meaningful build action (filtered)
      subtitle=$(tail -20 "$logfile" 2>/dev/null \
        | grep -aE '^(Compiling|CompileSwift|CompileC|Linking|Ld|Touch|CodeSign|ProcessProductPackaging|ExtractAppIntentsMetadata|Validate|Archive|GenerateAssetSymbols|CopySwiftLibs|PhaseScriptExecution|> Task|BUILD|\[CP|\[Pods)' \
        | tail -1 \
        | sed -E 's|.*/||; s|\(.*||' \
        | cut -c1-50)
      # Fallback: any last non-empty line (so user sees activity during setup/parsing)
      if [[ -z "$subtitle" ]]; then
        subtitle=$(tail -5 "$logfile" 2>/dev/null \
          | grep -av '^[[:space:]]*$' \
          | tail -1 \
          | sed -E 's|^[[:space:]]+||' \
          | cut -c1-50)
      fi
    fi
    render_progress "$elapsed" "$expected" "$label" "$subtitle"
    sleep 0.2
  done
  wait "$pid"
  rc=$?
  set -e
  elapsed=$((SECONDS - start))
  # Clear progress line
  printf '\r\033[K' >&2
  if [[ $rc -eq 0 ]]; then
    ok "$label ${YELLOW}($(format_duration "$elapsed"))${RESET}"
    runtime_save "$label" "$elapsed"
  else
    error "$label fehlgeschlagen nach $(format_duration "$elapsed") (exit $rc)"
    echo "" >&2
    echo "${BOLD}── Letzte Log-Zeilen (${logfile}) ──${RESET}" >&2
    tail -40 "$logfile" >&2
    echo "${BOLD}────────────────────────────────────${RESET}" >&2
    echo "Voller Log: $logfile" >&2
    exit $rc
  fi
}

# ═══════════════════════════════════════════════════════════════════════════
# Flag Parsing
# ═══════════════════════════════════════════════════════════════════════════

COMMAND="all"
if [[ $# -gt 0 && "$1" != -* ]]; then
  COMMAND="$1"
  shift
fi

DO_MDM=false
DO_TF=false
DO_ANDROID=false

case "$COMMAND" in
  all)        DO_MDM=true; DO_TF=true; DO_ANDROID=true ;;
  testflight|tf) DO_TF=true ;;
  mdm|adhoc)  DO_MDM=true ;;
  android)    DO_ANDROID=true ;;
  -h|--help)
    awk '/^#!/{next} /^#/{sub(/^# ?/, ""); print; next} {exit}' "$0"
    exit 0 ;;
  *)
    error "Unbekanntes Subcommand: $COMMAND"
    echo ""
    echo "Verfügbare Commands:"
    echo "  all          Alle Plattformen (testflight + mdm + android)"
    echo "  testflight   Nur iOS TestFlight"
    echo "  mdm          Nur iOS Ad-Hoc/MDM"
    echo "  android      Nur Android"
    echo ""
    echo "Nutze --help für Details"
    exit 1
    ;;
esac

# Bump default ON — Android requires new versionCode for every upload,
# TestFlight requires unique build per version. --no-bump to opt out.
BUMP_IOS=true
BUMP_ANDROID=true
EXPLICIT_VERSION=""
EXPLICIT_BUILD=""
ANDROID_VERSION_CODE_OVERRIDE=""
RELEASE_NOTES=""
SKIP_CLEAN=false
SKIP_PODS=false
SKIP_VALIDATE=false
SKIP_SUBMIT=false
KEEP_BUILD=false
DRY_RUN=false
VERBOSE=false

while [[ $# -gt 0 ]]; do
  case "$1" in
    --bump)                 shift ;;  # default on — silently accepted for backward compat
    --no-bump)              BUMP_IOS=false; BUMP_ANDROID=false; shift ;;
    --version)              EXPLICIT_VERSION="$2"; shift 2 ;;
    --build)                EXPLICIT_BUILD="$2"; shift 2 ;;
    --android-version-code) ANDROID_VERSION_CODE_OVERRIDE="$2"; shift 2 ;;
    --notes)                RELEASE_NOTES="$2"; shift 2 ;;
    --skip-clean)           SKIP_CLEAN=true; shift ;;
    --skip-pods)            SKIP_PODS=true; shift ;;
    --skip-validate)        SKIP_VALIDATE=true; shift ;;
    --skip-submit)          SKIP_SUBMIT=true; shift ;;
    --keep-build)           KEEP_BUILD=true; shift ;;
    --dry-run)              DRY_RUN=true; shift ;;
    -v|--verbose)           VERBOSE=true; shift ;;
    -h|--help)
      awk '/^#!/{next} /^#/{sub(/^# ?/, ""); print; next} {exit}' "$0"
      exit 0 ;;
    *) die "Unbekannter Flag: $1 (--help für Hilfe)" ;;
  esac
done

# Disk-Guard: warnt bei wenig freiem Speicher, bevor (große) Builds starten.
[ -x "$SCRIPT_DIR/clean.sh" ] && "$SCRIPT_DIR/clean.sh" --guard 8 || true

# ═══════════════════════════════════════════════════════════════════════════
# Secrets-File auto-loading (NICHT committen — siehe .deploy-secrets.local.example)
# ═══════════════════════════════════════════════════════════════════════════
# Lädt automatisch:
#   apps/rebreak-native/.deploy-secrets.local   (lokal, gitignored)
#   ~/.config/rebreak/deploy.env                 (global fallback, optional)
# Hinweis: Datei heißt bewusst NICHT .env.* — sonst greift Metro's File-Watcher
# zu und versucht die Shell-Exports als JS zu parsen (SyntaxError im Bundle).
for secrets_file in "$SCRIPT_DIR/.deploy-secrets.local" "$HOME/.config/rebreak/deploy.env"; do
  if [[ -f "$secrets_file" ]]; then
    # shellcheck disable=SC1090
    set -a; source "$secrets_file"; set +a
    log "Secrets geladen aus: $secrets_file"
    break
  fi
done

# ═══════════════════════════════════════════════════════════════════════════
# ENV & Paths
# ═══════════════════════════════════════════════════════════════════════════

REBREAK_TEAM_ID="${REBREAK_TEAM_ID:-84BQ7MTFYK}"
MDM_SERVER="${MDM_SERVER:-rebreak-mdm}"
INSTALL_BASE_URL="${INSTALL_BASE_URL:-https://mdm.rebreak.org/install}"
export REBREAK_ENABLE_FAMILY_CONTROLS="${REBREAK_ENABLE_FAMILY_CONTROLS:-1}"
export EXPO_PUBLIC_ENABLE_DEBUG="${EXPO_PUBLIC_ENABLE_DEBUG:-0}"

IOS_DIR="$SCRIPT_DIR/ios"
ANDROID_DIR="$SCRIPT_DIR/android"
ARCHIVE_PATH="/tmp/Rebreak.xcarchive"
ADHOC_EXPORT_DIR="/tmp/Rebreak-ipa"
TF_EXPORT_DIR="/tmp/Rebreak-tf"
ADHOC_IPA="$ADHOC_EXPORT_DIR/Rebreak.ipa"
TF_IPA="$TF_EXPORT_DIR/Rebreak.ipa"
ADHOC_EXPORT_OPTIONS="$SCRIPT_DIR/build-config/exportOptions-adhoc.plist"
TF_EXPORT_OPTIONS="$SCRIPT_DIR/build-config/exportOptions-tf.plist"
WORKSPACE="$IOS_DIR/ReBreak.xcworkspace"
SCHEME="ReBreak"

APPLE_ID_EMAIL="${APPLE_ID_EMAIL:-chahinebrini@gmail.com}"
ASC_API_KEY_PATH="${ASC_API_KEY_PATH:-}"
ASC_API_KEY_ID="${ASC_API_KEY_ID:-}"
ASC_API_KEY_ISSUER="${ASC_API_KEY_ISSUER:-}"

# Stoppt laufende Gradle-Daemons + entfernt android-Build-Output, BEVOR ein
# 'expo prebuild --clean' android/ löschen will. Ein Daemon (von Builds, lebt ~3h)
# hält sonst android/build|.gradle offen → 'rmdir android' failt mit ENOTEMPTY →
# android/ wird nur halb gewischt (Source weg, gelocktes build/ bleibt). Idempotent.
release_android_locks() {
  if [[ -x "$ANDROID_DIR/gradlew" ]]; then
    ( cd "$ANDROID_DIR" && ./gradlew --stop >/dev/null 2>&1 ) || true
  fi
  pkill -f "GradleDaemon" 2>/dev/null || true
  rm -rf "$ANDROID_DIR/build" "$ANDROID_DIR/.gradle" "$ANDROID_DIR/app/build" 2>/dev/null || true
}

# Stellt sicher dass ios/ oder android/ ein VOLLSTÄNDIGES Prebuild ist — sonst
# Auto-(Re)Prebuild (fehlt komplett → prebuild; halb-gewischt → prebuild --clean).
# Usage: ensure_native_dir ios   |   ensure_native_dir android
# Immer --platform <p>, damit der jeweils andere Ordner unangetastet bleibt.
ensure_native_dir() {
  local platform="$1"
  local target_dir marker
  case "$platform" in
    # Marker = eine Datei die NUR ein vollständiges Prebuild hat (nicht der reine
    # build/-Output). Damit erkennen wir auch einen halb-gewischten Ordner.
    ios)     target_dir="$IOS_DIR";     marker="$IOS_DIR/Podfile" ;;
    android) target_dir="$ANDROID_DIR"; marker="$ANDROID_DIR/app/build.gradle" ;;
    *) die "ensure_native_dir: unbekannte Plattform '$platform'" ;;
  esac

  # Self-Healing: NICHT nur prüfen ob der Ordner da ist (-d), sondern ob er ein
  # VOLLSTÄNDIGES Prebuild ist. Ein halb-gewischter $platform/ (nur build/-Output,
  # keine Gradle-/Podfile-Projektdateien) trickste die alte -d-Prüfung aus → der
  # Release-Build failte mit "autolinking.json doesn't exist". Marker-Datei prüfen.
  if [[ -d "$target_dir" && -f "$marker" ]]; then
    return 0
  fi

  if [[ -d "$target_dir" && ! -f "$marker" ]]; then
    warn "$platform/ ist unvollständig (Marker $(basename "$marker") fehlt) — regeneriere via prebuild --clean"
    # Vor dem android-Clean Gradle-Daemons stoppen + Build-Output entfernen, sonst
    # kann 'prebuild --clean' am gelockten android/build mit ENOTEMPTY scheitern.
    [[ "$platform" == "android" ]] && release_android_locks
    # --clean wischt + regeneriert NUR $platform/ (die andere Plattform bleibt
    # unangetastet) und räumt den kaputten Halb-Zustand sauber auf.
    run_quiet "expo prebuild --clean ($platform)" "$LOG_DIR/prebuild-$platform-$TIMESTAMP.log" \
      pnpm exec expo prebuild --platform "$platform" --clean --no-install
  else
    warn "$platform/ fehlt — führe 'expo prebuild --platform $platform' automatisch aus"
    run_quiet "expo prebuild ($platform)" "$LOG_DIR/prebuild-$platform-$TIMESTAMP.log" \
      pnpm exec expo prebuild --platform "$platform" --no-install
  fi

  [[ -f "$marker" ]] || die "$platform/ nach prebuild immer noch unvollständig (kein $(basename "$marker"))"
  ok "$platform/ regeneriert"
}

# Build xcodebuild auth-args (ASC API-Key enables automatic cert/profile download)
xcodebuild_auth_args() {
  if [[ -n "$ASC_API_KEY_PATH" && -n "$ASC_API_KEY_ID" && -n "$ASC_API_KEY_ISSUER" ]]; then
    echo "-allowProvisioningUpdates -authenticationKeyPath $ASC_API_KEY_PATH -authenticationKeyID $ASC_API_KEY_ID -authenticationKeyIssuerID $ASC_API_KEY_ISSUER"
  fi
}

# Preflight check for ASC API-Key — fails fast with clear message before xcodebuild starts
require_asc_api_key() {
  local missing=()
  [[ -n "$ASC_API_KEY_ID" ]]     || missing+=("ASC_API_KEY_ID")
  [[ -n "$ASC_API_KEY_ISSUER" ]] || missing+=("ASC_API_KEY_ISSUER")
  [[ -n "$ASC_API_KEY_PATH" ]]   || missing+=("ASC_API_KEY_PATH")
  if (( ${#missing[@]} > 0 )); then
    die "iOS Signing braucht ASC API-Key. Fehlt: ${missing[*]}
→ Editiere apps/rebreak-native/.deploy-secrets.local (siehe .deploy-secrets.local.example)"
  fi
  if [[ ! -f "$ASC_API_KEY_PATH" ]]; then
    die "ASC API-Key Datei existiert nicht: $ASC_API_KEY_PATH
→ Lade .p8 von https://appstoreconnect.apple.com/access/integrations/api
→ Lege ab unter: $ASC_API_KEY_PATH"
  fi
}

PLAY_SERVICE_ACCOUNT_JSON="${PLAY_SERVICE_ACCOUNT_JSON:-$HOME/secrets/rebreak-play-service-account.json}"

mkdir -p "$LOG_DIR" 2>/dev/null || true

# ═══════════════════════════════════════════════════════════════════════════
# Helpers
# ═══════════════════════════════════════════════════════════════════════════

get_current_version() {
  grep -E '"version"' "$PACKAGE_JSON" | head -1 \
    | sed -E 's/[^"]*"version"[^"]*"([^"]+)".*/\1/' || echo "0.0.0"
}

get_current_build_number() {
  grep -E 'buildNumber:' "$APP_CONFIG" \
    | sed -E 's/[^:]*:[^"]*"([0-9]+)".*/\1/' || echo "0"
}

get_current_version_code() {
  grep -E 'versionCode:' "$APP_CONFIG" \
    | sed -E 's/[^:]*:[^0-9]*([0-9]+).*/\1/' || echo "0"
}

bump_ios_version() {
  log "iOS Version Bump..."
  
  local current_version
  current_version=$(get_current_version)
  local current_build
  current_build=$(get_current_build_number)
  
  local new_version="$current_version"
  local new_build
  
  if [[ -n "$EXPLICIT_VERSION" ]]; then
    new_version="$EXPLICIT_VERSION"
  fi
  
  if [[ -n "$EXPLICIT_BUILD" ]]; then
    new_build="$EXPLICIT_BUILD"
  else
    new_build=$((current_build + 1))
  fi
  
  echo "  $current_version (Build $current_build) → $new_version (Build $new_build)"
  
  if ! $DRY_RUN; then
    # Update package.json version
    if [[ "$new_version" != "$current_version" ]]; then
      if [[ "$(uname)" == "Darwin" ]]; then
        sed -i '' "s/\"version\": \"$current_version\"/\"version\": \"$new_version\"/" "$PACKAGE_JSON"
      else
        sed -i "s/\"version\": \"$current_version\"/\"version\": \"$new_version\"/" "$PACKAGE_JSON"
      fi
    fi
    
    # Update buildNumber in app.config.ts
    if [[ "$(uname)" == "Darwin" ]]; then
      sed -i '' "s/buildNumber: \"$current_build\"/buildNumber: \"$new_build\"/" "$APP_CONFIG"
    else
      sed -i "s/buildNumber: \"$current_build\"/buildNumber: \"$new_build\"/" "$APP_CONFIG"
    fi
    
    # Update Extension Info.plists
    local ext_plists=(
      "$SCRIPT_DIR/modules/rebreak-protection/ios/RebreakContentFilter/Info.plist"
      "$SCRIPT_DIR/modules/rebreak-protection/ios/RebreakPacketTunnelExtension/Info.plist"
      "$SCRIPT_DIR/modules/rebreak-protection/ios/RebreakURLFilterExtension/Info.plist"
    )
    for plist in "${ext_plists[@]}"; do
      if [[ -f "$plist" ]]; then
        /usr/libexec/PlistBuddy -c "Set :CFBundleShortVersionString $new_version" "$plist" 2>/dev/null || true
        /usr/libexec/PlistBuddy -c "Set :CFBundleVersion $new_build" "$plist" 2>/dev/null || true
      fi
    done
    
    ok "iOS Version aktualisiert"
  fi
}

bump_android_version() {
  log "Android Version Bump..."

  # WICHTIG: Gradle liest versionCode/versionName aus android/app/build.gradle —
  # NICHT aus app.config.ts. Frühere Bumps seddeten nur app.config → verpufften
  # (der AAB blieb auf dem alten build.gradle-Stand → Play "already submitted").
  # Daher ist build.gradle hier die Quelle der Wahrheit; app.config + package.json
  # werden nur synchron mitgezogen (Konsistenz, falls mal prebuild läuft).
  local BUILD_GRADLE="$ANDROID_DIR/app/build.gradle"
  [[ -f "$BUILD_GRADLE" ]] || die "build.gradle nicht gefunden: $BUILD_GRADLE"

  local current_vc current_vn
  current_vc=$(grep -E 'versionCode +[0-9]+' "$BUILD_GRADLE" | head -1 | grep -oE '[0-9]+' | head -1)
  current_vn=$(grep -E 'versionName +"' "$BUILD_GRADLE" | head -1 | sed -E 's/.*versionName +"([^"]+)".*/\1/')

  local new_vc new_vn
  if [[ -n "$ANDROID_VERSION_CODE_OVERRIDE" ]]; then
    new_vc="$ANDROID_VERSION_CODE_OVERRIDE"
  else
    new_vc=$((current_vc + 1))    # Play-Pflicht: versionCode MUSS bei jedem Upload steigen
  fi

  if [[ -n "$EXPLICIT_VERSION" ]]; then
    new_vn="$EXPLICIT_VERSION"     # --version Override (für Minor/Major-Releases)
  else
    # Default: Patch-Segment +1 (x.y.z → x.y.(z+1)). Minor/Major bewusst manuell.
    local major minor patch
    IFS='.' read -r major minor patch <<< "$current_vn"
    new_vn="${major:-0}.${minor:-0}.$(( ${patch:-0} + 1 ))"
  fi

  echo "  versionCode: $current_vc → $new_vc"
  echo "  versionName: $current_vn → $new_vn"

  if ! $DRY_RUN; then
    local SED=(sed -i)
    [[ "$(uname)" == "Darwin" ]] && SED=(sed -i '')

    # 1) build.gradle — die Quelle, die Gradle wirklich liest
    "${SED[@]}" -E "s/versionCode +[0-9]+/versionCode $new_vc/" "$BUILD_GRADLE"
    "${SED[@]}" -E "s/versionName +\"[^\"]+\"/versionName \"$new_vn\"/" "$BUILD_GRADLE"
    # 2) app.config.ts — versionCode synchron halten
    "${SED[@]}" -E "s/versionCode: [0-9]+,/versionCode: $new_vc,/" "$APP_CONFIG"
    # 3) package.json — versionName-Quelle für app.config (version: pkg.version) + iOS
    "${SED[@]}" -E "s/\"version\": \"[^\"]+\"/\"version\": \"$new_vn\"/" "$PACKAGE_JSON"

    ok "Android Version aktualisiert: $new_vn (versionCode $new_vc) → build.gradle + app.config + package.json"
  fi
}

# ═══════════════════════════════════════════════════════════════════════════
# Release Notes Collection
# ═══════════════════════════════════════════════════════════════════════════

collect_release_notes() {
  # Priority: --notes flag > NEXT_RELEASE.md > interactive prompt
  local next_release_file="$SCRIPT_DIR/NEXT_RELEASE.md"
  
  if [[ -n "$RELEASE_NOTES" ]]; then
    log "Release-Notes: aus --notes Flag"
    return
  fi
  
  if [[ -f "$next_release_file" ]] && [[ -s "$next_release_file" ]]; then
    RELEASE_NOTES="$(cat "$next_release_file")"
    log "Release-Notes: aus $next_release_file"
    return
  fi
  
  # Interactive prompt (nur wenn TTY)
  if [[ -t 0 ]] && [[ -t 1 ]] && ! $DRY_RUN; then
    echo ""
    echo "${BOLD}Release-Notes für diese Version (optional):${RESET}"
    echo "${YELLOW}Tipp: Multi-line mit Strg-D beenden, oder ENTER für skip${RESET}"
    echo ""
    
    # Read multi-line input until EOF (Ctrl-D) or empty line
    local input_lines=()
    local line
    while IFS= read -r line; do
      if [[ -z "$line" ]] && [[ ${#input_lines[@]} -eq 0 ]]; then
        # First line empty = skip
        break
      fi
      input_lines+=("$line")
    done
    
    if [[ ${#input_lines[@]} -gt 0 ]]; then
      RELEASE_NOTES="$(printf '%s\n' "${input_lines[@]}")"
      log "Release-Notes: interaktiv erfasst"
    fi
  fi
}

archive_release_notes_to_changelog() {
  [[ -z "$RELEASE_NOTES" ]] && return
  
  local changelog="$SCRIPT_DIR/CHANGELOG.md"
  local version build version_code
  version="$(get_current_version)"
  build="$(get_current_build_number)"
  version_code="$(get_current_version_code)"
  local date_stamp
  date_stamp="$(date +%Y-%m-%d)"
  
  local header="## v${version} (Build ${build} / versionCode ${version_code}) — ${date_stamp}"
  local entry="${header}\n\n${RELEASE_NOTES}\n"
  
  if [[ ! -f "$changelog" ]]; then
    # Create new CHANGELOG.md with header
    echo "# Changelog" > "$changelog"
    echo "" >> "$changelog"
    echo "Alle wichtigen Änderungen an diesem Projekt werden in dieser Datei dokumentiert." >> "$changelog"
    echo "" >> "$changelog"
  fi
  
  # Prepend new entry after header (assumes "# Changelog" on line 1)
  if $DRY_RUN; then
    log "[DRY-RUN] Würde Release-Notes in $changelog archivieren"
  else
    # Use temporary file to prepend
    local temp_file
    temp_file="$(mktemp)"
    {
      head -3 "$changelog" 2>/dev/null || echo -e "# Changelog\n"
      echo "$entry"
      tail -n +4 "$changelog" 2>/dev/null || true
    } > "$temp_file"
    mv "$temp_file" "$changelog"
    ok "Release-Notes in $changelog archiviert"
  fi
  
  # Clear NEXT_RELEASE.md if it was used
  local next_release_file="$SCRIPT_DIR/NEXT_RELEASE.md"
  if [[ -f "$next_release_file" ]] && ! $DRY_RUN; then
    rm "$next_release_file"
    ok "NEXT_RELEASE.md geleert"
  fi
}

# ═══════════════════════════════════════════════════════════════════════════
# iOS Ad-Hoc / MDM Pipeline
# ═══════════════════════════════════════════════════════════════════════════

deploy_mdm() {
  section "iOS Ad-Hoc (MDM)"
  
  # Preflight
  command -v xcodebuild >/dev/null 2>&1 || die "xcodebuild nicht gefunden"
  command -v ssh >/dev/null 2>&1 || die "ssh nicht gefunden"
  command -v scp >/dev/null 2>&1 || die "scp nicht gefunden"
  [[ -f "$ADHOC_EXPORT_OPTIONS" ]] || die "ExportOptions nicht gefunden: $ADHOC_EXPORT_OPTIONS"
  ensure_native_dir ios
  require_asc_api_key
  
  log "Prüfe SSH-Verbindung zu $MDM_SERVER..."
  if ! ssh -o ConnectTimeout=10 -o BatchMode=yes "$MDM_SERVER" "echo ok" >/dev/null 2>&1; then
    die "SSH zu $MDM_SERVER fehlgeschlagen — VPN oder SSH-Key prüfen"
  fi
  ok "SSH OK"
  
  # Clean
  if ! $SKIP_CLEAN; then
    log "Clean iOS..."
    local clean_args=(--quiet)
    $SKIP_PODS && clean_args+=(--skip-pods)
    run "$SCRIPT_DIR/clean-ios.sh" "${clean_args[@]}"
  fi
  
  # Archive
  rm -rf "$ARCHIVE_PATH"
  # shellcheck disable=SC2046
  run_quiet "Building xcarchive" "$LOG_DIR/mdm-archive-$TIMESTAMP.log" \
    xcodebuild archive \
    -workspace "$WORKSPACE" \
    -scheme "$SCHEME" \
    -configuration Release \
    -archivePath "$ARCHIVE_PATH" \
    -destination 'generic/platform=iOS' \
    DEVELOPMENT_TEAM="$REBREAK_TEAM_ID" \
    $(xcodebuild_auth_args)
  
  ok "xcarchive fertig: $ARCHIVE_PATH"
  
  # Export IPA
  rm -rf "$ADHOC_EXPORT_DIR"
  # shellcheck disable=SC2046
  run_quiet "Exporting Ad-Hoc IPA" "$LOG_DIR/mdm-export-$TIMESTAMP.log" \
    xcodebuild -exportArchive \
    -archivePath "$ARCHIVE_PATH" \
    -exportPath "$ADHOC_EXPORT_DIR" \
    -exportOptionsPlist "$ADHOC_EXPORT_OPTIONS" \
    $(xcodebuild_auth_args)
  
  [[ -f "$ADHOC_IPA" ]] || die "IPA nicht erzeugt: $ADHOC_IPA"
  ok "IPA exportiert: $ADHOC_IPA"
  
  # Upload
  log "Uploading zu $MDM_SERVER..."
  run scp "$ADHOC_IPA" "$MDM_SERVER:/opt/nanomdm/install/Rebreak.ipa"
  run scp "$ADHOC_EXPORT_DIR/manifest.plist" "$MDM_SERVER:/opt/nanomdm/install/manifest.plist"
  
  ok "MDM-Deploy abgeschlossen"
  echo ""
  echo "  Install-URL: $INSTALL_BASE_URL/manifest.plist"
  echo "  Server-seitiger systemd path-watcher triggert MDM-Push automatisch"
}

# ═══════════════════════════════════════════════════════════════════════════
# iOS TestFlight Pipeline
# ═══════════════════════════════════════════════════════════════════════════

deploy_testflight() {
  section "iOS TestFlight"
  
  # Preflight
  command -v xcodebuild >/dev/null 2>&1 || die "xcodebuild nicht gefunden"
  command -v xcrun >/dev/null 2>&1 || die "xcrun nicht gefunden"
  [[ -f "$TF_EXPORT_OPTIONS" ]] || die "ExportOptions nicht gefunden: $TF_EXPORT_OPTIONS"
  require_asc_api_key
  
  # Auth — require_asc_api_key bereits im Preflight oben gelaufen
  log "Auth: ASC API-Key ($ASC_API_KEY_ID)"
  
  # Archive lokalisieren
  local USED_ARCHIVE="$ARCHIVE_PATH"
  if [[ ! -d "$USED_ARCHIVE" ]]; then
    # Fallback: neuestes Xcode-Archive
    USED_ARCHIVE=$(find ~/Library/Developer/Xcode/Archives -name "ReBreak*.xcarchive" -type d 2>/dev/null \
      | sort -r | head -1 || true)
    if [[ -z "$USED_ARCHIVE" ]]; then
      die "Kein xcarchive gefunden.
Entweder:
  1. ./deploy.sh mdm zuerst ausführen (erzeugt $ARCHIVE_PATH)
  2. Oder: ./deploy.sh all (baut MDM + TF in einem Lauf)"
    fi
    log "Auto-detect: $USED_ARCHIVE"
  else
    log "Verwende Archive: $USED_ARCHIVE"
  fi
  
  # Export IPA
  rm -rf "$TF_EXPORT_DIR"
  # shellcheck disable=SC2046
  run_quiet "Exporting App-Store IPA" "$LOG_DIR/tf-export-$TIMESTAMP.log" \
    xcodebuild -exportArchive \
    -archivePath "$USED_ARCHIVE" \
    -exportPath "$TF_EXPORT_DIR" \
    -exportOptionsPlist "$TF_EXPORT_OPTIONS" \
    $(xcodebuild_auth_args)
  
  [[ -f "$TF_IPA" ]] || die "IPA nicht erzeugt: $TF_IPA"
  ok "IPA exportiert: $TF_IPA"
  
  # Validate
  if ! $SKIP_VALIDATE; then
    run_quiet "Validating IPA (App-Store Connect)" "$LOG_DIR/tf-validate-$TIMESTAMP.log" \
      xcrun altool --validate-app \
      -f "$TF_IPA" \
      -t ios \
      --apiKey "$ASC_API_KEY_ID" \
      --apiIssuer "$ASC_API_KEY_ISSUER"
  fi
  
  # Upload
  run_quiet "Uploading zu App-Store Connect (TestFlight)" "$LOG_DIR/tf-upload-$TIMESTAMP.log" \
    xcrun altool --upload-app \
    -f "$TF_IPA" \
    -t ios \
    --apiKey "$ASC_API_KEY_ID" \
    --apiIssuer "$ASC_API_KEY_ISSUER"
  
  ok "TestFlight-Deploy abgeschlossen"
  echo ""
  echo "  IPA erscheint automatisch in Internal Testing"
  echo "  Status: https://appstoreconnect.apple.com"
}

# ═══════════════════════════════════════════════════════════════════════════
# Android Pipeline
# ═══════════════════════════════════════════════════════════════════════════

deploy_android() {
  section "Android Release"
  
  # Preflight
  ensure_native_dir android
  
  local KEYSTORE_PROPS="$ANDROID_DIR/key.properties"

  # Auto-Restore: prebuild --clean wischt android/ — die Master-Files leben
  # in build-config/android-signing/ (gitignored) und werden hier zurückkopiert.
  local SIGNING_MASTER="$SCRIPT_DIR/build-config/android-signing"
  if [[ -d "$SIGNING_MASTER" ]]; then
    if [[ -f "$SIGNING_MASTER/key.properties" && ! -f "$KEYSTORE_PROPS" ]]; then
      cp "$SIGNING_MASTER/key.properties" "$KEYSTORE_PROPS"
      log "key.properties aus build-config/android-signing/ restauriert"
    fi
    local jks
    for jks in "$SIGNING_MASTER"/*.jks "$SIGNING_MASTER"/*.keystore; do
      [[ -f "$jks" ]] || continue
      local target="$ANDROID_DIR/app/$(basename "$jks")"
      if [[ ! -f "$target" ]]; then
        cp "$jks" "$target"
        log "$(basename "$jks") nach android/app/ restauriert"
      fi
    done
  fi

  if [[ ! -f "$KEYSTORE_PROPS" ]]; then
    error "Android Signing nicht konfiguriert"
    echo ""
    echo "Fehlt: $KEYSTORE_PROPS"
    echo ""
    echo "Einmalig-Setup (überlebt prebuild --clean):"
    echo ""
    echo "1. Master-Folder anlegen:"
    echo "   mkdir -p $SIGNING_MASTER"
    echo ""
    echo "2. Keystore reinkopieren:"
    echo "   cp <pfad-zur-jks> $SIGNING_MASTER/rebreak-release.jks"
    echo ""
    echo "3. key.properties dort anlegen:"
    echo "   cat > $SIGNING_MASTER/key.properties << EOF"
    echo "storePassword=<dein-password>"
    echo "keyPassword=<dein-password>"
    echo "keyAlias=<dein-alias>"
    echo "storeFile=rebreak-release.jks"
    echo "EOF"
    echo ""
    echo "→ deploy.sh restauriert ab dann automatisch nach android/ bei jedem Run."
    echo "   build-config/android-signing/ ist in .gitignore — niemals committen."
    exit 1
  fi
  
  log "Keystore-Config gefunden: $KEYSTORE_PROPS"

  # Android SDK: ANDROID_HOME env oder Standard-macOS-Pfad. Auch local.properties
  # automatisch erzeugen, damit gradle ohne env-export funktioniert.
  if [[ -z "${ANDROID_HOME:-}" ]]; then
    if [[ -d "$HOME/Library/Android/sdk" ]]; then
      export ANDROID_HOME="$HOME/Library/Android/sdk"
      log "ANDROID_HOME auto-detected: $ANDROID_HOME"
    else
      die "ANDROID_HOME nicht gesetzt und SDK nicht in ~/Library/Android/sdk gefunden — Android Studio installieren oder ANDROID_HOME setzen"
    fi
  fi
  if [[ ! -f "$ANDROID_DIR/local.properties" ]]; then
    echo "sdk.dir=$ANDROID_HOME" > "$ANDROID_DIR/local.properties"
    log "android/local.properties erzeugt"
  fi

  # Build
  run_quiet "Building Release AAB (gradlew bundleRelease)" \
    "$LOG_DIR/android-build-$TIMESTAMP.log" \
    bash -c "cd $ANDROID_DIR && ANDROID_HOME='$ANDROID_HOME' ./gradlew bundleRelease --console=plain"
  
  local AAB="$ANDROID_DIR/app/build/outputs/bundle/release/app-release.aab"
  [[ -f "$AAB" ]] || die "AAB nicht erzeugt: $AAB"
  ok "AAB gebaut: $AAB"
  
  # Submit
  if ! $SKIP_SUBMIT; then
    if [[ ! -f "$PLAY_SERVICE_ACCOUNT_JSON" ]]; then
      warn "Play Console Service-Account-JSON fehlt: $PLAY_SERVICE_ACCOUNT_JSON"
      echo ""
      echo "Setup-Schritte:"
      echo "1. Google Cloud Console → Service Accounts → Create → JSON-Key"
      echo "2. Play Console → Setup → API-Access → Service-Account linken"
      echo "3. Permissions: 'Releases' (Edit + Read)"
      echo "4. JSON-Key ablegen:"
      echo "   mkdir -p ~/secrets"
      echo "   mv ~/Downloads/rebreak-play-*.json ~/secrets/rebreak-play-service-account.json"
      echo ""
      echo "Oder ENV setzen:"
      echo "   export PLAY_SERVICE_ACCOUNT_JSON=/pfad/zu/key.json"
      echo ""
      echo "Skipped Submit — AAB ist gebaut und bereit für manuellen Upload"
    else
      log "Submitting zu Play Console Internal Track..."
      local eas_bin
      eas_bin="$(command -v eas || true)"
      if [[ -z "$eas_bin" ]]; then
        die "eas-cli nicht gefunden. Installiere mit: pnpm add -g eas-cli  (oder npm i -g eas-cli)"
      fi
      run "$eas_bin" submit --platform android \
        --path "$AAB" \
        --profile production \
        --non-interactive
      ok "Play Console Submit abgeschlossen"
    fi
  else
    log "Submit skipped (--skip-submit)"
  fi
  
  ok "Android-Deploy abgeschlossen"
  echo ""
  echo "  AAB: $AAB"
  if ! $SKIP_SUBMIT && [[ -f "$PLAY_SERVICE_ACCOUNT_JSON" ]]; then
    echo "  Status: https://play.google.com/console"
  fi
}

# ═══════════════════════════════════════════════════════════════════════════# Cleanup nach erfolgreichem Submit
# ═════════════════════════════════════════════════════════════════════════

human_size() {
  # Cross-platform du -sh fallback for missing path
  local p="$1"
  [[ -e "$p" ]] || { echo "-"; return; }
  du -sh "$p" 2>/dev/null | awk '{print $1}'
}

cleanup_build_artifacts() {
  if $KEEP_BUILD; then
    log "Cleanup skipped (--keep-build)"
    return
  fi
  if $DRY_RUN; then
    log "Cleanup (dry-run) — würde löschen:"
  else
    log "Cleanup Build-Artefakte..."
  fi

  local freed_paths=()

  if $DO_MDM || $DO_TF; then
    for p in "$ARCHIVE_PATH" "$ADHOC_EXPORT_DIR" "$TF_EXPORT_DIR"; do
      if [[ -e "$p" ]]; then
        echo "  $(human_size "$p")\t$p"
        freed_paths+=("$p")
      fi
    done
  fi

  if $DO_ANDROID; then
    for p in "$ANDROID_DIR/app/build" "$ANDROID_DIR/build" "$ANDROID_DIR/.gradle"; do
      if [[ -e "$p" ]]; then
        echo "  $(human_size "$p")\t$p"
        freed_paths+=("$p")
      fi
    done
  fi

  if ! $DRY_RUN; then
    for p in "${freed_paths[@]}"; do
      rm -rf "$p" 2>/dev/null || true
    done
  fi
  ok "Cleanup fertig (${#freed_paths[@]} Pfade)"
}

# ═════════════════════════════════════════════════════════════════════════# Main
# ═══════════════════════════════════════════════════════════════════════════

echo ""
log "ReBreak Multi-Platform Deploy"
echo ""
echo "Targets:"
if $DO_MDM; then echo "  ${GREEN}✓${RESET} iOS Ad-Hoc/MDM"; fi
if $DO_TF; then echo "  ${GREEN}✓${RESET} iOS TestFlight"; fi
if $DO_ANDROID; then echo "  ${GREEN}✓${RESET} Android"; fi
echo ""

# Collect Release Notes early (before bumping)
collect_release_notes

# Version Bumping
if $BUMP_IOS && ($DO_MDM || $DO_TF); then
  bump_ios_version
fi

if $BUMP_ANDROID && $DO_ANDROID; then
  bump_android_version
fi

# Deploy
if $DO_MDM; then
  deploy_mdm
fi

if $DO_TF; then
  deploy_testflight
fi

if $DO_ANDROID; then
  deploy_android
fi

# Cleanup (default on — spart Mac-Speicher; --keep-build zum Opt-out)
cleanup_build_artifacts

# Archive Release Notes
archive_release_notes_to_changelog

# Summary
echo ""
section "✓ Deploy Abgeschlossen"
echo ""
echo "Logs: $LOG_DIR"
echo ""

if [[ -n "$RELEASE_NOTES" ]]; then
  echo "${BOLD}═══════════════════════════════════════════════════════════${RESET}"
  echo "${BOLD}Release-Notes für v$(get_current_version) (Build $(get_current_build_number)):${RESET}"
  echo "${BOLD}═══════════════════════════════════════════════════════════${RESET}"
  echo ""
  echo "$RELEASE_NOTES"
  echo ""
  echo "${BOLD}═══════════════════════════════════════════════════════════${RESET}"
  echo ""
  echo "${YELLOW}→ Copy-Paste in:${RESET}"
  if $DO_TF; then
    echo "  ${BLUE}•${RESET} TestFlight: https://appstoreconnect.apple.com → TestFlight → Internal Testing → 'What to Test'"
  fi
  if $DO_ANDROID; then
    echo "  ${BLUE}•${RESET} Play Console: https://play.google.com/console → Internal Testing → Release-Notes"
  fi
  echo ""
fi

if ! $DRY_RUN; then
  echo "Nächste Schritte:"
  echo "  - Änderungen committen (Version-Bump + CHANGELOG.md)"
  echo "  - Git-Tag erstellen: git tag -a v$(get_current_version) -m 'Release $(get_current_version)'"
  echo "  - Push: git push && git push --tags"
fi
