#!/usr/bin/env bash
set -euo pipefail

# Shared implementation for the macOS/Windows Electron release entrypoints and
# the final daemon/desktop manifest publication step.

fail() {
  echo "error: $*" >&2
  exit 1
}

require_cmd() {
  command -v "$1" >/dev/null 2>&1 || fail "required command not found: $1"
}

require_file() {
  [[ -f "$1" ]] || fail "missing expected file: $1"
}

truthy() {
  case "${1:-}" in
    1|true|TRUE|True|yes|YES|Yes) return 0 ;;
    *) return 1 ;;
  esac
}

ensure_ks3util_bin() {
  if [[ -n "${KS3UTIL_BIN:-}" && "${KS3UTIL_BIN}" != "ks3util" ]]; then
    return
  fi
  if command -v "${KS3UTIL_BIN:-ks3util}" >/dev/null 2>&1; then
    KS3UTIL_BIN="${KS3UTIL_BIN:-ks3util}"
    export KS3UTIL_BIN
    return
  fi

  local candidate
  for candidate in \
    "${HOME:-}/bin/ks3util" \
    /opt/homebrew/bin/ks3util \
    /usr/local/bin/ks3util \
    /c/pingcap/ks3util.exe \
    /c/pingcap/ks3util; do
    [[ -n "${candidate}" ]] || continue
    if [[ -x "${candidate}" || -f "${candidate}" ]]; then
      KS3UTIL_BIN="${candidate}"
      export KS3UTIL_BIN
      return
    fi
  done
}

ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
MANAGED_OPENCODE_CONFIG_PATH="${AGENTTEAM_MANAGED_OPENCODE_CONFIG:-${ROOT_DIR}/release/managed-opencode.env}"
if [[ -f "${MANAGED_OPENCODE_CONFIG_PATH}" ]]; then
  # shellcheck source=/dev/null
  source "${MANAGED_OPENCODE_CONFIG_PATH}"
fi

managed_opencode_cache_path() {
  printf '%s/%s/%s/package/bin/%s' \
    "${MANAGED_OPENCODE_CACHE_DIR}" "${MANAGED_OPENCODE_VERSION}" "$1" "$2"
}

managed_opencode_models_cache_path() {
  printf '%s/%s/models.json' "${MANAGED_OPENCODE_CACHE_DIR}" "${MANAGED_OPENCODE_VERSION}"
}

ensure_managed_opencode_package() {
  local package_name="$1"
  local binary_name="$2"
  local target_path="$3"
  [[ -f "${target_path}" ]] && return

  require_cmd npm
  require_cmd tar
  local package_root
  local npm_tmp
  local tgz
  package_root="$(dirname "$(dirname "$(dirname "${target_path}")")")"
  mkdir -p "${MANAGED_OPENCODE_CACHE_DIR}" "${package_root}"
  npm_tmp="$(mktemp -d "${MANAGED_OPENCODE_CACHE_DIR}/npm-pack.XXXXXX")"
  (
    cd "${npm_tmp}"
    npm pack "${package_name}@${MANAGED_OPENCODE_VERSION}" \
      --registry "${MANAGED_OPENCODE_NPM_REGISTRY}" \
      --silent >/dev/null
  ) || {
    rm -rf "${npm_tmp}"
    fail "failed to download managed OpenCode ${package_name}@${MANAGED_OPENCODE_VERSION}"
  }
  tgz="$(find "${npm_tmp}" -maxdepth 1 -type f -name '*.tgz' -print -quit)"
  [[ -n "${tgz}" ]] || {
    rm -rf "${npm_tmp}"
    fail "npm pack did not produce ${package_name}"
  }
  rm -rf "${package_root}/package"
  tar -xzf "${tgz}" -C "${package_root}"
  rm -rf "${npm_tmp}"
  require_file "${target_path}"
  chmod 0755 "${target_path}"
}

ensure_managed_opencode_models_source() {
  local default_models
  default_models="$(managed_opencode_models_cache_path)"
  if [[ -z "${MANAGED_OPENCODE_MODELS_SOURCE:-}" ]]; then
    if [[ ! -f "${default_models}" ]]; then
      require_cmd curl
      mkdir -p "$(dirname "${default_models}")"
      curl -fsSL "${MANAGED_OPENCODE_MODELS_URL}" -o "${default_models}.tmp" ||
        fail "failed to download managed OpenCode models catalog"
      mv "${default_models}.tmp" "${default_models}"
    fi
    MANAGED_OPENCODE_MODELS_SOURCE="${default_models}"
  fi
  require_file "${MANAGED_OPENCODE_MODELS_SOURCE}"
}

require_managed_opencode_source() {
  local variable_name="$1"
  local path="$2"
  local goos="$3"
  [[ -n "${path}" ]] || fail "${variable_name} is required"
  require_file "${path}"
  bash "${ROOT_DIR}/release/managed-opencode-validate.sh" "${goos}" "${path}"
}

ensure_managed_opencode_preflight() {
  [[ -n "${MANAGED_OPENCODE_VERSION:-}" ]] ||
    fail "AGENTTEAM_MANAGED_OPENCODE_VERSION is required; configure ${MANAGED_OPENCODE_CONFIG_PATH}"
  mkdir -p "${MANAGED_OPENCODE_CACHE_DIR}"
}

ensure_managed_opencode_macos_sources() {
  ensure_managed_opencode_preflight
  local arm64_default
  local x64_default
  arm64_default="$(managed_opencode_cache_path opencode-darwin-arm64 opencode)"
  x64_default="$(managed_opencode_cache_path opencode-darwin-x64 opencode)"
  if [[ -z "${MANAGED_OPENCODE_DARWIN_ARM64_SOURCE:-}" ]]; then
    ensure_managed_opencode_package opencode-darwin-arm64 opencode "${arm64_default}"
    MANAGED_OPENCODE_DARWIN_ARM64_SOURCE="${arm64_default}"
  fi
  if [[ -z "${MANAGED_OPENCODE_DARWIN_AMD64_SOURCE:-}" ]]; then
    ensure_managed_opencode_package opencode-darwin-x64 opencode "${x64_default}"
    MANAGED_OPENCODE_DARWIN_AMD64_SOURCE="${x64_default}"
  fi
  ensure_managed_opencode_models_source
  require_managed_opencode_source AGENTTEAM_MANAGED_OPENCODE_DARWIN_ARM64_SOURCE "${MANAGED_OPENCODE_DARWIN_ARM64_SOURCE}" darwin
  require_managed_opencode_source AGENTTEAM_MANAGED_OPENCODE_DARWIN_AMD64_SOURCE "${MANAGED_OPENCODE_DARWIN_AMD64_SOURCE}" darwin
}

ensure_managed_opencode_windows_sources() {
  ensure_managed_opencode_preflight
  local arm64_default
  local x64_default
  arm64_default="$(managed_opencode_cache_path opencode-windows-arm64 opencode.exe)"
  x64_default="$(managed_opencode_cache_path opencode-windows-x64 opencode.exe)"
  if [[ -z "${MANAGED_OPENCODE_WINDOWS_ARM64_SOURCE:-}" ]]; then
    ensure_managed_opencode_package opencode-windows-arm64 opencode.exe "${arm64_default}"
    MANAGED_OPENCODE_WINDOWS_ARM64_SOURCE="${arm64_default}"
  fi
  if [[ -z "${MANAGED_OPENCODE_WINDOWS_AMD64_SOURCE:-}" ]]; then
    ensure_managed_opencode_package opencode-windows-x64 opencode.exe "${x64_default}"
    MANAGED_OPENCODE_WINDOWS_AMD64_SOURCE="${x64_default}"
  fi
  ensure_managed_opencode_models_source
  require_managed_opencode_source AGENTTEAM_MANAGED_OPENCODE_WINDOWS_ARM64_SOURCE "${MANAGED_OPENCODE_WINDOWS_ARM64_SOURCE}" windows
  require_managed_opencode_source AGENTTEAM_MANAGED_OPENCODE_WINDOWS_AMD64_SOURCE "${MANAGED_OPENCODE_WINDOWS_AMD64_SOURCE}" windows
}

ensure_managed_opencode_sources() {
  ensure_managed_opencode_macos_sources
  ensure_managed_opencode_windows_sources
}

home_output_dir() {
  local relative="$1"
  local home_dir="${HOME:-${USERPROFILE:-}}"
  if [[ -n "${home_dir}" ]]; then
    printf '%s/Desktop/%s/%s\n' "${home_dir}" "${relative}" "${VERSION}"
  else
    printf '%s/release/.artifacts/manual-owner-client-assets/%s/%s\n' \
      "${ROOT_DIR}" "${VERSION}" "${relative}"
  fi
}

