# ────────────────────────────────────────────────────────────
# Flutter CI/CD → DeployHub
#
# Copy this file to your Flutter project:
#   .github/workflows/deployhub.yml
#
# Required GitHub Secrets:
#   DEPLOYHUB_API_KEY       – API key from DeployHub
#   DEPLOYHUB_URL           – e.g. https://deployhub-back.workzy.co
#   KEYSTORE_BASE64         – (Android) base64-encoded .jks keystore
#   KEYSTORE_PASSWORD       – (Android) keystore password
#   KEY_ALIAS               – (Android) key alias
#   KEY_PASSWORD            – (Android) key password
#   IOS_CERTIFICATE_BASE64  – (iOS) base64 p12 cert (Development or Distribution)
#   IOS_CERTIFICATE_PASSWORD – (iOS) certificate password
#   IOS_PROVISION_PROFILE_BASE64 – (iOS) base64 provisioning profile
#
# Optional Secrets:
#   DEPLOYHUB_FRONTEND_URL  – e.g. https://deployhub.workzy.co (for install link)
# ────────────────────────────────────────────────────────────
name: Flutter → DeployHub

on:
  push:
    branches: [development]
  workflow_dispatch:
    inputs:
      release_notes:
        description: "Release notes for this build"
        required: false
        type: string

concurrency:
  group: flutter-deploy-${{ github.ref }}
  cancel-in-progress: true

env:
  FLUTTER_VERSION: "3.41.5"
  JAVA_VERSION: "17"

