name: Update FCS Keys

on:
  schedule:
    - cron: "0 19 * * *" # daily at 11:00 PST (19:00 UTC)
  workflow_dispatch:

permissions:
  contents: write

jobs:
  update-all-fcs-keys:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v7

      - name: Set up Go
        uses: actions/setup-go@v7
        with:
          go-version: "1.26"

      - name: Build ipsw CLI
        run: |
          go build -o ipsw ./cmd/ipsw

      - name: Get all latest builds
        id: get-builds
        run: |
          set -euo pipefail
          echo "Getting all latest build numbers..."

          # AppleDB tracks release/rc/beta separately and picks the latest by
          # release DATE, so maintenance betas (e.g. 26.6 beta) are surfaced even
          # when a newer major's beta exists. It is also the source `make
          # update_fcs_keys_*` scrapes, so detection and scraping stay in sync.
          get_latest_build() {
            os="$1"
            device="$2"
            track="$3"
            case "$track" in
              release) flag="--release" ;;
              rc) flag="--rc" ;;
              beta) flag="--beta" ;;
              *) echo "::error::unknown track '$track'" >&2; exit 1 ;;
            esac
            json=$(./ipsw download appledb --os "$os" "$flag" --latest --show-latest --device "$device" --no-color 2>/dev/null) || {
              echo "::error::appledb query failed for os=$os device=$device track=$track" >&2
              exit 1
            }
            build=$(printf '%s' "$json" | jq -r '.build // ""' | tr -d '\r\n')
            if [ -z "$build" ]; then
              echo "::error::No build returned for os=$os device=$device track=$track" >&2
              exit 1
            fi
            if ! printf '%s' "$build" | grep -Eq '^[0-9A-Za-z]+$'; then
              echo "::error::Invalid build identifier '$build' for os=$os device=$device track=$track" >&2
              exit 1
            fi
            printf '%s' "$build"
          }

          # Probe devices match Makefile defaults
          STABLE_IOS=$(get_latest_build iOS iPhone17,1 release)
          STABLE_MACOS=$(get_latest_build macOS Mac14,7 release)
          STABLE_VISIONOS=$(get_latest_build visionOS RealityDevice14,1 release)

          RC_IOS=$(get_latest_build iOS iPhone17,1 rc)
          RC_MACOS=$(get_latest_build macOS Mac14,7 rc)
          RC_VISIONOS=$(get_latest_build visionOS RealityDevice14,1 rc)

          BETA_IOS=$(get_latest_build iOS iPhone17,1 beta)
          BETA_MACOS=$(get_latest_build macOS Mac14,7 beta)
          BETA_VISIONOS=$(get_latest_build visionOS RealityDevice14,1 beta)

          # Create fingerprints
          STABLE_FP="${STABLE_IOS}_${STABLE_MACOS}_${STABLE_VISIONOS}"
          RC_FP="${RC_IOS}_${RC_MACOS}_${RC_VISIONOS}"
          BETA_FP="${BETA_IOS}_${BETA_MACOS}_${BETA_VISIONOS}"

          # Output all builds
          echo "stable_ios=$STABLE_IOS" >> $GITHUB_OUTPUT
          echo "stable_macos=$STABLE_MACOS" >> $GITHUB_OUTPUT
          echo "stable_visionos=$STABLE_VISIONOS" >> $GITHUB_OUTPUT
          echo "stable_fingerprint=$STABLE_FP" >> $GITHUB_OUTPUT

          echo "rc_ios=$RC_IOS" >> $GITHUB_OUTPUT
          echo "rc_macos=$RC_MACOS" >> $GITHUB_OUTPUT
          echo "rc_visionos=$RC_VISIONOS" >> $GITHUB_OUTPUT
          echo "rc_fingerprint=$RC_FP" >> $GITHUB_OUTPUT

          echo "beta_ios=$BETA_IOS" >> $GITHUB_OUTPUT
          echo "beta_macos=$BETA_MACOS" >> $GITHUB_OUTPUT
          echo "beta_visionos=$BETA_VISIONOS" >> $GITHUB_OUTPUT
          echo "beta_fingerprint=$BETA_FP" >> $GITHUB_OUTPUT

          echo "Current fingerprints:"
          echo "  Stable: $STABLE_FP"
          echo "  RC: $RC_FP"
          echo "  Beta: $BETA_FP"

      - name: Check for new builds
        id: check-builds
        run: |
          set -euo pipefail
          [ -n "${{ steps.get-builds.outputs.stable_ios }}" ] || { echo "::error::stable_ios output is empty" >&2; exit 1; }
          [ -n "${{ steps.get-builds.outputs.stable_macos }}" ] || { echo "::error::stable_macos output is empty" >&2; exit 1; }
          [ -n "${{ steps.get-builds.outputs.stable_visionos }}" ] || { echo "::error::stable_visionos output is empty" >&2; exit 1; }
          [ -n "${{ steps.get-builds.outputs.beta_ios }}" ] || { echo "::error::beta_ios output is empty" >&2; exit 1; }
          [ -n "${{ steps.get-builds.outputs.beta_macos }}" ] || { echo "::error::beta_macos output is empty" >&2; exit 1; }
          [ -n "${{ steps.get-builds.outputs.beta_visionos }}" ] || { echo "::error::beta_visionos output is empty" >&2; exit 1; }

          # Get cached fingerprints from the nested FcsKeys structure
          LAST_STABLE=$(jq -r '.fcs_keys_stable.fcs_keys.release.fingerprint // ""' hack/.watch_cache)
          LAST_RC=$(jq -r '.fcs_keys_rc.fcs_keys.rc.fingerprint // ""' hack/.watch_cache)
          LAST_BETA=$(jq -r '.fcs_keys_beta.fcs_keys.beta.fingerprint // ""' hack/.watch_cache)

          # Check what needs updating
          UPDATE_STABLE=false
          UPDATE_RC=false
          UPDATE_BETA=false
          UPDATE_ANY=false

          if [ "$LAST_STABLE" != "${{ steps.get-builds.outputs.stable_fingerprint }}" ]; then
            echo "New stable builds found"
            UPDATE_STABLE=true
            UPDATE_ANY=true
          fi

          if [ "$LAST_RC" != "${{ steps.get-builds.outputs.rc_fingerprint }}" ]; then
            echo "New RC builds found"
            UPDATE_RC=true
            UPDATE_ANY=true
          fi

          if [ "$LAST_BETA" != "${{ steps.get-builds.outputs.beta_fingerprint }}" ]; then
            echo "New beta builds found"
            UPDATE_BETA=true
            UPDATE_ANY=true
          fi

          echo "update_stable=$UPDATE_STABLE" >> $GITHUB_OUTPUT
          echo "update_rc=$UPDATE_RC" >> $GITHUB_OUTPUT
          echo "update_beta=$UPDATE_BETA" >> $GITHUB_OUTPUT
          echo "update_any=$UPDATE_ANY" >> $GITHUB_OUTPUT

      - name: Update stable FCS keys
        if: steps.check-builds.outputs.update_stable == 'true'
        run: |
          echo "Running FCS keys update for stable..."
          make update_fcs_keys_release \
            FCS_IOS_BUILD="${{ steps.get-builds.outputs.stable_ios }}" \
            FCS_MOS_BUILD="${{ steps.get-builds.outputs.stable_macos }}" \
            FCS_VOS_BUILD="${{ steps.get-builds.outputs.stable_visionos }}"

      - name: Update RC FCS keys
        if: steps.check-builds.outputs.update_rc == 'true'
        run: |
          echo "Running FCS keys update for RC..."
          make update_fcs_keys_rc

      - name: Update beta FCS keys
        if: steps.check-builds.outputs.update_beta == 'true'
        run: |
          echo "Running FCS keys update for beta..."
          make update_fcs_keys_beta

      - name: Check for changes and commit
        if: steps.check-builds.outputs.update_any == 'true'
        run: |
          set -euo pipefail
          # Debug: Show file timestamps and git status
          echo "=== File timestamps ==="
          ls -la pkg/aea/data/fcs-keys*
          echo "=== Git status ==="
          git status pkg/aea/data/fcs-keys*
          echo "=== Git diff ==="
          git diff pkg/aea/data/fcs-keys.json pkg/aea/data/fcs-keys.gz || echo "No diff output"

          # Update the cache file with all new fingerprints
          cp hack/.watch_cache hack/.watch_cache.tmp

          # Update stable if needed
          if [ "${{ steps.check-builds.outputs.update_stable }}" = "true" ]; then
            jq -c --sort-keys --arg fp "${{ steps.get-builds.outputs.stable_fingerprint }}" \
               --arg ios "${{ steps.get-builds.outputs.stable_ios }}" \
               --arg macos "${{ steps.get-builds.outputs.stable_macos }}" \
               --arg visionos "${{ steps.get-builds.outputs.stable_visionos }}" \
               --arg timestamp "$(date -u +%Y-%m-%dT%H:%M:%SZ)" \
               '.fcs_keys_stable = {
                  "fcs_keys": {
                    "release": {
                      "fingerprint": $fp,
                      "ios_build": $ios,
                      "macos_build": $macos,
                      "visionos_build": $visionos,
                      "updated_at": $timestamp
                    }
                  }
                }' hack/.watch_cache.tmp > hack/.watch_cache.tmp2
            mv hack/.watch_cache.tmp2 hack/.watch_cache.tmp
          fi

          # Update RC if needed
          if [ "${{ steps.check-builds.outputs.update_rc }}" = "true" ]; then
            jq -c --sort-keys --arg fp "${{ steps.get-builds.outputs.rc_fingerprint }}" \
               --arg ios "${{ steps.get-builds.outputs.rc_ios }}" \
               --arg macos "${{ steps.get-builds.outputs.rc_macos }}" \
               --arg visionos "${{ steps.get-builds.outputs.rc_visionos }}" \
               --arg timestamp "$(date -u +%Y-%m-%dT%H:%M:%SZ)" \
               '.fcs_keys_rc = {
                  "fcs_keys": {
                    "rc": {
                      "fingerprint": $fp,
                      "ios_build": $ios,
                      "macos_build": $macos,
                      "visionos_build": $visionos,
                      "updated_at": $timestamp
                    }
                  }
                }' hack/.watch_cache.tmp > hack/.watch_cache.tmp2
            mv hack/.watch_cache.tmp2 hack/.watch_cache.tmp
          fi

          # Update beta if needed
          if [ "${{ steps.check-builds.outputs.update_beta }}" = "true" ]; then
            jq -c --sort-keys --arg fp "${{ steps.get-builds.outputs.beta_fingerprint }}" \
               --arg ios "${{ steps.get-builds.outputs.beta_ios }}" \
               --arg macos "${{ steps.get-builds.outputs.beta_macos }}" \
               --arg visionos "${{ steps.get-builds.outputs.beta_visionos }}" \
               --arg timestamp "$(date -u +%Y-%m-%dT%H:%M:%SZ)" \
               '.fcs_keys_beta = {
                  "fcs_keys": {
                    "beta": {
                      "fingerprint": $fp,
                      "ios_build": $ios,
                      "macos_build": $macos,
                      "visionos_build": $visionos,
                      "updated_at": $timestamp
                    }
                  }
                }' hack/.watch_cache.tmp > hack/.watch_cache.tmp2
            mv hack/.watch_cache.tmp2 hack/.watch_cache.tmp
          fi

          # Final sort of the cache file for idempotency
          jq --sort-keys . hack/.watch_cache.tmp > hack/.watch_cache
          rm hack/.watch_cache.tmp

          # Normalize the main FCS keys JSON the same way as hack/make/json_mini.
          if [ -f pkg/aea/data/fcs-keys.json ]; then
            jq -c --sort-keys . pkg/aea/data/fcs-keys.json > pkg/aea/data/fcs-keys.json.tmp && mv pkg/aea/data/fcs-keys.json.tmp pkg/aea/data/fcs-keys.json
            jq -e 'type == "object" and length > 0' pkg/aea/data/fcs-keys.json >/dev/null
            gzip -cn pkg/aea/data/fcs-keys.json > pkg/aea/data/fcs-keys.gz
          fi

          # Check if normalization or cache update actually changed anything.
          if git diff --quiet pkg/aea/data/fcs-keys.json pkg/aea/data/fcs-keys.gz hack/.watch_cache; then
            echo "No changes to FCS keys files or watch cache despite new builds"
            exit 0
          fi

          # Configure git
          git config --local user.name "github-actions[bot]"
          git config --local user.email "github-actions[bot]@users.noreply.github.com"

          # Build commit message
          COMMIT_MSG="chore(fcs-keys): update FCS keys"
          UPDATES=()

          if [ "${{ steps.check-builds.outputs.update_stable }}" = "true" ]; then
            UPDATES+=("stable")
          fi
          if [ "${{ steps.check-builds.outputs.update_rc }}" = "true" ]; then
            UPDATES+=("RC")
          fi
          if [ "${{ steps.check-builds.outputs.update_beta }}" = "true" ]; then
            UPDATES+=("beta")
          fi

          # Join updates with commas
          UPDATE_STR=$(IFS=', '; echo "${UPDATES[*]}")
          COMMIT_MSG="$COMMIT_MSG ($UPDATE_STR) [skip ci]"

          # Commit all changes at once
          git add pkg/aea/data/fcs-keys.json pkg/aea/data/fcs-keys.gz hack/.watch_cache
          git commit -m "$COMMIT_MSG"
          git push