init_manual_owner_client_assets() {
  [[ $# -eq 1 ]] || fail "init_manual_owner_client_assets expects one version"
  VERSION="$1"
  [[ "${VERSION}" =~ ^v[0-9]+\.[0-9]+\.[0-9]+([-+][0-9A-Za-z.-]+)?$ ]] ||
    fail "invalid version: ${VERSION}; expected v1.2.3 or v1.2.3-rc.1"
  VERSION_NUMBER="${VERSION#v}"

  ELECTRON_DIR="${AGENTTEAM_ELECTRON_DIR:-${ROOT_DIR}/electron}"
  BUILDER_OUTPUT_DIR="${AGENTTEAM_ELECTRON_BUILDER_OUT_DIR:-${ELECTRON_DIR}/dist/builder}"
  MAC_INSTALLER_OUT_DIR="${AGENTTEAM_MACOS_INSTALLER_OUT_DIR:-$(home_output_dir AgentTeam-macOS-Installers)}"
  MAC_RELEASE_ASSET_OUT_DIR="${AGENTTEAM_MACOS_RELEASE_ASSET_OUT_DIR:-${ROOT_DIR}/release/.artifacts/manual-owner-client-assets/${VERSION}/macos}"
  WIN_INSTALLER_OUT_DIR="${AGENTTEAM_WINDOWS_INSTALLER_OUT_DIR:-$(home_output_dir agentteam_window)}"
  WIN_RELEASE_ASSET_OUT_DIR="${AGENTTEAM_WINDOWS_RELEASE_ASSET_OUT_DIR:-${ROOT_DIR}/release/.artifacts/manual-owner-client-assets/${VERSION}/windows}"

  MAC_ARM64_BUILDER_DMG_NAME="AgentTeam-${VERSION_NUMBER}-mac-arm64.dmg"
  MAC_X64_BUILDER_DMG_NAME="AgentTeam-${VERSION_NUMBER}-mac-x64.dmg"
  MAC_ARM64_BUILDER_APP="${BUILDER_OUTPUT_DIR}/mac-arm64/AgentTeam.app"
  MAC_X64_BUILDER_APP="${BUILDER_OUTPUT_DIR}/mac/AgentTeam.app"
  MAC_ARM64_DMG="${MAC_INSTALLER_OUT_DIR}/AgentTeam-${VERSION}-darwin-arm64.dmg"
  MAC_X64_DMG="${MAC_INSTALLER_OUT_DIR}/AgentTeam-${VERSION}-darwin-amd64.dmg"
  MAC_ARM64_ZIP="${MAC_RELEASE_ASSET_OUT_DIR}/AgentTeam-${VERSION_NUMBER}-mac-arm64.zip"
  MAC_ARM64_ZIP_BLOCKMAP="${MAC_ARM64_ZIP}.blockmap"
  MAC_X64_ZIP="${MAC_RELEASE_ASSET_OUT_DIR}/AgentTeam-${VERSION_NUMBER}-mac-x64.zip"
  MAC_X64_ZIP_BLOCKMAP="${MAC_X64_ZIP}.blockmap"
  MAC_UPDATER_METADATA="${MAC_RELEASE_ASSET_OUT_DIR}/latest-mac.yml"
  MAC_CHECKSUM_FILE="${MAC_INSTALLER_OUT_DIR}/SHA256SUMS.txt"
  MAC_RELEASE_CHECKSUM_FILE="${MAC_RELEASE_ASSET_OUT_DIR}/SHA256SUMS.txt"

  WIN_ARM64_BUILDER_INSTALLER_NAME="AgentTeam-${VERSION_NUMBER}-win-arm64.exe"
  WIN_X64_BUILDER_INSTALLER_NAME="AgentTeam-${VERSION_NUMBER}-win-x64.exe"
  WIN_ARM64_INSTALLER="${WIN_INSTALLER_OUT_DIR}/AgentTeam_Windows_arm64_${VERSION}.exe"
  WIN_X64_INSTALLER="${WIN_INSTALLER_OUT_DIR}/AgentTeam_Windows_x64_${VERSION}.exe"
  WIN_ARM64_RELEASE_INSTALLER="${WIN_RELEASE_ASSET_OUT_DIR}/${WIN_ARM64_BUILDER_INSTALLER_NAME}"
  WIN_X64_RELEASE_INSTALLER="${WIN_RELEASE_ASSET_OUT_DIR}/${WIN_X64_BUILDER_INSTALLER_NAME}"
  WIN_UPDATER_METADATA="${WIN_RELEASE_ASSET_OUT_DIR}/latest.yml"
  WIN_CHECKSUM_FILE="${WIN_INSTALLER_OUT_DIR}/SHA256SUMS.txt"

  KS3UTIL_BIN="${KS3UTIL_BIN:-ks3util}"
  KS3_BUCKET="${AGENTTEAM_KS3_BUCKET:-pingcap-agentteam}"
  KS3_CLIENT_PREFIX="${AGENTTEAM_KS3_CLIENT_PREFIX:-agentteam/client}"
  KS3_SERVER_PREFIX="${AGENTTEAM_KS3_SERVER_PREFIX:-agentteam/server}"
  DESKTOP_ASSET_PREFIX="${AGENTTEAM_DESKTOP_ASSET_PREFIX:-agentteam/desktop}"
  MANIFEST_DESKTOP_PREFIX="${AGENTTEAM_MANIFEST_DESKTOP_PREFIX:-agentteam/releases/desktop}"
  MANIFEST_WORK_DIR="${AGENTTEAM_MANIFEST_WORK_DIR:-${ROOT_DIR}/release/.artifacts/manual-release-manifests/${VERSION}}"
  PUBLIC_DOWNLOAD_BASE_URL="${AGENTTEAM_PUBLIC_DOWNLOAD_BASE_URL:-https://static.pingcap.com}"
  GITEA_RELEASE_URL="${AGENTTEAM_GITEA_RELEASE_URL:-https://github.com/tidbcloud/AgentTeam/releases/tag/${VERSION}}"
  GENERATE_MANIFEST_SCRIPT="${AGENTTEAM_GENERATE_MANIFEST_SCRIPT:-${ROOT_DIR}/../../ci-ops/pipelines/pingcap/agentteam/generate-release-manifests.sh}"

  MANAGED_OPENCODE_VERSION="${AGENTTEAM_MANAGED_OPENCODE_VERSION:-${MANAGED_OPENCODE_VERSION:-}}"
  MANAGED_OPENCODE_NPM_REGISTRY="${AGENTTEAM_MANAGED_OPENCODE_NPM_REGISTRY:-${MANAGED_OPENCODE_NPM_REGISTRY:-https://registry.npmjs.org}}"
  MANAGED_OPENCODE_MODELS_URL="${AGENTTEAM_MANAGED_OPENCODE_MODELS_URL:-${MANAGED_OPENCODE_MODELS_URL:-https://models.dev/api.json}}"
  MANAGED_OPENCODE_MODELS_SOURCE="${AGENTTEAM_MANAGED_OPENCODE_MODELS_SOURCE:-${MANAGED_OPENCODE_MODELS_SOURCE:-}}"
  MANAGED_OPENCODE_CACHE_DIR="${AGENTTEAM_MANAGED_OPENCODE_CACHE_DIR:-${MANAGED_OPENCODE_CACHE_DIR:-${ROOT_DIR}/release/.artifacts/managed-opencode}}"
  MANAGED_OPENCODE_DARWIN_ARM64_SOURCE="${AGENTTEAM_MANAGED_OPENCODE_DARWIN_ARM64_SOURCE:-${MANAGED_OPENCODE_DARWIN_ARM64_SOURCE:-}}"
  MANAGED_OPENCODE_DARWIN_AMD64_SOURCE="${AGENTTEAM_MANAGED_OPENCODE_DARWIN_AMD64_SOURCE:-${MANAGED_OPENCODE_DARWIN_AMD64_SOURCE:-}}"
  MANAGED_OPENCODE_WINDOWS_ARM64_SOURCE="${AGENTTEAM_MANAGED_OPENCODE_WINDOWS_ARM64_SOURCE:-${MANAGED_OPENCODE_WINDOWS_ARM64_SOURCE:-}}"
  MANAGED_OPENCODE_WINDOWS_AMD64_SOURCE="${AGENTTEAM_MANAGED_OPENCODE_WINDOWS_AMD64_SOURCE:-${MANAGED_OPENCODE_WINDOWS_AMD64_SOURCE:-}}"
  ELECTRON_DOWNLOAD_MIRROR="${AGENTTEAM_ELECTRON_DOWNLOAD_MIRROR:-${ELECTRON_MIRROR:-https://npmmirror.com/mirrors/electron/}}"
  ELECTRON_BUILDER_BINARIES_DOWNLOAD_MIRROR="${AGENTTEAM_ELECTRON_BUILDER_BINARIES_MIRROR:-${ELECTRON_BUILDER_BINARIES_MIRROR:-https://npmmirror.com/mirrors/electron-builder-binaries/}}"

  AGENTTEAM_CLIENT_AMD64_ARCHIVE="agentteam_${VERSION_NUMBER}_linux_amd64.tar.gz"
  AGENTTEAM_CLIENT_ARM64_ARCHIVE="agentteam_${VERSION_NUMBER}_linux_arm64.tar.gz"
  AGENTTEAM_CLIENT_CHECKSUM="agentteam_${VERSION_NUMBER}_checksums.txt"
  AGENTTEAM_SERVER_BUNDLE="agentteam_server_${VERSION_NUMBER}_linux_amd64.tar.gz"
  AGENTTEAM_SERVER_CHECKSUM="agentteam_server_${VERSION_NUMBER}_checksums.txt"

  AGENTTEAM_CLIENT_DARWIN_ARM64_INSTALLER="$(basename "${MAC_ARM64_DMG}")"
  AGENTTEAM_CLIENT_DARWIN_AMD64_INSTALLER="$(basename "${MAC_X64_DMG}")"
  AGENTTEAM_CLIENT_WINDOWS_ARM64_INSTALLER="$(basename "${WIN_ARM64_INSTALLER}")"
  AGENTTEAM_CLIENT_WINDOWS_AMD64_INSTALLER="$(basename "${WIN_X64_INSTALLER}")"
  CLIENT_SHA256SUMS_NAME="SHA256SUMS.txt"
}

prepare_macos_builder_signing_env() {
  unset AGENTTEAM_MACOS_ADHOC_SIGN
  if ! truthy "${AGENTTEAM_MACOS_CODESIGN:-0}" ||
     [[ "${AGENTTEAM_MACOS_CODESIGN_IDENTITY:-}" == "-" ]]; then
    unset CSC_LINK CSC_KEY_PASSWORD
    export CSC_NAME=-
    export CSC_IDENTITY_AUTO_DISCOVERY=false
    export AGENTTEAM_MACOS_ADHOC_SIGN=1
  else
    if [[ -n "${AGENTTEAM_MACOS_CODESIGN_IDENTITY:-}" ]]; then
      unset CSC_LINK CSC_KEY_PASSWORD
      export CSC_NAME="${AGENTTEAM_MACOS_CODESIGN_IDENTITY}"
    elif [[ -n "${AGENTTEAM_MACOS_CODESIGN_P12:-}" ]]; then
      unset CSC_NAME
      require_file "${AGENTTEAM_MACOS_CODESIGN_P12}"
      [[ -n "${AGENTTEAM_MACOS_CODESIGN_P12_PASSWORD:-}" ]] ||
        fail "AGENTTEAM_MACOS_CODESIGN_P12_PASSWORD is required with AGENTTEAM_MACOS_CODESIGN_P12"
      if [[ -n "${AGENTTEAM_MACOS_CODESIGN_CERT:-}" ]]; then
        require_cmd openssl
        require_file "${AGENTTEAM_MACOS_CODESIGN_CERT}"
        MACOS_TEMP_SIGNING_DIR="$(mktemp -d "${TMPDIR:-/tmp}/agentteam-electron-signing.XXXXXX")"
        local key_file="${MACOS_TEMP_SIGNING_DIR}/private-key.pem"
        local cert_file="${MACOS_TEMP_SIGNING_DIR}/certificate.pem"
        local combined_p12="${MACOS_TEMP_SIGNING_DIR}/identity.p12"
        local combined_password
        combined_password="$(openssl rand -hex 24)"
        openssl pkcs12 -legacy \
          -in "${AGENTTEAM_MACOS_CODESIGN_P12}" \
          -passin "pass:${AGENTTEAM_MACOS_CODESIGN_P12_PASSWORD}" \
          -nocerts -nodes -out "${key_file}" >/dev/null 2>&1 ||
          fail "failed to extract the macOS signing private key from ${AGENTTEAM_MACOS_CODESIGN_P12}"
        openssl x509 -inform DER -in "${AGENTTEAM_MACOS_CODESIGN_CERT}" -out "${cert_file}" 2>/dev/null ||
          openssl x509 -in "${AGENTTEAM_MACOS_CODESIGN_CERT}" -out "${cert_file}" 2>/dev/null ||
          fail "failed to read the macOS signing certificate from ${AGENTTEAM_MACOS_CODESIGN_CERT}"
        local key_public cert_public
        key_public="$(openssl pkey -in "${key_file}" -pubout 2>/dev/null | openssl sha256)"
        cert_public="$(openssl x509 -in "${cert_file}" -pubkey -noout 2>/dev/null | openssl sha256)"
        [[ -n "${key_public}" && "${key_public}" == "${cert_public}" ]] ||
          fail "macOS signing private key does not match certificate ${AGENTTEAM_MACOS_CODESIGN_CERT}"
        openssl pkcs12 -legacy -export \
          -inkey "${key_file}" \
          -in "${cert_file}" \
          -out "${combined_p12}" \
          -passout "pass:${combined_password}" >/dev/null 2>&1 ||
          fail "failed to assemble the temporary macOS signing identity"
        export CSC_LINK="${combined_p12}"
        export CSC_KEY_PASSWORD="${combined_password}"
      else
        export CSC_LINK="${AGENTTEAM_MACOS_CODESIGN_P12}"
        export CSC_KEY_PASSWORD="${AGENTTEAM_MACOS_CODESIGN_P12_PASSWORD}"
      fi
    else
      fail "macOS signing requires AGENTTEAM_MACOS_CODESIGN_IDENTITY or AGENTTEAM_MACOS_CODESIGN_P12"
    fi
  fi
  if truthy "${AGENTTEAM_MACOS_NOTARIZE:-0}"; then
    [[ -z "${AGENTTEAM_MACOS_ADHOC_SIGN:-}" ]] || fail "macOS notarization requires Developer ID signing"
    [[ -n "${AGENTTEAM_MACOS_NOTARY_PROFILE:-}" ]] ||
      fail "AGENTTEAM_MACOS_NOTARY_PROFILE is required for notarization"
  fi
}

cleanup_macos_builder_signing_env() {
  if [[ -n "${MACOS_TEMP_SIGNING_DIR:-}" && -d "${MACOS_TEMP_SIGNING_DIR}" ]]; then
    rm -rf "${MACOS_TEMP_SIGNING_DIR}"
  fi
  MACOS_TEMP_SIGNING_DIR=""
}

prepare_windows_builder_signing_env() {
  if ! truthy "${AGENTTEAM_WINDOWS_SIGN:-0}"; then
    export CSC_IDENTITY_AUTO_DISCOVERY=false
    return
  fi
  if [[ -z "${AGENTTEAM_WINDOWS_SIGN_CERT_SHA1:-}" && -z "${AGENTTEAM_WINDOWS_SIGN_CERT_SUBJECT:-}" ]]; then
    fail "Windows signing requires AGENTTEAM_WINDOWS_SIGN_CERT_SHA1 or AGENTTEAM_WINDOWS_SIGN_CERT_SUBJECT"
  fi

  local signtool_file="${AGENTTEAM_WINDOWS_SIGNTOOL:-${SIGNTOOL_PATH:-}}"
  if [[ -z "${signtool_file}" ]]; then
    signtool_file="$(detect_windows_signtool || true)"
  fi
  [[ -n "${signtool_file}" ]] ||
    fail "Windows signing requires signtool.exe from the Windows SDK"
  if command -v cygpath >/dev/null 2>&1; then
    signtool_file="$(cygpath -u "${signtool_file}")"
    export SIGNTOOL_PATH
    SIGNTOOL_PATH="$(cygpath -w "${signtool_file}")"
  else
    export SIGNTOOL_PATH="${signtool_file}"
  fi
  require_file "${signtool_file}"
  # Explicit builder configuration below selects the intended certificate.
  # Disable discovery so an unrelated certificate can never be chosen.
  export CSC_IDENTITY_AUTO_DISCOVERY=false
}

detect_windows_signtool() {
  local candidate
  candidate="$(command -v signtool.exe 2>/dev/null || true)"
  if [[ -n "${candidate}" ]]; then
    printf '%s\n' "${candidate}"
    return
  fi

  local -a sdk_roots=(
    "/c/Program Files (x86)/Windows Kits/10/bin"
    "/c/Program Files/Windows Kits/10/bin"
  )
  local env_name windows_program_files
  if command -v cygpath >/dev/null 2>&1; then
    for env_name in 'ProgramFiles(x86)' ProgramFiles; do
      windows_program_files="$(printenv "${env_name}" 2>/dev/null || true)"
      [[ -n "${windows_program_files}" ]] || continue
      sdk_roots+=("$(cygpath -u "${windows_program_files}")/Windows Kits/10/bin")
    done
  fi

  local sdk_root
  for sdk_root in "${sdk_roots[@]}"; do
    [[ -d "${sdk_root}" ]] || continue
    candidate="$(find "${sdk_root}" -mindepth 2 -maxdepth 3 -type f -path '*/x64/signtool.exe' 2>/dev/null | sort -V | tail -n 1)"
    if [[ -n "${candidate}" ]]; then
      printf '%s\n' "${candidate}"
      return
    fi
  done
  return 1
}

windows_builder_certificate_args() {
  if ! truthy "${AGENTTEAM_WINDOWS_SIGN:-0}"; then
    return
  fi
  if [[ -n "${AGENTTEAM_WINDOWS_SIGN_CERT_SHA1:-}" ]]; then
    printf '%s\n' "--config.win.signtoolOptions.certificateSha1=${AGENTTEAM_WINDOWS_SIGN_CERT_SHA1}"
  elif [[ -n "${AGENTTEAM_WINDOWS_SIGN_CERT_SUBJECT:-}" ]]; then
    printf '%s\n' "--config.win.signtoolOptions.certificateSubjectName=${AGENTTEAM_WINDOWS_SIGN_CERT_SUBJECT}"
  else
    fail "Windows signing requires AGENTTEAM_WINDOWS_SIGN_CERT_SHA1 or AGENTTEAM_WINDOWS_SIGN_CERT_SUBJECT"
  fi
  printf '%s\n' \
    "--config.win.signtoolOptions.rfc3161TimeStampServer=${AGENTTEAM_WINDOWS_SIGN_TIMESTAMP_URL:-http://timestamp.globalsign.com/tsa/r45standard}" \
    "--config.forceCodeSigning=true"
}

run_electron_build_prerequisites() {
  require_cmd npm
  require_cmd npx
  require_file "${ELECTRON_DIR}/package.json"
  require_file "${ELECTRON_DIR}/build/icon.icns"
  require_file "${ELECTRON_DIR}/build/icon.ico"
  (
    cd "${ELECTRON_DIR}"
    AGENTTEAM_DESKTOP_VERSION="${VERSION_NUMBER}" \
      npm_package_version="${VERSION_NUMBER}" \
      npm run build:electron
  )
}

stage_electron_target() {
  local platform="$1"
  local arch="$2"
  local runtime="$3"
  (
    cd "${ELECTRON_DIR}"
    AGENTTEAM_DESKTOP_PLATFORM="${platform}" \
      AGENTTEAM_DESKTOP_ARCH="${arch}" \
      AGENTTEAM_DESKTOP_VERSION="${VERSION_NUMBER}" \
      AGENTTEAM_MANAGED_OPENCODE_SOURCE="${runtime}" \
      AGENTTEAM_MANAGED_OPENCODE_VERSION="${MANAGED_OPENCODE_VERSION}" \
      AGENTTEAM_MANAGED_OPENCODE_MODELS_SOURCE="${MANAGED_OPENCODE_MODELS_SOURCE}" \
      npm_package_version="${VERSION_NUMBER}" \
      npm run stage
    AGENTTEAM_DESKTOP_VERSION="${VERSION_NUMBER}" \
      AGENTTEAM_MANAGED_OPENCODE_VERSION="${MANAGED_OPENCODE_VERSION}" \
      npm_package_version="${VERSION_NUMBER}" \
      npm run verify
  )
}

run_electron_builder() {
  (
    cd "${ELECTRON_DIR}"
    export ELECTRON_MIRROR="${ELECTRON_DOWNLOAD_MIRROR}"
    export ELECTRON_BUILDER_BINARIES_MIRROR="${ELECTRON_BUILDER_BINARIES_DOWNLOAD_MIRROR}"
    local -a builder_args=("$@")
    if [[ " $* " == *" --mac "* && -n "${AGENTTEAM_MACOS_ADHOC_SIGN:-}" ]]; then
      builder_args+=(
        "--config.mac.identity=-"
        "--config.mac.entitlements=build/entitlements.mac.adhoc.plist"
        "--config.mac.entitlementsInherit=build/entitlements.mac.adhoc.plist"
        "--config.dmg.sign=false"
      )
    fi
    if [[ " $* " == *" --win "* ]]; then
      while IFS= read -r arg; do
        [[ -n "${arg}" ]] && builder_args+=("${arg}")
      done < <(windows_builder_certificate_args)
    fi
    builder_args+=(
      --publish never
      "--config.extraMetadata.version=${VERSION_NUMBER}"
    )
    AGENTTEAM_DESKTOP_VERSION="${VERSION_NUMBER}" \
      npm_package_version="${VERSION_NUMBER}" \
      npx electron-builder "${builder_args[@]}"
  )
}

submit_macos_dmg_for_notarization() {
  local dmg="$1"
  require_cmd xcrun
  require_cmd jq
  require_cmd codesign
  require_file "${dmg}"
  codesign --verify --strict --verbose=4 "${dmg}" ||
    fail "Developer ID signature verification failed for ${dmg}"
  local response
  local submission_id
  response="$(mktemp)"
  echo "==> submit $(basename "${dmg}") for notarization" >&2
  if ! xcrun notarytool submit "${dmg}" \
    --keychain-profile "${AGENTTEAM_MACOS_NOTARY_PROFILE}" \
    --no-s3-acceleration --no-progress --no-wait --output-format json >"${response}"; then
    rm -f "${response}"
    fail "failed to submit ${dmg} for Apple notarization"
  fi
  submission_id="$(jq -er '.id | strings | select(length > 0)' "${response}")" || {
    cat "${response}" >&2
    rm -f "${response}"
    fail "Apple notarization submission returned no id for ${dmg}"
  }
  echo "==> submitted $(basename "${dmg}") for notarization: ${submission_id}" >&2
  rm -f "${response}"
  printf '%s\n' "${submission_id}"
}

finalize_macos_notarization() {
  local dmg="$1"
  local app="$2"
  require_cmd spctl
  [[ -d "${app}" ]] || fail "missing expected application bundle: ${app}"
  xcrun stapler staple "${dmg}" ||
    fail "failed to staple the notarization ticket to ${dmg}"
  xcrun stapler validate "${dmg}" ||
    fail "failed to validate the notarization ticket on ${dmg}"
  xcrun stapler staple "${app}" ||
    fail "failed to staple the nested notarization ticket to ${app}"
  xcrun stapler validate "${app}" ||
    fail "failed to validate the notarization ticket on ${app}"
  codesign --verify --strict --verbose=4 "${dmg}" ||
    fail "Developer ID signature verification failed for ${dmg} after stapling"
  codesign --verify --strict --deep --verbose=4 "${app}" ||
    fail "Developer ID signature verification failed for ${app}"
  spctl --assess --type open --context context:primary-signature --verbose=4 "${dmg}" ||
    fail "Gatekeeper rejected notarized disk image ${dmg}"
  spctl --assess --type execute --verbose=4 "${app}" ||
    fail "Gatekeeper rejected notarized application ${app}"
}

wait_for_macos_notarization() {
  local submission_id="$1"
  local dmg="$2"
  local app="$3"
  local timeout_seconds="${AGENTTEAM_MACOS_NOTARY_TIMEOUT_SECONDS:-3600}"
  local poll_interval_seconds="${AGENTTEAM_MACOS_NOTARY_POLL_INTERVAL_SECONDS:-15}"
  [[ "${timeout_seconds}" =~ ^[1-9][0-9]*$ ]] ||
    fail "AGENTTEAM_MACOS_NOTARY_TIMEOUT_SECONDS must be a positive integer: ${timeout_seconds}"
  [[ "${poll_interval_seconds}" =~ ^[1-9][0-9]*$ ]] ||
    fail "AGENTTEAM_MACOS_NOTARY_POLL_INTERVAL_SECONDS must be a positive integer: ${poll_interval_seconds}"
  local elapsed_seconds=0
  local poll=0
  while true; do
    poll=$((poll + 1))
    local response
    local status=""
    response="$(mktemp)"
    if xcrun notarytool info "${submission_id}" \
      --keychain-profile "${AGENTTEAM_MACOS_NOTARY_PROFILE}" \
      --output-format json >"${response}" 2>&1; then
      status="$(jq -er '.status | strings | select(length > 0)' "${response}" 2>/dev/null || true)"
    fi
    case "${status}" in
      Accepted)
        rm -f "${response}"
        finalize_macos_notarization "${dmg}" "${app}"
        return
        ;;
      Invalid)
        echo "Apple notarization rejected ${dmg} (submission ${submission_id})" >&2
        cat "${response}" >&2
        rm -f "${response}"
        local notary_log
        notary_log="$(mktemp)"
        if xcrun notarytool log "${submission_id}" \
          --keychain-profile "${AGENTTEAM_MACOS_NOTARY_PROFILE}" >"${notary_log}" 2>&1; then
          cat "${notary_log}" >&2
        else
          echo "failed to fetch Apple notarization log for submission ${submission_id}" >&2
          cat "${notary_log}" >&2
        fi
        rm -f "${notary_log}"
        fail "Apple notarization returned Invalid for ${dmg} (submission ${submission_id})"
        ;;
      "In Progress") ;;
      *)
        echo "Apple notarization status query failed for ${submission_id}; retrying (poll ${poll}, ${elapsed_seconds}s/${timeout_seconds}s)" >&2
        cat "${response}" >&2
        ;;
    esac
    rm -f "${response}"
    [[ "${elapsed_seconds}" -lt "${timeout_seconds}" ]] ||
      fail "Apple notarization timed out after ${timeout_seconds}s for ${dmg} (submission ${submission_id})"
    local sleep_seconds="${poll_interval_seconds}"
    if ((elapsed_seconds + sleep_seconds > timeout_seconds)); then
      sleep_seconds=$((timeout_seconds - elapsed_seconds))
    fi
    sleep "${sleep_seconds}"
    elapsed_seconds=$((elapsed_seconds + sleep_seconds))
  done
}

