#!/usr/bin/env bash
set -euo pipefail
set -e  # Exit on any error

########################
# configurable section #
########################

# Check if the required arguments are provided
if [ &#34;$#&#34; -lt 4 ]; then
    echo &#34;❌ Error: Missing arguments&#34;
    echo &#34;Usage: $0   &#34;
    exit 10
fi

qtBin=&#34;$1/bin&#34;
identity=&#34;$2&#34;
profile=&#34;$3&#34;
appBundle=&#34;$4&#34;


appBundleExe=$(basename &#34;$appBundle&#34; .app)
archiveName=&#34;$(dirname &#34;$appBundle&#34;)/${appBundleExe}_toNotarize.zip&#34;
outputArchive=&#34;$(dirname &#34;$appBundle&#34;)/${appBundleExe}.zip&#34;
EXECUTABLE_PATH=&#34;$appBundle/Contents/MacOS/$appBundleExe&#34;

export COPYFILE_DISABLE=1            # prevent future ._ files


echo &#34;📦 Summary of paths:&#34;
echo &#34;Qt bin path: $qtBin&#34; 
echo &#34;App bundle: $appBundle&#34;
echo &#34;App executable: $appBundle/Contents/MacOS/$appBundleExe&#34;
echo &#34;Archive for notarization: $archiveName&#34;
echo &#34;Final output archive: $outputArchive&#34;

cleanApp(){
    # Remove output archive if it exists
    if [ -f &#34;$outputArchive&#34; ]; then
      echo &#34;🗑️  Removing existing output archive: $outputArchive&#34;
      rm -f &#34;$outputArchive&#34; || { echo &#34;❌ Error: Failed to delete archive $outputArchive&#34;; exit 1; }
    fi

  # Code signing fix script for macOS
  # This script cleans resource forks, extended attributes, and re-signs the app
  # Configuration
  echo &#34;🔧 Starting code signing fix for &#34;$appBundleExe&#34;.app&#34;

  # Check if app exists
  if [ ! -d &#34;$appBundle&#34; ]; then
      echo &#34;❌ Error: App not found at $appBundle&#34;
      exit 1
  fi

  # Navigate to the app directory
  cd &#34;$(dirname &#34;$appBundle&#34;)&#34;
  APP_NAME=&#34;$(basename &#34;$appBundle&#34;)&#34;

  echo &#34;📁 Working with app: $APP_NAME&#34;

  # Step 1: Remove existing signature
  echo &#34;🗑️  Removing existing signature...&#34;
  codesign --remove-signature &#34;$APP_NAME&#34; 2&gt;/dev/null || echo &#34;No existing signature found&#34;

  # Step 2: Clean extended attributes and resource forks
  echo &#34;🧹 Cleaning extended attributes and resource forks...&#34;
  xattr -cr &#34;$APP_NAME&#34;
  xattr -dr com.apple.quarantine &#34;$APP_NAME&#34;

  # Step 3: Remove problematic files
  #echo &#34;🗂️  Removing .DS_Store and resource fork files...&#34;
  #find &#34;$APP_NAME&#34; -name &#34;.DS_Store&#34; -delete 2&gt;/dev/null || true
  #find &#34;$APP_NAME&#34; -name &#34;._*&#34; -delete 2&gt;/dev/null || true

  # Step 4: Remove any hidden files that might cause issues
  #echo &#34;👻 Removing hidden files...&#34;
  #find &#34;$APP_NAME&#34; -type f -name &#34;.*&#34; -not -name &#34;.keep&#34; -delete 2&gt;/dev/null || true

  # Step 5: Verify app bundle structure
  echo &#34;📋 Verifying app bundle structure...&#34;
  if [ ! -f &#34;$APP_NAME/Contents/Info.plist&#34; ]; then
      echo &#34;❌ Error: Info.plist not found in Contents/&#34;
      exit 1
  fi

  if [ ! -f &#34;$APP_NAME/Contents/MacOS/$appBundleExe&#34; ]; then
      echo &#34;❌ Error: Executable not found in Contents/MacOS/$appBundleExe&#34;
      exit 1
  fi

  echo &#34;✅ App bundle structure looks good&#34;

  # Optional: Check for any remaining extended attributes
  echo &#34;🔎 Checking for remaining extended attributes...&#34;
  if xattr &#34;$APP_NAME/Contents/MacOS/$appBundleExe&#34; 2&gt;/dev/null; then
      echo &#34;⚠️  Warning: Extended attributes still present&#34;
      xattr -l &#34;$APP_NAME/Contents/MacOS/$appBundleExe&#34; 2&gt;/dev/null
  else
      echo &#34;✅ No extended attributes found&#34;
  fi

  echo &#34;🎉 All done! Your app should now be properly signed.&#34;

}

relinkLibs() {
    echo &#34;🔄  Relinking libraries in $appBundle&#34;
    local binary_path=&#34;$EXECUTABLE_PATH&#34;
    local relative_path=&#34;MacOS&#34;

    echo &#34;Processing: $binary_path - $relative_path&#34;

    # Get current dependencies
    dependencies=$(otool -L &#34;$binary_path&#34; 2&gt;/dev/null | grep -E &#34;@rpath/.*\.framework|@rpath/.*\.dylib&#34; | awk &#39;{print $1}&#39;)

    if [ -z &#34;$dependencies&#34; ]; then
        echo &#34;  No @rpath dependencies found&#34;
        return
    fi

    # Process each @rpath dependency
    while IFS= read -r dep; do
        if [ -n &#34;$dep&#34; ]; then
            # Extract the framework/library name from @rpath/...
            framework_name=$(echo &#34;$dep&#34; | sed &#39;s|@rpath/||&#39;)

            # Check if this framework exists in Contents/Frameworks
            framework_path=&#34;$appBundle/Contents/Frameworks/$framework_name&#34;

            if [ -e &#34;$framework_path&#34; ]; then
                # Calculate the new path relative to the binary
                if [ &#34;$relative_path&#34; = &#34;MacOS&#34; ]; then
                    new_path=&#34;@executable_path/../Frameworks/$framework_name&#34;
                else
                    # For frameworks calling other frameworks, use @loader_path
                    new_path=&#34;@loader_path/../$framework_name&#34;
                fi

                echo &#34;  Changing: $dep&#34;
                echo &#34;       To: $new_path&#34;

                # Make the binary writable
                chmod u+w &#34;$binary_path&#34;

                # Use install_name_tool to change the dependency
                install_name_tool -change &#34;$dep&#34; &#34;$new_path&#34; &#34;$binary_path&#34; 2&gt;/dev/null

                if [ $? -eq 0 ]; then
                    echo &#34;  ✓ Successfully changed&#34;
                else
                    echo &#34;  ✗ Failed to change&#34;
                    exit 1
                fi
            else
                echo &#34;  Skipping: $dep (not found in Contents/Frameworks)&#34;
            fi
        fi
    done &lt;&lt;&lt; &#34;$dependencies&#34;

}

collectApp() {
  echo &#34;🔄  Collecting application bundle $appBundle&#34;
  if [ ! -d &#34;$appBundle&#34; ]; then
    echo &#34;❌ Error: Application bundle not found at $appBundle&#34;
    exit 1
  fi

  # Remove existing archive if it exists
  if [ -f &#34;$archiveName&#34; ]; then
    echo &#34;🗑️  Removing existing archive: $archiveName&#34;
    rm -f &#34;$archiveName&#34;
  fi
  echo &#34; &#34;
  echo &#34;🔧 Running macdeployqt to collect dependencies and resources&#34;
  echo &#34;$qtBin/macdeployqt $appBundle -verbose=3 -always-overwrite -appstore-compliant&#34;

  &#34;$qtBin/macdeployqt&#34; &#34;$appBundle&#34; -verbose=3 -always-overwrite -appstore-compliant -no-strip     # -no-strip # -sign-for-notarization=$identity -verbose=1
  echo &#34; &#34;
}


signFolderRecursively() {
  local folder=&#34;$1&#34;
  echo &#34;🔄  Signing folder recursively: $folder&#34;
  if [ -d &#34;$folder&#34; ]; then
    find &#34;$folder&#34; -type f \( -name &#34;*.dylib&#34; -o -name &#34;*.so&#34; -o -perm +111 \) | while read bin; do
      echo &#34;🔑 Signing binary: $bin&#34;
      codesign --force --options runtime --timestamp --sign &#34;$identity&#34; &#34;$bin&#34;
    done
  else
    echo &#34;⚠️  Folder not found: $folder (skipping)&#34;
  fi
}


resignApps(){
  echo &#34;🔄  Resigning application bundle $appBundle&#34;

  # Sign libraries in Frameworks directory first
  echo &#34;🔑 Signing frameworks...&#34;
  signFolderRecursively &#34;$appBundle/Contents/Frameworks&#34;

  # Sign plugins next
  echo &#34;🔑 Signing PlugIns...&#34;
  signFolderRecursively &#34;$appBundle/Contents/PlugIns&#34;

  echo &#34;🔑 Signing icPlugins...&#34;
  signFolderRecursively &#34;$appBundle/Contents/icPlugins&#34;

  echo &#34;🔑 Signing icExtra...&#34;
  signFolderRecursively &#34;$appBundle/Contents/icExtra&#34;

  # Sign the main executable
  echo &#34;🔑 Signing main executable...&#34;
  codesign --force --options runtime --timestamp --sign &#34;$identity&#34; &#34;$appBundle/Contents/MacOS/$appBundleExe&#34;

  # Finally sign the whole bundle
  echo &#34;🔑 Signing complete application bundle...&#34;
  codesign --force --deep --options runtime --timestamp --sign &#34;$identity&#34; &#34;$appBundle&#34;

  # Verify signature
  codesign -vvv --deep &#34;$appBundle&#34;
}





wrap_app() {
  echo &#34;🔄  Wrapping application bundle $appBundle into archive $archiveName&#34;
  case &#34;$archiveName&#34; in
    *.dmg) &#34;$qtBin/macdeployqt&#34; &#34;$appBundle&#34; -dmg -verbose=2 ;;
       *)  ditto -c -k  --keepParent &#34;$appBundle&#34; &#34;$archiveName&#34; ;;
  esac
  # --sequesterRsrc
}


submit_and_staple() {
  echo &#34;🔄  Submitting archive $archiveName for notarization&#34;

  notarization_output=$(xcrun notarytool submit &#34;$archiveName&#34; \
                       --keychain-profile &#34;$profile&#34; \
                       --wait)
  echo &#34;$notarization_output&#34;

  # Extract submission ID using regex
  submissionId=$(echo &#34;$notarization_output&#34; | grep -o &#34;id: [a-f0-9\-]\+&#34; | head -1 | cut -d&#39; &#39; -f2)
  status=$(echo &#34;$notarization_output&#34; | grep -o &#34;status: [A-Za-z]\+&#34; | tail -1 | cut -d&#39; &#39; -f2)


  if [[ &#34;$status&#34; != &#34;Accepted&#34; ]]; then
    echo &#34;❌ Notarization failed with status: $status&#34;
    statusJson=$(xcrun notarytool info &#34;$submissionId&#34; \
                 --keychain-profile &#34;$profile&#34; --output-format json)
    if [[ $(jq -r &#39;.status&#39; &lt;&lt;&lt;&#34;$statusJson&#34;) != &#34;Accepted&#34; ]]; then
      echo &#34;❌ Notarization details:&#34;
      xcrun notarytool log &#34;$submissionId&#34; --keychain-profile &#34;$profile&#34;
      exit 1
    fi
  else
    echo &#34;✅ Notarization accepted, stapling app bundle...&#34;
    xcrun stapler staple &#34;$appBundle&#34;

    # Verify stapling was successful
    echo &#34;🔍 Verifying staple status...&#34;
    staple_check=$(xcrun stapler validate -v &#34;$appBundle&#34;)
    if [[ $staple_check == *&#34;valid&#34;* ]]; then
      echo &#34;✅ Stapling validation successful&#34;
    else
      echo &#34;⚠️ Stapling validation issue: $staple_check&#34;
      exit 1
    fi

    # Check Gatekeeper validation
    echo &#34;🔐 Checking Gatekeeper validation...&#34;
    spctl_result=$(spctl -a -t exec -vv &#34;$appBundle&#34; 2&gt;&amp;1)
    echo &#34;$spctl_result&#34;
    if [[ $spctl_result == *&#34;accepted&#34;* ]]; then
      echo &#34;✅ App passed Gatekeeper validation&#34;
      
    else
      echo &#34;⚠️ App may have Gatekeeper validation issues&#34;
      exit 1
    fi
  fi

  echo &#34;📦 Creating final archive: $outputArchive&#34;
  if [ -f &#34;$outputArchive&#34; ]; then
    rm -f &#34;$outputArchive&#34;
  fi

  ditto -c -k -rsrc --sequesterRsrc --keepParent  &#34;$appBundle&#34; &#34;$outputArchive&#34;

  if [ -f &#34;$outputArchive&#34; ]; then
    echo &#34;✅ Final archive created successfully at: $outputArchive&#34;
  else
    echo &#34;❌ Failed to create final archive&#34;
    exit 1
  fi

  exit 0

}

#echo &#34;🔄  Starting application build process for $appBundleExe Waiting for 10 seconds to ensure all processes are ready...&#34;
#sync
#sleep 10

cleanApp
collectApp
#relinkLibs
resignApps
wrap_app
submit_and_staple