#!/usr/bin/env sh
set -eu

prune_qt_deploy() {
  app="$1"
  rel="$app/Contents"
  # qmlimportscanner still copies every Controls style. Keep Material plus
  # Basic as a load fallback, matching bin/build.bat.
  for dir in \
    "$rel/Resources/qml/QtQuick/Controls/FluentWinUI3" \
    "$rel/Resources/qml/QtQuick/Controls/Fusion" \
    "$rel/Resources/qml/QtQuick/Controls/Imagine" \
    "$rel/Resources/qml/QtQuick/Controls/Universal" \
    "$rel/Resources/qml/QtQuick/Controls/Windows" \
    "$rel/Resources/qml/QtQuick/Controls/iOS" \
    "$rel/Resources/qml/QtQuick/Controls/macOS" \
    "$rel/Resources/qml/QtQuick/NativeStyle" \
    "$rel/Resources/qml/QtQuick/Effects" \
    "$rel/Resources/qml/QtQuick/Shapes" \
    "$rel/Resources/qml/QtQuick/Dialogs" \
    "$rel/Resources/qml/QtQuick/Particles" \
    "$rel/Resources/qml/QtQuick/LocalStorage" \
    "$rel/Resources/qml/QtQuick/Timeline" \
    "$rel/Resources/qml/QtQuick/tooling" \
    "$rel/Resources/qml/QtQuick/VectorImage" \
    "$rel/Resources/qml/QtQuick/Controls/designer" \
    "$rel/Resources/qml/QtQml/XmlListModel" \
    "$rel/PlugIns/sqldrivers" \
    "$rel/PlugIns/styles" \
    "$rel/Frameworks/QtQuickControls2Imagine.framework" \
    "$rel/Frameworks/QtQuickControls2ImagineStyleImpl.framework" \
    "$rel/Frameworks/QtQuickControls2Fusion.framework" \
    "$rel/Frameworks/QtQuickControls2FusionStyleImpl.framework" \
    "$rel/Frameworks/QtQuickControls2Universal.framework" \
    "$rel/Frameworks/QtQuickControls2UniversalStyleImpl.framework" \
    "$rel/Frameworks/QtQuickControls2FluentWinUI3StyleImpl.framework" \
    "$rel/Frameworks/QtQuickControls2IOSStyleImpl.framework" \
    "$rel/Frameworks/QtQuickControls2MacOSStyleImpl.framework" \
    "$rel/Frameworks/QtQuickDialogs2.framework" \
    "$rel/Frameworks/QtQuickDialogs2QuickImpl.framework" \
    "$rel/Frameworks/QtQuickDialogs2Utils.framework" \
    "$rel/Frameworks/QtQuickParticles.framework" \
    "$rel/Frameworks/QtQuickEffects.framework" \
    "$rel/Frameworks/QtQuickShapes.framework" \
    "$rel/Frameworks/QtQuickVectorImage.framework" \
    "$rel/Frameworks/QtQuickVectorImageGenerator.framework" \
    "$rel/Frameworks/QtSql.framework" \
    "$rel/Frameworks/QtQmlXmlListModel.framework" \
    "$rel/Frameworks/QtQmlLocalStorage.framework" \
    "$rel/Frameworks/QtWidgets.framework"
  do
    rm -rf -- "$dir"
  done
  # macdeployqt names these libqtquickcontrols2* (the previous prune looked
  # for libquickcontrols2* and left the unused style plugins in the zip).
  for plugin in \
    "$rel/PlugIns/quick/libqtquickcontrols2fusionstyleplugin.dylib" \
    "$rel/PlugIns/quick/libqtquickcontrols2fusionstyleimplplugin.dylib" \
    "$rel/PlugIns/quick/libqtquickcontrols2imaginestyleplugin.dylib" \
    "$rel/PlugIns/quick/libqtquickcontrols2imaginestyleimplplugin.dylib" \
    "$rel/PlugIns/quick/libqtquickcontrols2universalstyleplugin.dylib" \
    "$rel/PlugIns/quick/libqtquickcontrols2universalstyleimplplugin.dylib" \
    "$rel/PlugIns/quick/libqtquickcontrols2fluentwinui3styleplugin.dylib" \
    "$rel/PlugIns/quick/libqtquickcontrols2fluentwinui3styleimplplugin.dylib" \
    "$rel/PlugIns/quick/libqtquickcontrols2windowsstyleplugin.dylib" \
    "$rel/PlugIns/quick/libqtquickcontrols2iosstyleplugin.dylib" \
    "$rel/PlugIns/quick/libqtquickcontrols2iosstyleimplplugin.dylib" \
    "$rel/PlugIns/quick/libqtquickcontrols2macosstyleplugin.dylib" \
    "$rel/PlugIns/quick/libqtquickcontrols2macosstyleimplplugin.dylib" \
    "$rel/PlugIns/quick/libqtquickcontrols2nativestyleplugin.dylib" \
    "$rel/PlugIns/quick/libqtquickdialogsplugin.dylib" \
    "$rel/PlugIns/quick/libqtquickdialogs2quickimplplugin.dylib" \
    "$rel/PlugIns/quick/libparticlesplugin.dylib" \
    "$rel/PlugIns/quick/libeffectsplugin.dylib" \
    "$rel/PlugIns/quick/libqmlshapesplugin.dylib" \
    "$rel/PlugIns/quick/libqquickvectorimageplugin.dylib" \
    "$rel/PlugIns/quick/libqmllocalstorageplugin.dylib" \
    "$rel/PlugIns/quick/libqmlxmllistmodelplugin.dylib" \
    "$rel/PlugIns/quick/libquicktoolingplugin.dylib" \
    "$rel/PlugIns/imageformats/libqgif.dylib" \
    "$rel/PlugIns/imageformats/libqico.dylib"
  do
    rm -f -- "$plugin"
  done
  if [ -d "$rel/Resources/qml" ]; then
    find "$rel/Resources/qml" -name '*.qmltypes' -delete
  fi
}