notarize_macos_dmgs() {
  local arm64_dmg="$1"
  local arm64_app="$2"
  local x64_dmg="$3"
  local x64_app="$4"
  truthy "${AGENTTEAM_MACOS_NOTARIZE:-0}" || return 0
  local arm64_submission_id
  local x64_submission_id
  arm64_submission_id="$(submit_macos_dmg_for_notarization "${arm64_dmg}")"
  x64_submission_id="$(submit_macos_dmg_for_notarization "${x64_dmg}")"
  wait_for_macos_notarization "${arm64_submission_id}" "${arm64_dmg}" "${arm64_app}"
  wait_for_macos_notarization "${x64_submission_id}" "${x64_dmg}" "${x64_app}"
}

merge_updater_metadata() {
  local arm64_metadata="$1"
  local x64_metadata="$2"
  local output="$3"
  local arm64_asset="$4"
  local x64_asset="$5"
  local asset_base_url="$6"
  require_cmd node
  (
    cd "${ELECTRON_DIR}"
    node --input-type=module - \
      "${arm64_metadata}" "${x64_metadata}" "${output}" "${VERSION_NUMBER}" \
      "${arm64_asset}" "${x64_asset}" "${asset_base_url}" <<'EOF_NODE'
import fs from "node:fs"
import crypto from "node:crypto"
import path from "node:path"
import yaml from "js-yaml"

const [armFile, x64File, output, expectedVersion, armAsset, x64Asset, assetBaseURL] = process.argv.slice(2)
const arm = yaml.load(fs.readFileSync(armFile, "utf8"))
const x64 = yaml.load(fs.readFileSync(x64File, "utf8"))
if (String(arm.version) !== expectedVersion || String(x64.version) !== expectedVersion) {
  throw new Error(`updater metadata version mismatch; expected ${expectedVersion}`)
}
const select = (metadata, asset, label) => {
  const matches = (metadata.files ?? []).filter((entry) => entry?.url === asset)
  if (matches.length !== 1 || typeof matches[0]?.sha512 !== "string" || !matches[0].sha512) {
    throw new Error(`${label} updater metadata must contain exactly one ${asset} entry with sha512`)
  }
  const artifact = path.join(path.dirname(output), asset)
  const contents = fs.readFileSync(artifact)
  return {
    ...matches[0],
    url: new URL(asset, `${assetBaseURL.replace(/\/$/, "")}/`).href,
    sha512: crypto.createHash("sha512").update(contents).digest("base64"),
    size: contents.length,
  }
}
const files = [select(arm, armAsset, "arm64"), select(x64, x64Asset, "x64")]
const x64URL = new URL(x64Asset, `${assetBaseURL.replace(/\/$/, "")}/`).href
const x64Entry = files.find((entry) => entry.url === x64URL)
const merged = {
  ...x64,
  version: expectedVersion,
  files: files.sort((a, b) => a.url.localeCompare(b.url)),
  path: x64URL,
  sha512: x64Entry.sha512,
}
fs.writeFileSync(output, yaml.dump(merged, { lineWidth: -1, noRefs: true }))
EOF_NODE
  )
}