jobs:
  # ── Android APK Build ────────────────────────────────────
  build-android:
    name: Build Android APK
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - uses: actions/setup-java@v4
        with:
          distribution: temurin
          java-version: ${{ env.JAVA_VERSION }}

      - uses: subosito/flutter-action@v2
        with:
          flutter-version: ${{ env.FLUTTER_VERSION }}
          cache: true

      - name: Install dependencies
        run: flutter pub get

      - name: Setup Android signing
        env:
          KEYSTORE_PASSWORD: ${{ secrets.KEYSTORE_PASSWORD }}
          KEY_PASSWORD: ${{ secrets.KEY_PASSWORD }}
          KEY_ALIAS: ${{ secrets.KEY_ALIAS }}
        run: |
          echo "${{ secrets.KEYSTORE_BASE64 }}" | base64 --decode > android/app/keystore.jks
          export KEYSTORE_ABS="$(cd android/app && pwd)/keystore.jks"
          echo "Keystore at: $KEYSTORE_ABS"

          # Create key.properties (consumed by projects that already read it)
          printf 'storePassword=%s\nkeyPassword=%s\nkeyAlias=%s\nstoreFile=%s\n' \
            "$KEYSTORE_PASSWORD" "$KEY_PASSWORD" "$KEY_ALIAS" "$KEYSTORE_ABS" \
            > android/key.properties

          mkdir -p ~/.gradle/init.d
          python3 <<'PYEOF'
          import os, textwrap

          ks   = os.environ["KEYSTORE_ABS"]
          ksp  = os.environ["KEYSTORE_PASSWORD"]
          ka   = os.environ["KEY_ALIAS"]
          kp   = os.environ["KEY_PASSWORD"]

          groovy_println = 'println "CI signing config applied to ' + "$" + '{project.path}"'

          script = textwrap.dedent(f'''\
          allprojects {{
            afterEvaluate {{ project ->
              if (project.plugins.hasPlugin("com.android.application")) {{
                project.android {{
                  signingConfigs {{
                    ciRelease {{
                      storeFile     file("{ks}")
                      storePassword "{ksp}"
                      keyAlias      "{ka}"
                      keyPassword   "{kp}"
                    }}
                  }}
                  buildTypes {{
                    release {{
                      signingConfig signingConfigs.ciRelease
                    }}
                  }}
                }}
                {groovy_println}
              }}
            }}
          }}
          ''')

          out = os.path.expanduser("~/.gradle/init.d/ci-signing.gradle")
          with open(out, "w") as f:
              f.write(script)
          print(f"Gradle init script created at {out}")
          print(script)
          PYEOF

      - name: Build APK
        run: flutter build apk --release --no-tree-shake-icons

      - name: Upload to DeployHub
        env:
          DEPLOYHUB_API_KEY: ${{ secrets.DEPLOYHUB_API_KEY }}
          DEPLOYHUB_URL: ${{ secrets.DEPLOYHUB_URL }}
          DEPLOYHUB_FRONTEND_URL: ${{ secrets.DEPLOYHUB_FRONTEND_URL }}
        run: |
          APK_PATH=$(find build/app/outputs/flutter-apk -name "*.apk" -type f | head -1)
          if [ -z "$APK_PATH" ]; then
            echo "::error::No APK found in build output"
            exit 1
          fi
          echo "Uploading $(basename "$APK_PATH")..."

          NOTES="${{ inputs.release_notes || format('Build from {0} @ {1}', github.ref_name, github.sha) }}"

          HTTP_CODE=$(curl --silent --show-error --location --max-time 600 \
            --output "$RUNNER_TEMP/upload_response.json" \
            --write-out "%{http_code}" \
            -H "X-API-Key: ${DEPLOYHUB_API_KEY}" \
            -F "file=@${APK_PATH}" \
            -F "releaseNotes=${NOTES}" \
            "${DEPLOYHUB_URL}/api/builds/ci-upload")

          RESPONSE=$(cat "$RUNNER_TEMP/upload_response.json")
          echo "HTTP Status: $HTTP_CODE"
          echo "$RESPONSE" | jq '.' 2>/dev/null || echo "$RESPONSE"

          if [ "$HTTP_CODE" -lt 200 ] || [ "$HTTP_CODE" -ge 300 ]; then
            echo "::error::Upload failed with HTTP $HTTP_CODE"
            exit 1
          fi

          SHORT_CODE=$(echo "$RESPONSE" | jq -r '.shortCode // empty')
          VERSION=$(echo "$RESPONSE" | jq -r '.version // empty')
          APP_NAME=$(echo "$RESPONSE" | jq -r '.app.appName // empty')
          if [ -n "$SHORT_CODE" ]; then
            echo "### DeployHub Upload" >> $GITHUB_STEP_SUMMARY
            echo "- **App:** ${APP_NAME}" >> $GITHUB_STEP_SUMMARY
            echo "- **Version:** ${VERSION}" >> $GITHUB_STEP_SUMMARY
            echo "- **Short Code:** ${SHORT_CODE}" >> $GITHUB_STEP_SUMMARY
            if [ -n "${DEPLOYHUB_FRONTEND_URL:-}" ]; then
              echo "- **Install:** [${DEPLOYHUB_FRONTEND_URL}/install/${SHORT_CODE}](${DEPLOYHUB_FRONTEND_URL}/install/${SHORT_CODE})" >> $GITHUB_STEP_SUMMARY
            fi
          fi

  # ── iOS IPA Build ──────────────────────────────────────
  build-ios:
    name: Build iOS IPA
    runs-on: macos-latest
    steps:
      - uses: actions/checkout@v4

      - uses: subosito/flutter-action@v2
        with:
          flutter-version: ${{ env.FLUTTER_VERSION }}
          cache: true

      - name: Install dependencies
        run: flutter pub get

      - name: Install Apple certificate and provisioning profile
        env:
          CERTIFICATE_BASE64: ${{ secrets.IOS_CERTIFICATE_BASE64 }}
          CERTIFICATE_PASSWORD: ${{ secrets.IOS_CERTIFICATE_PASSWORD }}
          PROVISION_PROFILE_BASE64: ${{ secrets.IOS_PROVISION_PROFILE_BASE64 }}
        run: |
          CERT_PATH=$RUNNER_TEMP/build_certificate.p12
          PP_PATH=$RUNNER_TEMP/build_pp.mobileprovision
          KEYCHAIN_PATH=$RUNNER_TEMP/app-signing.keychain-db
          KEYCHAIN_PASSWORD=$(openssl rand -hex 16)

          echo "$CERTIFICATE_BASE64" | base64 --decode > "$CERT_PATH"
          echo "$PROVISION_PROFILE_BASE64" | base64 --decode > "$PP_PATH"

          security create-keychain -p "$KEYCHAIN_PASSWORD" "$KEYCHAIN_PATH"
          security set-keychain-settings -lut 21600 "$KEYCHAIN_PATH"
          security unlock-keychain -p "$KEYCHAIN_PASSWORD" "$KEYCHAIN_PATH"

          security import "$CERT_PATH" -P "$CERTIFICATE_PASSWORD" -A \
            -t cert -f pkcs12 -k "$KEYCHAIN_PATH"
          security set-key-partition-list -S apple-tool:,apple: \
            -k "$KEYCHAIN_PASSWORD" "$KEYCHAIN_PATH"

          # Keep existing keychains in search list
          security list-keychains -d user -s "$KEYCHAIN_PATH" $(security list-keychains -d user | tr -d '"' | tr '\n' ' ')

          # Decode the provisioning profile and install with UUID filename
          PP_DECODED=$RUNNER_TEMP/pp_decoded.plist
          security cms -D -i "$PP_PATH" > "$PP_DECODED"
          PP_UUID=$(/usr/libexec/PlistBuddy -c "Print UUID" "$PP_DECODED")

          mkdir -p ~/Library/MobileDevice/Provisioning\ Profiles
          cp "$PP_PATH" ~/Library/MobileDevice/Provisioning\ Profiles/"${PP_UUID}.mobileprovision"

          # Detect the signing identity name from the keychain
          SIGN_IDENTITY=$(security find-identity -v -p codesigning "$KEYCHAIN_PATH" | head -1 | sed 's/.*"\(.*\)"/\1/')

          # Extract profile values
          TEAM_ID=$(/usr/libexec/PlistBuddy -c "Print TeamIdentifier:0" "$PP_DECODED")
          APP_ID_FULL=$(/usr/libexec/PlistBuddy -c "Print Entitlements:application-identifier" "$PP_DECODED")
          PROFILE_BUNDLE_ID="${APP_ID_FULL#${TEAM_ID}.}"

          # If the profile uses a wildcard App ID (*), read the real bundle ID from the Xcode project
          if [ "$PROFILE_BUNDLE_ID" = "*" ]; then
            # Method 1: Read from project.pbxproj directly using grep
            BUNDLE_ID=$(grep -m1 'PRODUCT_BUNDLE_IDENTIFIER' ios/Runner.xcodeproj/project.pbxproj 2>/dev/null \
              | head -1 | sed 's/.*= *"\{0,1\}\([^";]*\)"\{0,1\} *;.*/\1/' || true)

            # Method 2: If that failed or returned a variable, use xcodebuild
            if [ -z "$BUNDLE_ID" ] || [[ "$BUNDLE_ID" == *'$'* ]] || [ "$BUNDLE_ID" = "*" ]; then
              BUNDLE_ID=$(xcodebuild -workspace ios/Runner.xcworkspace -scheme Runner -showBuildSettings \
                -configuration Release 2>/dev/null \
                | grep '^\s*PRODUCT_BUNDLE_IDENTIFIER' | head -1 \
                | sed 's/.*= *//' | tr -d '[:space:]')
            fi

            # Method 3: Try the AppFrameworkInfo or Info.plist
            if [ -z "$BUNDLE_ID" ] || [ "$BUNDLE_ID" = "NO" ] || [ "$BUNDLE_ID" = "*" ]; then
              BUNDLE_ID=$(/usr/libexec/PlistBuddy -c "Print CFBundleIdentifier" ios/Runner/Info.plist 2>/dev/null || true)
            fi

            echo "Wildcard profile detected. Real bundle ID from project: $BUNDLE_ID"
          else
            BUNDLE_ID="$PROFILE_BUNDLE_ID"
          fi

          # Final validation
          if [ -z "$BUNDLE_ID" ] || [ "$BUNDLE_ID" = "NO" ] || [ "$BUNDLE_ID" = "*" ]; then
            echo "::error::Could not detect bundle ID. Please set it manually in the workflow."
            exit 1
          fi

          PP_NAME=$(/usr/libexec/PlistBuddy -c "Print Name" "$PP_DECODED" 2>/dev/null || echo "")
          GET_TASK_ALLOW=$(/usr/libexec/PlistBuddy -c "Print Entitlements:get-task-allow" "$PP_DECODED" 2>/dev/null || echo "false")
          HAS_ALL_DEVICES=$(/usr/libexec/PlistBuddy -c "Print ProvisionsAllDevices" "$PP_DECODED" 2>/dev/null || echo "false")
          HAS_DEVICES=$(/usr/libexec/PlistBuddy -c "Print ProvisionedDevices:0" "$PP_DECODED" 2>/dev/null || echo "")

          # Detect export method (using new Xcode method names)
          if [ "$HAS_ALL_DEVICES" = "true" ]; then
            METHOD="enterprise"
          elif [ "$GET_TASK_ALLOW" = "true" ]; then
            METHOD="debugging"
          elif [ -n "$HAS_DEVICES" ]; then
            METHOD="release-testing"
          else
            METHOD="app-store"
          fi

          echo "==============================="
          echo "Profile name:     $PP_NAME"
          echo "Profile UUID:     $PP_UUID"
          echo "Profile bundle:   $PROFILE_BUNDLE_ID"
          echo "Team ID:          $TEAM_ID"
          echo "Bundle ID:        $BUNDLE_ID"
          echo "Signing identity: $SIGN_IDENTITY"
          echo "get-task-allow:   $GET_TASK_ALLOW"
          echo "Export method:    $METHOD"
          echo "==============================="

          echo "PP_UUID=$PP_UUID" >> $GITHUB_ENV
          echo "TEAM_ID=$TEAM_ID" >> $GITHUB_ENV
          echo "BUNDLE_ID=$BUNDLE_ID" >> $GITHUB_ENV
          echo "SIGN_IDENTITY=$SIGN_IDENTITY" >> $GITHUB_ENV
          echo "EXPORT_METHOD=$METHOD" >> $GITHUB_ENV

      - name: Generate ExportOptions.plist
        run: |
          printf '<?xml version="1.0" encoding="UTF-8"?>\n<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">\n<plist version="1.0">\n<dict>\n\t<key>method</key>\n\t<string>%s</string>\n\t<key>teamID</key>\n\t<string>%s</string>\n\t<key>signingCertificate</key>\n\t<string>%s</string>\n\t<key>provisioningProfiles</key>\n\t<dict>\n\t\t<key>%s</key>\n\t\t<string>%s</string>\n\t</dict>\n\t<key>signingStyle</key>\n\t<string>manual</string>\n\t<key>stripSwiftSymbols</key>\n\t<true/>\n\t<key>compileBitcode</key>\n\t<false/>\n</dict>\n</plist>\n' \
            "$EXPORT_METHOD" "$TEAM_ID" "$SIGN_IDENTITY" "$BUNDLE_ID" "$PP_UUID" \
            > "$RUNNER_TEMP/ExportOptions.plist"

          echo "--- ExportOptions.plist ---"
          cat "$RUNNER_TEMP/ExportOptions.plist"
          echo ""
          plutil -lint "$RUNNER_TEMP/ExportOptions.plist"

      - name: Build iOS (compile Dart + prepare Xcode project)
        run: |
          flutter build ios --release --no-tree-shake-icons --no-codesign

      - name: Configure signing in Xcode project
        run: |
          gem install xcodeproj 2>/dev/null || true
          # Patch the Runner target to use manual signing with our provisioning profile.
          # This avoids passing signing settings as xcodebuild args, which would
          # incorrectly apply them to CocoaPods targets that don't support provisioning.
          ruby - <<'RUBY'
          require 'xcodeproj'

          project_path = 'ios/Runner.xcodeproj'
          project = Xcodeproj::Project.open(project_path)

          team_id = ENV['TEAM_ID']
          pp_uuid = ENV['PP_UUID']
          sign_id = ENV['SIGN_IDENTITY']
          bundle_id = ENV['BUNDLE_ID']

          project.targets.each do |target|
            next unless target.name == 'Runner'

            target.build_configurations.each do |config|
              config.build_settings['CODE_SIGN_STYLE'] = 'Manual'
              config.build_settings['DEVELOPMENT_TEAM'] = team_id
              config.build_settings['PROVISIONING_PROFILE_SPECIFIER'] = pp_uuid
              config.build_settings['CODE_SIGN_IDENTITY'] = sign_id
              config.build_settings['PRODUCT_BUNDLE_IDENTIFIER'] = bundle_id
              config.build_settings['CODE_SIGN_ALLOWED'] = 'YES'
              config.build_settings['CODE_SIGNING_REQUIRED'] = 'YES'
            end
          end

          project.save
          puts "Configured manual signing for Runner target (team=#{team_id}, profile=#{pp_uuid})"
          RUBY

      - name: Archive
        run: |
          xcodebuild archive \
            -workspace ios/Runner.xcworkspace \
            -scheme Runner \
            -sdk iphoneos \
            -configuration Release \
            -destination 'generic/platform=iOS' \
            -archivePath "$RUNNER_TEMP/Runner.xcarchive"

          if [ ! -d "$RUNNER_TEMP/Runner.xcarchive" ]; then
            echo "::error::Archive was not created."
            exit 1
          fi
          echo "Archive created successfully."

      - name: Export IPA
        run: |
          xcodebuild -exportArchive \
            -archivePath $RUNNER_TEMP/Runner.xcarchive \
            -exportOptionsPlist $RUNNER_TEMP/ExportOptions.plist \
            -exportPath $RUNNER_TEMP/ipa-export \
            -allowProvisioningUpdates

          EXPORTED_IPA=$(find "$RUNNER_TEMP/ipa-export" -name "*.ipa" -type f | head -1)
          if [ -z "$EXPORTED_IPA" ]; then
            echo "::error::Export did not produce an IPA file."
            ls -la "$RUNNER_TEMP/ipa-export/" 2>/dev/null
            exit 1
          fi

          mkdir -p build/ios/ipa
          cp "$EXPORTED_IPA" build/ios/ipa/
          echo "IPA exported: $EXPORTED_IPA"

      - name: Find IPA
        id: find_ipa
        run: |
          IPA_PATH=$(find build/ios/ipa -name "*.ipa" -type f 2>/dev/null | head -1)
          if [ -z "$IPA_PATH" ]; then
            echo "::error::No IPA found."
            exit 1
          fi
          IPA_SIZE=$(stat -f%z "$IPA_PATH" 2>/dev/null || echo "unknown")
          echo "IPA ready: $IPA_PATH ($IPA_SIZE bytes)"
          echo "ipa_path=$IPA_PATH" >> $GITHUB_OUTPUT

      - name: Upload to DeployHub
        env:
          DEPLOYHUB_API_KEY: ${{ secrets.DEPLOYHUB_API_KEY }}
          DEPLOYHUB_URL: ${{ secrets.DEPLOYHUB_URL }}
          DEPLOYHUB_FRONTEND_URL: ${{ secrets.DEPLOYHUB_FRONTEND_URL }}
        run: |
          IPA_PATH="${{ steps.find_ipa.outputs.ipa_path }}"
          echo "Uploading $(basename "$IPA_PATH")..."

          NOTES="${{ inputs.release_notes || format('Build from {0} @ {1}', github.ref_name, github.sha) }}"

          HTTP_CODE=$(curl --silent --show-error --location --max-time 600 \
            --output "$RUNNER_TEMP/upload_response.json" \
            --write-out "%{http_code}" \
            -H "X-API-Key: ${DEPLOYHUB_API_KEY}" \
            -F "file=@${IPA_PATH}" \
            -F "releaseNotes=${NOTES}" \
            "${DEPLOYHUB_URL}/api/builds/ci-upload")

          RESPONSE=$(cat "$RUNNER_TEMP/upload_response.json")
          echo "HTTP Status: $HTTP_CODE"
          echo "$RESPONSE" | jq '.' 2>/dev/null || echo "$RESPONSE"

          if [ "$HTTP_CODE" -lt 200 ] || [ "$HTTP_CODE" -ge 300 ]; then
            echo "::error::Upload failed with HTTP $HTTP_CODE"
            exit 1
          fi

          SHORT_CODE=$(echo "$RESPONSE" | jq -r '.shortCode // empty')
          VERSION=$(echo "$RESPONSE" | jq -r '.version // empty')
          APP_NAME=$(echo "$RESPONSE" | jq -r '.app.appName // empty')
          if [ -n "$SHORT_CODE" ]; then
            echo "### DeployHub Upload" >> $GITHUB_STEP_SUMMARY
            echo "- **App:** ${APP_NAME}" >> $GITHUB_STEP_SUMMARY
            echo "- **Version:** ${VERSION}" >> $GITHUB_STEP_SUMMARY
            echo "- **Short Code:** ${SHORT_CODE}" >> $GITHUB_STEP_SUMMARY
            if [ -n "${DEPLOYHUB_FRONTEND_URL:-}" ]; then
              echo "- **Install:** [${DEPLOYHUB_FRONTEND_URL}/install/${SHORT_CODE}](${DEPLOYHUB_FRONTEND_URL}/install/${SHORT_CODE})" >> $GITHUB_STEP_SUMMARY
            fi
          fi

      - name: Cleanup keychain
        if: always()
        run: |
          security delete-keychain $RUNNER_TEMP/app-signing.keychain-db 2>/dev/null || true