# aqt's clang_64 kit is universal. macdeployqt copies both slices, so the
# "macos-arm64" zip was shipping an unused x86_64 copy of every Qt library.
thin_host_arch() {
  app="$1"
  host="$(uname -m)"
  case "$host" in
    arm64|x86_64) ;;
    *) return 0 ;;
  esac
  find "$app" -type f | while IFS= read -r file; do
    archs=$(lipo -archs "$file" 2>/dev/null || true)
    [ -n "$archs" ] || continue
    case "$archs" in
      *"$host"*) ;;
      *) continue ;;
    esac
    case "$archs" in
      *" "*) ;;
      *) continue ;;
    esac
    tmp="$file.thin.$$"
    if lipo "$file" -thin "$host" -output "$tmp"; then
      mv "$tmp" "$file"
    else
      rm -f "$tmp"
    fi
  done
}

embed_qtkeychain() {
  app="$1"
  exe="$app/Contents/MacOS/omairc"
  frameworks="$app/Contents/Frameworks"
  mkdir -p "$frameworks"

  candidates=
  # Prefer a project-local .deps build, then Qt's lib dir, then Homebrew.
  for keychain in \
    "$ROOT/.deps/usr/lib/libqt6keychain.1.dylib" \
    "$ROOT/.deps/usr/lib/libqt6keychain.dylib" \
    "$QT_LIB/libqt6keychain.1.dylib" \
    "$QT_LIB/libqt6keychain.dylib" \
    "$QT_LIB/libQt6Keychain.dylib"
  do
    if [ -f "$keychain" ]; then
      candidates="$keychain"
      break
    fi
  done
  if [ -z "$candidates" ] && command -v brew >/dev/null 2>&1; then
    brew_lib="$(brew --prefix qtkeychain 2>/dev/null)/lib"
    for keychain in \
      "$brew_lib/libqt6keychain.1.dylib" \
      "$brew_lib/libqt6keychain.dylib"
    do
      if [ -f "$keychain" ]; then
        candidates="$keychain"
        break
      fi
    done
  fi
  if [ -z "$candidates" ]; then
    echo "Warning: libqt6keychain not found to embed; runtime may need DYLD_LIBRARY_PATH." >&2
    return 0
  fi

  src="$candidates"
  base="$(basename "$src")"
  # Normalize to the usual SONAME so @rpath/libqt6keychain.1.dylib resolves.
  case "$base" in
    libqt6keychain.dylib) dest_base=libqt6keychain.1.dylib ;;
    *) dest_base="$base" ;;
  esac
  dest="$frameworks/$dest_base"
  cp -f "$src" "$dest"
  id="@executable_path/../Frameworks/$dest_base"
  install_name_tool -id "$id" "$dest" 2>/dev/null || true
  otool -L "$exe" | awk 'NR>1 { print $1 }' | while read -r dep; do
    case "$dep" in
      *qt6keychain*|*Qt6Keychain*)
        install_name_tool -change "$dep" "$id" "$exe" 2>/dev/null || true
        ;;
    esac
  done
  # CMake leaves an absolute Qt prefix LC_RPATH. Gatekeeper treats that as a
  # dangling load-command path once the zip is quarantined.
  otool -l "$dest" | awk '
    $1 == "cmd" && $2 == "LC_RPATH" { pending = 1; next }
    pending && $1 == "path" {
      print $2
      pending = 0
    }
  ' | while IFS= read -r rpath; do
    case "$rpath" in
      /*)
        install_name_tool -delete_rpath "$rpath" "$dest"
        ;;
    esac
  done
  if ! otool -l "$dest" | grep -q 'path @loader_path'; then
    install_name_tool -add_rpath "@loader_path" "$dest"
  fi
}

scrub_dangling_rpaths() {
  app="$1"
  find "$app" -type f ! -path '*/_CodeSignature/*' | while IFS= read -r bin; do
    case "$(file -b "$bin" 2>/dev/null || true)" in
      Mach-O*) ;;
      *) continue ;;
    esac
    otool -l "$bin" | awk '
      $1 == "cmd" && $2 == "LC_RPATH" { pending = 1; next }
      pending && $1 == "path" {
        print $2
        pending = 0
      }
    ' | while IFS= read -r rpath; do
      case "$rpath" in
        /System/*|/usr/lib/*|@*) continue ;;
        /*)
          echo "Removing dangling rpath $rpath from $bin"
          install_name_tool -delete_rpath "$rpath" "$bin"
          ;;
      esac
    done
  done
}

sign_app() {
  "$ROOT/bin/sign-macos" "$1"
}

is_homebrew_qt() {
  case "$QT_PREFIX" in
    /opt/homebrew|/opt/homebrew/*|/usr/local/Cellar/*|/usr/local/opt/*|/home/linuxbrew/*)
      return 0
      ;;
  esac
  # Homebrew's qt formula reports QT_INSTALL_PREFIX=/opt/homebrew.
  if command -v brew >/dev/null 2>&1; then
    brew_qt="$(brew --prefix qt@6 2>/dev/null || brew --prefix qt 2>/dev/null || true)"
    if [ -n "$brew_qt" ] && [ "$QT_PREFIX" = "$(CDPATH= cd -- "$brew_qt" && pwd)" ]; then
      return 0
    fi
    if [ "$QT_PREFIX" = "$(brew --prefix 2>/dev/null)" ]; then
      return 0
    fi
  fi
  return 1
}

ROOT="$(CDPATH= cd -- "$(dirname -- "$0")/.." && pwd)"
BUILD_DIR="$ROOT/build"
RELEASE_VERSION="$("$ROOT/bin/version")"
BUILD_NUMBER="${OMAIRC_BUILD_NUMBER:-$RELEASE_VERSION}"

if [ "$(uname -s)" != "Darwin" ]; then
  echo "bin/build-macos must run on macOS." >&2
  exit 1
fi

if command -v qmake6 >/dev/null 2>&1; then
  QMAKE=qmake6
elif command -v qmake >/dev/null 2>&1 && qmake -query QT_VERSION | grep -q '^6\.'; then
  QMAKE=qmake
else
  echo "A Qt 6 qmake (qmake6) is required to build omairc on macOS." >&2
  exit 1
fi

JOBS="$(sysctl -n hw.ncpu 2>/dev/null || echo 2)"
QT_BIN="$("$QMAKE" -query QT_INSTALL_BINS)"
QT_LIB="$("$QMAKE" -query QT_INSTALL_LIBS)"
QT_PREFIX="$("$QMAKE" -query QT_INSTALL_PREFIX)"

"$ROOT/packaging/macos/generate-icns"

INFO_PLIST="$BUILD_DIR/Info.plist"
mkdir -p "$BUILD_DIR"
sed \
  -e "s/@OMAIRC_RELEASE_VERSION@/$RELEASE_VERSION/g" \
  -e "s/@OMAIRC_BUILD_NUMBER@/$BUILD_NUMBER/g" \
  "$ROOT/packaging/macos/Info.plist" >"$INFO_PLIST"

cd "$BUILD_DIR"
if [ -n "${OMAIRC_BUILD_VERSION:-}" ]; then
  "$QMAKE" "$ROOT/omairc.pro" \
    CONFIG+=sdk_no_version_check \
    CONFIG+=app_bundle \
    "ICON=$ROOT/data/icons/omairc.icns" \
    "QMAKE_INFO_PLIST=$INFO_PLIST" \
    QMAKE_MACOSX_DEPLOYMENT_TARGET=11.0 \
    "OMAIRC_BUILD_VERSION=$OMAIRC_BUILD_VERSION"
else
  "$QMAKE" "$ROOT/omairc.pro" \
    CONFIG+=sdk_no_version_check \
    CONFIG+=app_bundle \
    "ICON=$ROOT/data/icons/omairc.icns" \
    "QMAKE_INFO_PLIST=$INFO_PLIST" \
    QMAKE_MACOSX_DEPLOYMENT_TARGET=11.0
fi
# Qt 6.8 still emits -framework AGL; SDK 26 removed it.
find . -name Makefile -print0 | xargs -0 sed -i '' 's/-framework AGL//g'
make -j"$JOBS"

APP="$BUILD_DIR/omairc.app"
EXE="$APP/Contents/MacOS/omairc"
if [ ! -x "$EXE" ]; then
  echo "Expected $EXE after the build." >&2
  exit 1
fi
# omairc.pro may reset QMAKE_INFO_PLIST to the unsubstituted template; install
# the versioned plist we generated above.
cp -f "$INFO_PLIST" "$APP/Contents/Info.plist"

# Homebrew's framework layout confuses macdeployqt; the resulting bundle can
# hang forever inside dyld. Keep those builds linked against the brew prefix.
if is_homebrew_qt; then
  echo "Homebrew Qt detected at $QT_PREFIX; skipping macdeployqt."
  rm -rf -- "$APP/Contents/PlugIns" "$APP/Contents/Resources/qml"
  embed_qtkeychain "$APP"
  sign_app "$APP"
else
  if [ -x "$QT_BIN/macdeployqt" ]; then
    "$QT_BIN/macdeployqt" "$APP" \
      -qmldir="$ROOT/src" \
      -always-overwrite
    prune_qt_deploy "$APP"
  fi
  embed_qtkeychain "$APP"
  thin_host_arch "$APP"
  scrub_dangling_rpaths "$APP"
  sign_app "$APP"
  "$ROOT/bin/check-macos-bundle" "$APP"
fi

ln -sf "omairc.app/Contents/MacOS/omairc" "$BUILD_DIR/omairc"

echo "Built $APP"