copy_builder_artifact() {
  require_file "${BUILDER_OUTPUT_DIR}/$1"
  mkdir -p "$2"
  cp "${BUILDER_OUTPUT_DIR}/$1" "$2/${3:-$1}"
}

validate_updater_metadata() {
  local file="$1"
  shift
  require_cmd node
  (
    cd "${ELECTRON_DIR}"
    node --input-type=module - "${file}" "${VERSION_NUMBER}" "$@" <<'EOF_NODE'
import fs from "node:fs"
import yaml from "js-yaml"

const [file, version, ...expected] = process.argv.slice(2)
const data = yaml.load(fs.readFileSync(file, "utf8"))
if (String(data?.version) !== version) {
  throw new Error(`${file}: expected version ${version}, got ${data?.version}`)
}
const files = data?.files ?? []
const actual = files.map((entry) => entry?.url).sort()
const wanted = [...expected].sort()
if (JSON.stringify(actual) !== JSON.stringify(wanted)) {
  throw new Error(`${file}: expected updater files ${JSON.stringify(wanted)}, got ${JSON.stringify(actual)}`)
}
if (files.some((entry) => typeof entry?.sha512 !== "string" || !entry.sha512)) {
  throw new Error(`${file}: every updater file must include sha512`)
}
EOF_NODE
  )
}

build_macos_assets() {
  ensure_managed_opencode_macos_sources
  prepare_macos_builder_signing_env
  run_electron_build_prerequisites
  mkdir -p "${MAC_INSTALLER_OUT_DIR}" "${MAC_RELEASE_ASSET_OUT_DIR}" "${BUILDER_OUTPUT_DIR}"
  local metadata_tmp
  metadata_tmp="$(mktemp -d "${BUILDER_OUTPUT_DIR}/agentteam-mac-metadata.XXXXXX")"

  echo "==> stage and package Electron macOS arm64"
  stage_electron_target darwin arm64 "${MANAGED_OPENCODE_DARWIN_ARM64_SOURCE}"
  run_electron_builder --mac dmg --arm64
  copy_builder_artifact "${MAC_ARM64_BUILDER_DMG_NAME}" "${MAC_INSTALLER_OUT_DIR}" "$(basename "${MAC_ARM64_DMG}")"

  echo "==> stage and package Electron macOS x64"
  stage_electron_target darwin x64 "${MANAGED_OPENCODE_DARWIN_AMD64_SOURCE}"
  run_electron_builder --mac dmg --x64
  copy_builder_artifact "${MAC_X64_BUILDER_DMG_NAME}" "${MAC_INSTALLER_OUT_DIR}" "$(basename "${MAC_X64_DMG}")"

  notarize_macos_dmgs \
    "${MAC_ARM64_DMG}" "${MAC_ARM64_BUILDER_APP}" \
    "${MAC_X64_DMG}" "${MAC_X64_BUILDER_APP}"

  run_electron_builder --mac zip --arm64 --prepackaged "${MAC_ARM64_BUILDER_APP}"
  require_file "${BUILDER_OUTPUT_DIR}/latest-mac.yml"
  cp "${BUILDER_OUTPUT_DIR}/latest-mac.yml" "${metadata_tmp}/latest-mac-arm64.yml"
  run_electron_builder --mac zip --x64 --prepackaged "${MAC_X64_BUILDER_APP}"
  require_file "${BUILDER_OUTPUT_DIR}/latest-mac.yml"
  cp "${BUILDER_OUTPUT_DIR}/latest-mac.yml" "${metadata_tmp}/latest-mac-x64.yml"
  merge_updater_metadata \
    "${metadata_tmp}/latest-mac-arm64.yml" \
    "${metadata_tmp}/latest-mac-x64.yml" \
    "${BUILDER_OUTPUT_DIR}/latest-mac.yml" \
    "$(basename "${MAC_ARM64_ZIP}")" \
    "$(basename "${MAC_X64_ZIP}")" \
    "${PUBLIC_DOWNLOAD_BASE_URL%/}/${DESKTOP_ASSET_PREFIX}"
  rm -rf "${metadata_tmp}"

  copy_builder_artifact "$(basename "${MAC_ARM64_ZIP}")" "${MAC_RELEASE_ASSET_OUT_DIR}"
  copy_builder_artifact "$(basename "${MAC_ARM64_ZIP_BLOCKMAP}")" "${MAC_RELEASE_ASSET_OUT_DIR}"
  copy_builder_artifact "$(basename "${MAC_X64_ZIP}")" "${MAC_RELEASE_ASSET_OUT_DIR}"
  copy_builder_artifact "$(basename "${MAC_X64_ZIP_BLOCKMAP}")" "${MAC_RELEASE_ASSET_OUT_DIR}"
  copy_builder_artifact latest-mac.yml "${MAC_RELEASE_ASSET_OUT_DIR}"
  validate_updater_metadata "${MAC_UPDATER_METADATA}" \
    "$(desktop_asset_public_url "$(basename "${MAC_ARM64_ZIP}")")" \
    "$(desktop_asset_public_url "$(basename "${MAC_X64_ZIP}")")"
  write_macos_asset_checksums
}

build_windows_installer_assets() {
  ensure_managed_opencode_windows_sources
  prepare_windows_builder_signing_env
  run_electron_build_prerequisites
  mkdir -p "${WIN_INSTALLER_OUT_DIR}" "${WIN_RELEASE_ASSET_OUT_DIR}" "${BUILDER_OUTPUT_DIR}"
  local metadata_tmp
  metadata_tmp="$(mktemp -d "${BUILDER_OUTPUT_DIR}/agentteam-win-metadata.XXXXXX")"

  echo "==> stage and package Electron Windows arm64 NSIS installer"
  stage_electron_target win32 arm64 "${MANAGED_OPENCODE_WINDOWS_ARM64_SOURCE}"
  run_electron_builder --win nsis --arm64
  require_file "${BUILDER_OUTPUT_DIR}/latest.yml"
  cp "${BUILDER_OUTPUT_DIR}/latest.yml" "${metadata_tmp}/latest-arm64.yml"

  echo "==> stage and package Electron Windows x64 NSIS installer"
  stage_electron_target win32 x64 "${MANAGED_OPENCODE_WINDOWS_AMD64_SOURCE}"
  run_electron_builder --win nsis --x64
  require_file "${BUILDER_OUTPUT_DIR}/latest.yml"
  cp "${BUILDER_OUTPUT_DIR}/latest.yml" "${metadata_tmp}/latest-x64.yml"
  merge_updater_metadata \
    "${metadata_tmp}/latest-arm64.yml" \
    "${metadata_tmp}/latest-x64.yml" \
    "${BUILDER_OUTPUT_DIR}/latest.yml" \
    "${WIN_ARM64_BUILDER_INSTALLER_NAME}" \
    "${WIN_X64_BUILDER_INSTALLER_NAME}" \
    "${PUBLIC_DOWNLOAD_BASE_URL%/}/${DESKTOP_ASSET_PREFIX}"
  rm -rf "${metadata_tmp}"

  copy_builder_artifact "${WIN_ARM64_BUILDER_INSTALLER_NAME}" "${WIN_INSTALLER_OUT_DIR}" "$(basename "${WIN_ARM64_INSTALLER}")"
  copy_builder_artifact "${WIN_X64_BUILDER_INSTALLER_NAME}" "${WIN_INSTALLER_OUT_DIR}" "$(basename "${WIN_X64_INSTALLER}")"
  copy_builder_artifact "${WIN_ARM64_BUILDER_INSTALLER_NAME}" "${WIN_RELEASE_ASSET_OUT_DIR}"
  copy_builder_artifact "${WIN_X64_BUILDER_INSTALLER_NAME}" "${WIN_RELEASE_ASSET_OUT_DIR}"
  copy_builder_artifact latest.yml "${WIN_RELEASE_ASSET_OUT_DIR}"
  validate_updater_metadata "${WIN_UPDATER_METADATA}" \
    "$(desktop_asset_public_url "${WIN_ARM64_BUILDER_INSTALLER_NAME}")" \
    "$(desktop_asset_public_url "${WIN_X64_BUILDER_INSTALLER_NAME}")"
  write_windows_installer_checksums
}

desktop_asset_public_url() {
  printf '%s/%s/%s\n' "${PUBLIC_DOWNLOAD_BASE_URL%/}" "${DESKTOP_ASSET_PREFIX}" "$1"
}

desktop_version_metadata_public_url() {
  printf '%s/%s/%s/%s\n' \
    "${PUBLIC_DOWNLOAD_BASE_URL%/}" "${MANIFEST_DESKTOP_PREFIX}" "${VERSION}" "$1"
}

desktop_metadata_public_url() {
  printf '%s/%s/%s\n' "${PUBLIC_DOWNLOAD_BASE_URL%/}" "${MANIFEST_DESKTOP_PREFIX}" "$1"
}

desktop_asset_source_url() {
  printf 'https://%s.ks3-cn-beijing.ksyuncs.com/%s/%s\n' "${KS3_BUCKET}" "${DESKTOP_ASSET_PREFIX}" "$1"
}

desktop_metadata_source_url() {
  printf 'https://%s.ks3-cn-beijing.ksyuncs.com/%s/%s\n' \
    "${KS3_BUCKET}" "${MANIFEST_DESKTOP_PREFIX}" "$1"
}

desktop_version_metadata_source_url() {
  printf 'https://%s.ks3-cn-beijing.ksyuncs.com/%s/%s/%s\n' \
    "${KS3_BUCKET}" "${MANIFEST_DESKTOP_PREFIX}" "${VERSION}" "$1"
}

client_asset_public_url() {
  printf '%s/%s/%s/%s\n' "${PUBLIC_DOWNLOAD_BASE_URL%/}" "${KS3_CLIENT_PREFIX}" "${VERSION}" "$1"
}

client_asset_source_url() {
  printf 'https://%s.ks3-cn-beijing.ksyuncs.com/%s/%s/%s\n' "${KS3_BUCKET}" "${KS3_CLIENT_PREFIX}" "${VERSION}" "$1"
}

server_asset_public_url() {
  printf '%s/%s/%s/%s\n' "${PUBLIC_DOWNLOAD_BASE_URL%/}" "${KS3_SERVER_PREFIX}" "${VERSION}" "$1"
}

desktop_checksum_source_url() {
  desktop_asset_source_url "${VERSION}/${CLIENT_SHA256SUMS_NAME}"
}

prepare_desktop_sha256sums_file() {
  local output="$1"
  shift
  local base
  local status
  base="$(mktemp)"
  if [[ -f "${output}" ]]; then
    cp "${output}" "${base}"
  elif ! curl -fsSL "$(desktop_checksum_source_url)" > "${base}"; then
    status="$(curl -sS -o /dev/null -w '%{http_code}' "$(desktop_checksum_source_url)" || true)"
    if [[ "${status}" == "404" || -z "${status}" ]]; then
      : > "${base}"
    else
      rm -f "${base}"
      fail "fetch existing SHA-256 checksums failed with HTTP ${status}: $(desktop_checksum_source_url)"
    fi
  fi

  mkdir -p "$(dirname "${output}")"
  awk -v version="${VERSION_NUMBER}" '
    NF >= 2 {
      name = $NF
      sub(/^\*/, "", name)
      if (name == "AgentTeam-v" version "-darwin-arm64.dmg" ||
          name == "AgentTeam-v" version "-darwin-amd64.dmg" ||
          name == "AgentTeam-" version "-mac-arm64.dmg" ||
          name == "AgentTeam-" version "-mac-arm64.zip" ||
          name == "AgentTeam-" version "-mac-arm64.zip.blockmap" ||
          name == "AgentTeam-" version "-mac-x64.dmg" ||
          name == "AgentTeam-" version "-mac-x64.zip" ||
          name == "AgentTeam-" version "-mac-x64.zip.blockmap" ||
          name == "AgentTeam-" version "-win-arm64.exe" ||
          name == "AgentTeam-" version "-win-x64.exe") {
        print
      }
    }
  ' "${base}" > "${output}"
  rm -f "${base}"

  local name
  local tmp
  for name in "$@"; do
    tmp="$(mktemp "${output}.XXXXXX")"
    awk -v expected="${name}" '
      {
        name = $NF
        sub(/^\*/, "", name)
        if (name != expected) print
      }
    ' "${output}" > "${tmp}"
    mv "${tmp}" "${output}"
  done
}

append_sha256sum() {
  local output="$1"
  local input="$2"
  if command -v shasum >/dev/null 2>&1; then
    shasum -a 256 "${input}"
  elif command -v sha256sum >/dev/null 2>&1; then
    sha256sum "${input}"
  else
    fail "required SHA-256 tool not found: shasum or sha256sum"
  fi | awk -v name="$(basename "${input}")" '{print $1 "  " name}' >> "${output}"
}

write_macos_asset_checksums() {
  local -a installer_files=("${MAC_ARM64_DMG}" "${MAC_X64_DMG}")
  local -a release_files=(
    "${MAC_ARM64_DMG}" "${MAC_ARM64_ZIP}" "${MAC_ARM64_ZIP_BLOCKMAP}"
    "${MAC_X64_DMG}" "${MAC_X64_ZIP}" "${MAC_X64_ZIP_BLOCKMAP}"
  )
  local file
  for file in "${release_files[@]}"; do require_file "${file}"; done
  : > "${MAC_CHECKSUM_FILE}"
  for file in "${installer_files[@]}"; do append_sha256sum "${MAC_CHECKSUM_FILE}" "${file}"; done
  prepare_desktop_sha256sums_file "${MAC_RELEASE_CHECKSUM_FILE}" \
    "$(basename "${MAC_ARM64_DMG}")" \
    "$(basename "${MAC_ARM64_ZIP}")" "$(basename "${MAC_ARM64_ZIP_BLOCKMAP}")" \
    "$(basename "${MAC_X64_DMG}")" \
    "$(basename "${MAC_X64_ZIP}")" "$(basename "${MAC_X64_ZIP_BLOCKMAP}")"
  for file in "${release_files[@]}"; do append_sha256sum "${MAC_RELEASE_CHECKSUM_FILE}" "${file}"; done
}

write_windows_installer_checksums() {
  require_file "${WIN_ARM64_INSTALLER}"
  require_file "${WIN_X64_INSTALLER}"
  prepare_desktop_sha256sums_file "${WIN_CHECKSUM_FILE}" \
    "$(basename "${WIN_ARM64_INSTALLER}")" "$(basename "${WIN_X64_INSTALLER}")"
  append_sha256sum "${WIN_CHECKSUM_FILE}" "${WIN_ARM64_INSTALLER}"
  append_sha256sum "${WIN_CHECKSUM_FILE}" "${WIN_X64_INSTALLER}"
}

upload_file() {
  "${KS3UTIL_BIN}" cp -f --acl public-read "$1" "$2"
}

is_stable_release_version() {
  [[ "$1" =~ ^v[0-9]+\.[0-9]+\.[0-9]+$ ]]
}

upload_macos_assets() {
  require_cmd "${KS3UTIL_BIN}"
  rm -f "${MAC_RELEASE_CHECKSUM_FILE}"
  write_macos_asset_checksums
  local root="ks3://${KS3_BUCKET}/${DESKTOP_ASSET_PREFIX}"
  local file
  for file in \
    "${MAC_ARM64_DMG}" "${MAC_ARM64_ZIP}" "${MAC_ARM64_ZIP_BLOCKMAP}" \
    "${MAC_X64_DMG}" "${MAC_X64_ZIP}" "${MAC_X64_ZIP_BLOCKMAP}"; do
    upload_file "${file}" "${root}/$(basename "${file}")"
  done
  upload_file "${MAC_UPDATER_METADATA}" \
    "ks3://${KS3_BUCKET}/${MANIFEST_DESKTOP_PREFIX}/${VERSION}/latest-mac.yml"
  upload_file "${MAC_RELEASE_CHECKSUM_FILE}" "${root}/${VERSION}/${CLIENT_SHA256SUMS_NAME}"
}

upload_windows_installer_assets() {
  require_cmd "${KS3UTIL_BIN}"
  rm -f "${WIN_CHECKSUM_FILE}"
  write_windows_installer_checksums
  local root="ks3://${KS3_BUCKET}/${DESKTOP_ASSET_PREFIX}"
  upload_file "${WIN_ARM64_INSTALLER}" "${root}/$(basename "${WIN_ARM64_INSTALLER}")"
  upload_file "${WIN_X64_INSTALLER}" "${root}/$(basename "${WIN_X64_INSTALLER}")"
  upload_file "${WIN_ARM64_RELEASE_INSTALLER}" "${root}/${WIN_ARM64_BUILDER_INSTALLER_NAME}"
  upload_file "${WIN_X64_RELEASE_INSTALLER}" "${root}/${WIN_X64_BUILDER_INSTALLER_NAME}"
  upload_file "${WIN_UPDATER_METADATA}" \
    "ks3://${KS3_BUCKET}/${MANIFEST_DESKTOP_PREFIX}/${VERSION}/latest.yml"
  upload_file "${WIN_CHECKSUM_FILE}" "${root}/${VERSION}/${CLIENT_SHA256SUMS_NAME}"
}

generate_manifest_for_prefix() {
  local prefix="$1"
  local output="$2"
  local existing="$3"
  mkdir -p "${output}"
  env \
    AGENTTEAM_RELEASE_TAG="${VERSION}" \
    AGENTTEAM_MANIFEST_BASE_URL="${PUBLIC_DOWNLOAD_BASE_URL%/}/${prefix}" \
    AGENTTEAM_PUBLIC_DOWNLOAD_BASE_URL="${PUBLIC_DOWNLOAD_BASE_URL}" \
    KS3_CLIENT_PREFIX="${KS3_CLIENT_PREFIX}" \
    KS3_SERVER_PREFIX="${KS3_SERVER_PREFIX}" \
    AGENTTEAM_CLIENT_AMD64_ARCHIVE="${AGENTTEAM_CLIENT_AMD64_ARCHIVE}" \
    AGENTTEAM_CLIENT_ARM64_ARCHIVE="${AGENTTEAM_CLIENT_ARM64_ARCHIVE}" \
    AGENTTEAM_CLIENT_CHECKSUM="${AGENTTEAM_CLIENT_CHECKSUM}" \
    AGENTTEAM_SERVER_BUNDLE="${AGENTTEAM_SERVER_BUNDLE}" \
    AGENTTEAM_SERVER_CHECKSUM="${AGENTTEAM_SERVER_CHECKSUM}" \
    AGENTTEAM_GITEA_RELEASE_URL="${GITEA_RELEASE_URL}" \
    AGENTTEAM_MANIFEST_OUT_DIR="${output}" \
    AGENTTEAM_MANIFEST_EXISTING_INDEX="${existing}" \
    bash "${GENERATE_MANIFEST_SCRIPT}"
}

installer_sha256() {
  local name="$1"
  local checksum
  local file
  file="$(mktemp)"
  curl -fsSL "$(desktop_checksum_source_url)" > "${file}" || {
    rm -f "${file}"
    fail "download desktop checksum file: $(desktop_checksum_source_url)"
  }
  checksum="$(awk -v expected="${name}" '$NF == expected || $NF == "*" expected {print $1}' "${file}")"
  rm -f "${file}"
  [[ "${checksum}" =~ ^[0-9a-fA-F]{64}$ ]] ||
    fail "desktop checksum file has no valid SHA-256 for ${name}"
  printf '%s\n' "${checksum}" | tr '[:upper:]' '[:lower:]'
}

write_desktop_manifest() {
  local dir="$1"
  local source="${dir}/${VERSION}.json"
  local output
  local mac_arm64_sha
  local mac_x64_sha
  local win_arm64_sha
  local win_x64_sha
  output="$(mktemp "${dir}/desktop.XXXXXX.json")"
  mac_arm64_sha="$(installer_sha256 "${AGENTTEAM_CLIENT_DARWIN_ARM64_INSTALLER}")"
  mac_x64_sha="$(installer_sha256 "${AGENTTEAM_CLIENT_DARWIN_AMD64_INSTALLER}")"
  win_arm64_sha="$(installer_sha256 "${AGENTTEAM_CLIENT_WINDOWS_ARM64_INSTALLER}")"
  win_x64_sha="$(installer_sha256 "${AGENTTEAM_CLIENT_WINDOWS_AMD64_INSTALLER}")"
  jq \
    --arg product agentteam-desktop \
    --arg version "${VERSION}" \
    --arg manifest_url "${PUBLIC_DOWNLOAD_BASE_URL%/}/${MANIFEST_DESKTOP_PREFIX}/${VERSION}.json" \
    --arg installer_checksums "$(desktop_asset_public_url "${VERSION}/${CLIENT_SHA256SUMS_NAME}")" \
    --arg runtime_checksums "$(client_asset_public_url "${AGENTTEAM_CLIENT_CHECKSUM}")" \
    --arg linux_amd64_url "$(client_asset_public_url "${AGENTTEAM_CLIENT_AMD64_ARCHIVE}")" \
    --arg linux_arm64_url "$(client_asset_public_url "${AGENTTEAM_CLIENT_ARM64_ARCHIVE}")" \
    --arg mac_arm64_url "$(desktop_asset_public_url "${AGENTTEAM_CLIENT_DARWIN_ARM64_INSTALLER}")" \
    --arg mac_x64_url "$(desktop_asset_public_url "${AGENTTEAM_CLIENT_DARWIN_AMD64_INSTALLER}")" \
    --arg win_arm64_url "$(desktop_asset_public_url "${AGENTTEAM_CLIENT_WINDOWS_ARM64_INSTALLER}")" \
    --arg win_x64_url "$(desktop_asset_public_url "${AGENTTEAM_CLIENT_WINDOWS_AMD64_INSTALLER}")" \
    --arg mac_arm64_sha "${mac_arm64_sha}" \
    --arg mac_x64_sha "${mac_x64_sha}" \
    --arg win_arm64_sha "${win_arm64_sha}" \
    --arg win_x64_sha "${win_x64_sha}" \
    --arg mac_metadata "$(desktop_version_metadata_public_url latest-mac.yml)" \
    --arg win_metadata "$(desktop_version_metadata_public_url latest.yml)" \
    '{
      schema_version: (.schema_version // "1"),
      product: $product,
      version: $version,
      published_at: .published_at,
      manifest_url: $manifest_url,
      client: {
        install_script: .client.install_script,
        checksums: $runtime_checksums,
        assets: [
          {platform:"linux-amd64", purpose:"runtime", kind:"tarball", label:"Linux x64 daemon/CLI", url:$linux_amd64_url, checksums:$runtime_checksums},
          {platform:"linux-arm64", purpose:"runtime", kind:"tarball", label:"Linux arm64 daemon/CLI", url:$linux_arm64_url, checksums:$runtime_checksums},
          {platform:"darwin-arm64", purpose:"installer", kind:"dmg", label:"macOS Apple Silicon installer", url:$mac_arm64_url, sha256:$mac_arm64_sha, checksums:$installer_checksums},
          {platform:"darwin-amd64", purpose:"installer", kind:"dmg", label:"macOS Intel installer", url:$mac_x64_url, sha256:$mac_x64_sha, checksums:$installer_checksums},
          {platform:"windows-arm64", purpose:"installer", kind:"exe", label:"Windows arm64 installer", url:$win_arm64_url, sha256:$win_arm64_sha, checksums:$installer_checksums},
          {platform:"windows-amd64", purpose:"installer", kind:"exe", label:"Windows x64 installer", url:$win_x64_url, sha256:$win_x64_sha, checksums:$installer_checksums}
        ],
        updater_metadata: [
          {platform:"darwin", kind:"electron-builder-yaml", version:$version, url:$mac_metadata},
          {platform:"windows", kind:"electron-builder-yaml", version:$version, url:$win_metadata}
        ]
      },
      release: (.release // {tag:$version})
    }' "${source}" > "${output}"
  mv "${output}" "${source}"
}

write_stable_file() {
  local prefix="$1"
  local dir="$2"
  local product="$3"
  local published_at="$4"
  jq -n \
    --arg product "${product}" \
    --arg version "${VERSION}" \
    --arg published_at "${published_at}" \
    --arg manifest_url "${PUBLIC_DOWNLOAD_BASE_URL%/}/${prefix}/${VERSION}.json" \
    --arg index_url "${PUBLIC_DOWNLOAD_BASE_URL%/}/${prefix}/index.json" \
    '{
      schema_version:"1", product:$product, channel:"stable",
      current_version:$version, published_at:$published_at,
      manifest_url:$manifest_url, index_url:$index_url
    }' > "${dir}/stable.json"
}

patch_index_stable_version() {
  local dir="$1"
  local product="$2"
  local published_at="$3"
  local tmp
  tmp="$(mktemp "${dir}/index.XXXXXX.json")"
  jq \
    --arg product "${product}" \
    --arg version "${VERSION}" \
    --arg updated_at "${published_at}" \
    '.schema_version = "1"
      | .product = $product
      | .updated_at = $updated_at
      | .versions = [(.versions // [])[]
          | if .version == $version then .product = $product else . end]' \
    "${dir}/index.json" > "${tmp}"
  mv "${tmp}" "${dir}/index.json"
}

sanitize_existing_desktop_index() {
  local file="$1"
  if ! jq -e '
    (.product == "agentteam-desktop" or .product == "agentteam-daemon")
    and ((.versions // []) | type == "array")
  ' "${file}" >/dev/null 2>&1; then
    printf '{}\n' > "${file}"
    return
  fi
  local tmp
  tmp="$(mktemp)"
  jq '
    .product as $source_product
    | .product = "agentteam-desktop"
    | .versions = [(.versions // [])[]
        | .product = (.product // $source_product)]
  ' "${file}" > "${tmp}"
  mv "${tmp}" "${file}"
}

validate_desktop_manifest() {
  local dir="$1"
  jq -e . "${dir}/${VERSION}.json" "${dir}/index.json" >/dev/null
  [[ "$(jq -r .version "${dir}/${VERSION}.json")" == "${VERSION}" ]] ||
    fail "desktop version manifest does not use ${VERSION}"
  if is_stable_release_version "${VERSION}"; then
    require_file "${dir}/stable.json"
    [[ "$(jq -r .stable_version "${dir}/index.json")" == "${VERSION}" ]] ||
      fail "desktop index does not use stable version ${VERSION}"
    [[ "$(jq -r .current_version "${dir}/stable.json")" == "${VERSION}" ]] ||
      fail "desktop stable manifest does not use ${VERSION}"
  fi
  jq -e --arg version "${VERSION}" '
    .product == "agentteam-desktop"
    and (.server == null)
    and (.client.artifacts == null)
    and (.client.install_script | endswith("/agentteam/client/install.sh"))
    and ([.client.assets[].platform] | sort) == ["darwin-amd64","darwin-arm64","linux-amd64","linux-arm64","windows-amd64","windows-arm64"]
    and ([.client.assets[] | select(.platform | startswith("linux-")) | .purpose] | unique) == ["runtime"]
    and ([.client.assets[] | select((.platform | startswith("darwin-")) or (.platform | startswith("windows-"))) | .purpose] | unique) == ["installer"]
    and ([.client.assets[] | select(.platform | startswith("linux-")) | .checksums] | unique) == [.client.checksums]
    and all(.client.assets[]; (.checksums | type) == "string")
    and all(.client.updater_metadata[]; .version == $version)
    and ([.client.updater_metadata[].platform] | sort) == ["darwin","windows"]
  ' "${dir}/${VERSION}.json" >/dev/null ||
    fail "desktop manifest must contain the six-package download matrix and updater metadata"
  jq -e --arg version "${VERSION}" '
    .product == "agentteam-desktop"
    and all(.versions[]; .product == "agentteam-desktop" or .product == "agentteam-daemon")
    and ([.versions[] | select(.version == $version and .product == "agentteam-desktop")] | length) == 1
  ' "${dir}/index.json" >/dev/null ||
    fail "desktop index must preserve explicit product ownership for every version"
}

publish_desktop_manifest_file() {
  local dir="$1"
  local name="$2"
  upload_file "${dir}/${name}" \
    "ks3://${KS3_BUCKET}/${MANIFEST_DESKTOP_PREFIX}/${name}"
  verify_remote_file_matches "${dir}/${name}" \
    "$(manifest_source_url "${MANIFEST_DESKTOP_PREFIX}" "${name}")" \
    "Desktop origin ${name}"
}

manifest_source_url() {
  printf 'https://%s.ks3-cn-beijing.ksyuncs.com/%s/%s\n' \
    "${KS3_BUCKET}" "$1" "$2"
}

regenerate_and_upload_manifests() {
  require_cmd "${KS3UTIL_BIN}"
  require_cmd cmp
  require_cmd curl
  require_cmd jq
  require_file "${GENERATE_MANIFEST_SCRIPT}"
  local desktop_dir="${MANIFEST_WORK_DIR}/desktop"
  local desktop_existing="${desktop_dir}/index-existing.json"
  local published_at
  mkdir -p "${desktop_dir}"
  curl -fsSL "$(manifest_source_url "${MANIFEST_DESKTOP_PREFIX}" index.json)" -o "${desktop_existing}" 2>/dev/null ||
    printf '{}\n' > "${desktop_existing}"
  sanitize_existing_desktop_index "${desktop_existing}"

  generate_manifest_for_prefix "${MANIFEST_DESKTOP_PREFIX}" "${desktop_dir}" "${desktop_existing}"
  write_desktop_manifest "${desktop_dir}"

  published_at="$(jq -r .published_at "${desktop_dir}/${VERSION}.json")"
  patch_index_stable_version "${desktop_dir}" agentteam-desktop "${published_at}"
  if is_stable_release_version "${VERSION}"; then
    write_stable_file "${MANIFEST_DESKTOP_PREFIX}" "${desktop_dir}" agentteam-desktop "${published_at}"
  fi
  validate_desktop_manifest "${desktop_dir}"
  publish_desktop_manifest_file "${desktop_dir}" "${VERSION}.json"
  publish_desktop_manifest_file "${desktop_dir}" index.json
  if is_stable_release_version "${VERSION}"; then
    publish_desktop_manifest_file "${desktop_dir}" stable.json
  fi
}

verify_public_url() {
  curl -fsIL --retry 3 --connect-timeout 10 --max-time 30 "$1" >/dev/null 2>&1 ||
    curl -fsSL --retry 3 --connect-timeout 10 --max-time 30 --range 0-0 "$1" >/dev/null
}

verify_remote_file_matches() {
  local expected="$1"
  local url="$2"
  local description="$3"
  local actual
  actual="$(mktemp)"
  curl -fsSL --retry 3 --connect-timeout 10 --max-time 30 \
    -H 'Cache-Control: no-cache' "${url}" -o "${actual}" || {
      rm -f "${actual}"
      fail "download ${description}: ${url}"
    }
  cmp -s "${expected}" "${actual}" || {
    rm -f "${actual}"
    fail "${description} differs from expected: ${url}"
  }
  rm -f "${actual}"
}

release_metadata_names() {
  printf '%s\n' "${VERSION}.json" index.json
  if is_stable_release_version "${VERSION}"; then
    printf '%s\n' stable.json latest-mac.yml latest.yml
  fi
}

release_metadata_refresh_urls() {
  local name
  while IFS= read -r name; do
    desktop_metadata_public_url "${name}"
  done < <(release_metadata_names)
}

verify_published_release_metadata() {
  require_cmd cmp
  require_cmd curl
  local expected_dir
  local name
  expected_dir="$(mktemp -d)"
  while IFS= read -r name; do
    curl -fsSL "$(desktop_metadata_source_url "${name}")" \
      -o "${expected_dir}/${name}" ||
      fail "download Desktop origin ${name}"
    verify_remote_file_matches "${expected_dir}/${name}" \
      "$(desktop_metadata_public_url "${name}")" \
      "CDN Desktop ${name}"
  done < <(release_metadata_names)
  rm -rf "${expected_dir}"
}

release_asset_urls() {
  client_asset_public_url "${AGENTTEAM_CLIENT_AMD64_ARCHIVE}"
  client_asset_public_url "${AGENTTEAM_CLIENT_ARM64_ARCHIVE}"
  client_asset_public_url "${AGENTTEAM_CLIENT_CHECKSUM}"
  server_asset_public_url "${AGENTTEAM_SERVER_BUNDLE}"
  server_asset_public_url "${AGENTTEAM_SERVER_CHECKSUM}"
  printf '%s/%s/install.sh\n' "${PUBLIC_DOWNLOAD_BASE_URL%/}" "${KS3_CLIENT_PREFIX}"
  desktop_asset_public_url "${AGENTTEAM_CLIENT_DARWIN_ARM64_INSTALLER}"
  desktop_asset_public_url "$(basename "${MAC_ARM64_ZIP}")"
  desktop_asset_public_url "$(basename "${MAC_ARM64_ZIP_BLOCKMAP}")"
  desktop_asset_public_url "${AGENTTEAM_CLIENT_DARWIN_AMD64_INSTALLER}"
  desktop_asset_public_url "$(basename "${MAC_X64_ZIP}")"
  desktop_asset_public_url "$(basename "${MAC_X64_ZIP_BLOCKMAP}")"
  desktop_asset_public_url "${AGENTTEAM_CLIENT_WINDOWS_AMD64_INSTALLER}"
  desktop_asset_public_url "${AGENTTEAM_CLIENT_WINDOWS_ARM64_INSTALLER}"
  desktop_asset_public_url "${WIN_ARM64_BUILDER_INSTALLER_NAME}"
  desktop_asset_public_url "${WIN_X64_BUILDER_INSTALLER_NAME}"
  desktop_version_metadata_public_url latest-mac.yml
  desktop_version_metadata_public_url latest.yml
  desktop_asset_public_url "${VERSION}/${CLIENT_SHA256SUMS_NAME}"
}

verify_release_asset_urls() {
  require_cmd curl
  local url
  while IFS= read -r url; do
    echo "==> verify CDN asset ${url}"
    verify_public_url "${url}" || fail "CDN asset is not reachable: ${url}"
  done < <(release_asset_urls)
}

checksum_file_contains() {
  awk -v expected="$2" '
    {
      name = $NF
      sub(/^\*/, "", name)
      if (name == expected) found = 1
    }
    END { exit(found ? 0 : 1) }
  ' "$1"
}

verify_checksum_only_entries() {
  local description="$1"
  local url="$2"
  shift 2
  local file
  local expected
  local actual
  file="$(mktemp)"
  curl -fsSL "${url}" > "${file}" || {
    rm -f "${file}"
    fail "download ${description} checksum file: ${url}"
  }
  for expected in "$@"; do
    checksum_file_contains "${file}" "${expected}" ||
      fail "${description} checksum file is missing ${expected}: ${url}"
  done
  while IFS= read -r actual; do
    local allowed=0
    for expected in "$@"; do
      [[ "${actual}" == "${expected}" ]] && allowed=1
    done
    [[ "${allowed}" -eq 1 ]] || {
      rm -f "${file}"
      fail "${description} checksum file contains unexpected entry ${actual}: ${url}"
    }
  done < <(awk 'NF >= 2 {name=$NF; sub(/^\*/, "", name); print name}' "${file}")
  rm -f "${file}"
}

verify_release_checksum_contents() {
  verify_checksum_only_entries runtime "$(client_asset_source_url "${AGENTTEAM_CLIENT_CHECKSUM}")" \
    "${AGENTTEAM_CLIENT_AMD64_ARCHIVE}" "${AGENTTEAM_CLIENT_ARM64_ARCHIVE}"
  verify_checksum_only_entries desktop "$(desktop_checksum_source_url)" \
    "$(basename "${MAC_ARM64_DMG}")" \
    "$(basename "${MAC_ARM64_ZIP}")" "$(basename "${MAC_ARM64_ZIP_BLOCKMAP}")" \
    "$(basename "${MAC_X64_DMG}")" \
    "$(basename "${MAC_X64_ZIP}")" "$(basename "${MAC_X64_ZIP_BLOCKMAP}")" \
    "$(basename "${WIN_ARM64_INSTALLER}")" "$(basename "${WIN_X64_INSTALLER}")"
}

verify_updater_metadata_versions() {
  require_cmd cmp
  local file
  local public_file
  file="$(mktemp)"
  public_file="$(mktemp)"
  curl -fsSL "$(desktop_version_metadata_source_url latest-mac.yml)" -o "${file}" ||
    fail "download ${VERSION}/latest-mac.yml"
  validate_updater_metadata "${file}" \
    "$(desktop_asset_public_url "$(basename "${MAC_ARM64_ZIP}")")" \
    "$(desktop_asset_public_url "$(basename "${MAC_X64_ZIP}")")"
  curl -fsSL -H 'Cache-Control: no-cache' \
    "$(desktop_version_metadata_public_url latest-mac.yml)" -o "${public_file}" ||
    fail "download public ${VERSION}/latest-mac.yml"
  cmp -s "${file}" "${public_file}" ||
    fail "public ${VERSION}/latest-mac.yml differs from origin"
  curl -fsSL "$(desktop_version_metadata_source_url latest.yml)" -o "${file}" ||
    fail "download ${VERSION}/latest.yml"
  validate_updater_metadata "${file}" \
    "$(desktop_asset_public_url "${WIN_ARM64_BUILDER_INSTALLER_NAME}")" \
    "$(desktop_asset_public_url "${WIN_X64_BUILDER_INSTALLER_NAME}")"
  curl -fsSL -H 'Cache-Control: no-cache' \
    "$(desktop_version_metadata_public_url latest.yml)" -o "${public_file}" ||
    fail "download public ${VERSION}/latest.yml"
  cmp -s "${file}" "${public_file}" ||
    fail "public ${VERSION}/latest.yml differs from origin"
  rm -f "${file}" "${public_file}"
}

activate_root_updater_metadata() {
  is_stable_release_version "${VERSION}" || return 0
  require_cmd cmp
  local name
  local metadata_dir
  metadata_dir="$(mktemp -d)"
  for name in latest-mac.yml latest.yml; do
    curl -fsSL "$(desktop_version_metadata_source_url "${name}")" \
      -o "${metadata_dir}/${name}" ||
      fail "download updater metadata candidate ${VERSION}/${name}"
  done
  for name in latest-mac.yml latest.yml; do
    upload_file "${metadata_dir}/${name}" \
      "ks3://${KS3_BUCKET}/${MANIFEST_DESKTOP_PREFIX}/${name}"
  done
  for name in latest-mac.yml latest.yml; do
    verify_remote_file_matches "${metadata_dir}/${name}" \
      "$(desktop_metadata_source_url "${name}")" \
      "root updater metadata origin ${name}"
  done
  rm -rf "${metadata_dir}"
}

verify_release_inputs() {
  verify_release_asset_urls
  verify_release_checksum_contents
  verify_updater_metadata_versions
}

publish_release_manifests() {
  verify_release_inputs
  regenerate_and_upload_manifests
  activate_root_updater_metadata
}

verify_release_publication() {
  verify_release_inputs
  verify_published_release_metadata
}

print_macos_summary() {
  cat <<EOF
macOS installers for testing:
  ${MAC_ARM64_DMG}
  ${MAC_X64_DMG}
  ${MAC_CHECKSUM_FILE}
EOF
}

print_windows_installer_summary() {
  cat <<EOF
Windows Electron assets:
  ${WIN_ARM64_INSTALLER}
  ${WIN_X64_INSTALLER}
  ${WIN_UPDATER_METADATA}
  ${WIN_CHECKSUM_FILE}
EOF
}

print_manifest_summary() {
  cat <<EOF
manifest outputs:
  ${MANIFEST_WORK_DIR}/desktop/${VERSION}.json
EOF
}
