name: Mobile CI
env:
  # Build environment versions
  # Node version is read from .nvmrc during workflow execution
  RUBY_VERSION: 3.2
  JAVA_VERSION: 17
  ANDROID_NDK_VERSION: 27.0.12077973
  XCODE_VERSION: 16.4
  # Path configuration
  WORKSPACE: ${{ github.workspace }}
  APP_PATH: ${{ github.workspace }}/app
  # Cache versions
  GH_CACHE_VERSION: v2 # Global cache version - bumped to invalidate caches
  GH_GEMS_CACHE_VERSION: v1 # Ruby gems cache version
  # Performance optimizations
  GRADLE_OPTS: -Dorg.gradle.daemon=false -Dorg.gradle.workers.max=4 -Dorg.gradle.parallel=true -Dorg.gradle.configureondemand=true -Dorg.gradle.caching=true
  CI: true
on:
  pull_request:
    branches:
      - dev
      - staging
      - main
    paths:
      - "common/**"
      - "app/**"
      - "packages/mobile-sdk-alpha/**"
      - ".github/workflows/mobile-ci.yml"
      - ".github/actions/**"
  workflow_dispatch: {}

concurrency:
  group: mobile-ci-${{ github.workflow }}-${{ github.ref }}
  cancel-in-progress: true

jobs:
  build-deps:
    runs-on: ubuntu-latest
    timeout-minutes: 60
    steps:
      - uses: actions/checkout@v6
      - name: Read and sanitize Node.js version
        shell: bash
        run: |
          if [ ! -f .nvmrc ] || [ -z "$(cat .nvmrc)" ]; then
            echo "❌ .nvmrc is missing or empty"; exit 1;
          fi
          VERSION="$(tr -d '\r\n' < .nvmrc)"
          VERSION="${VERSION#v}"
          if ! [[ "$VERSION" =~ ^[0-9]+(\.[0-9]+){0,2}$ ]]; then
            echo "Invalid .nvmrc content: '$VERSION'"; exit 1;
          fi
          echo "NODE_VERSION=$VERSION" >> "$GITHUB_ENV"
          echo "NODE_VERSION_SANITIZED=${VERSION//\//-}" >> "$GITHUB_ENV"
      - name: Use Node.js
        uses: actions/setup-node@v4
        with:
          node-version: ${{ env.NODE_VERSION }}
      - name: Enable Corepack
        run: corepack enable
      - name: Activate Yarn 4.6.0
        run: corepack prepare yarn@4.6.0 --activate
      - name: Cache Yarn
        uses: ./.github/actions/cache-yarn
        with:
          path: |
            .yarn/cache
            node_modules
            app/node_modules
          cache-version: ${{ env.GH_CACHE_VERSION }}-${{ env.NODE_VERSION_SANITIZED }}
      - name: Install Dependencies
        uses: ./.github/actions/yarn-install
      - name: Cache Built Dependencies
        id: built-deps
        uses: ./.github/actions/cache-built-deps
        with:
          cache-version: ${{ env.GH_CACHE_VERSION }}-${{ env.NODE_VERSION_SANITIZED }}
      - name: Build dependencies (cache miss)
        # Temporarily disabled due to `yarn types` failures when cache is used.
        # if: steps.built-deps.outputs.cache-hit != 'true'
        run: |
          echo "Cache miss for built dependencies. Building now..."
          yarn workspace @selfxyz/mobile-app run build:deps
      - name: Run linter
        run: yarn lint
        working-directory: ./app
      - name: Run prettier
        run: yarn fmt
        working-directory: ./app
      - name: Check App Types
        run: yarn types
        working-directory: ./app

  test:
    runs-on: ubuntu-latest
    needs: build-deps
    timeout-minutes: 60
    steps:
      - uses: actions/checkout@v6
      - name: Read and sanitize Node.js version
        shell: bash
        run: |
          if [ ! -f .nvmrc ] || [ -z "$(cat .nvmrc)" ]; then
            echo "❌ .nvmrc is missing or empty"; exit 1;
          fi
          VERSION="$(tr -d '\r\n' < .nvmrc)"
          VERSION="${VERSION#v}"
          if ! [[ "$VERSION" =~ ^[0-9]+(\.[0-9]+){0,2}$ ]]; then
            echo "Invalid .nvmrc content: '$VERSION'"; exit 1;
          fi
          echo "NODE_VERSION=$VERSION" >> "$GITHUB_ENV"
          echo "NODE_VERSION_SANITIZED=${VERSION//\//-}" >> "$GITHUB_ENV"
      - name: Use Node.js
        uses: actions/setup-node@v4
        with:
          node-version: ${{ env.NODE_VERSION }}
      - name: Enable Corepack
        run: corepack enable
      - name: Activate Yarn 4.6.0
        run: corepack prepare yarn@4.6.0 --activate
      - name: Cache Yarn
        uses: ./.github/actions/cache-yarn
        with:
          path: |
            .yarn/cache
            node_modules
            app/node_modules
          cache-version: ${{ env.GH_CACHE_VERSION }}-${{ env.NODE_VERSION_SANITIZED }}
      - name: Install Dependencies
        uses: ./.github/actions/yarn-install
      - name: Cache Built Dependencies
        id: built-deps
        uses: ./.github/actions/cache-built-deps
        with:
          cache-version: ${{ env.GH_CACHE_VERSION }}-${{ env.NODE_VERSION_SANITIZED }}
      - name: Debug Cache Restoration
        run: |
          echo "Cache hit: ${{ steps.built-deps.outputs.cache-hit }}"
          echo "Cache key: built-deps-${{ env.GH_CACHE_VERSION }}-${{ env.NODE_VERSION_SANITIZED }}-${{ hashFiles('common/**/*', 'packages/mobile-sdk-alpha/**/*', '!common/dist/**', '!packages/mobile-sdk-alpha/dist/**') }}"
          echo "Checking if cached files exist:"
          ls -la packages/mobile-sdk-alpha/dist/ || echo "❌ mobile-sdk-alpha dist not found"
          ls -la packages/mobile-sdk-alpha/dist/cjs/ || echo "❌ mobile-sdk-alpha dist/cjs not found"
          ls -la packages/mobile-sdk-alpha/dist/cjs/index.cjs || echo "❌ mobile-sdk-alpha dist/cjs/index.cjs not found"
          ls -la common/dist/ || echo "❌ common dist not found"
          ls -la common/dist/cjs/ || echo "❌ common dist/cjs not found"
          ls -la common/dist/cjs/index.cjs || echo "❌ common dist/cjs/index.cjs not found"
      - name: Build dependencies (always - debugging CI)
        # Temporarily always build to debug CI issues
        # TODO: Re-enable cache after fixing: if: steps.built-deps.outputs.cache-hit != 'true'
        run: |
          echo "Building dependencies (cache temporarily disabled for debugging)..."
          yarn workspace @selfxyz/mobile-app run build:deps
          # Verify build completed successfully
          if [ ! -f "packages/mobile-sdk-alpha/dist/cjs/index.cjs" ] || [ ! -f "common/dist/cjs/index.cjs" ]; then
            echo "❌ Build failed - required files missing after build"
            ls -la packages/mobile-sdk-alpha/dist/ || echo "mobile-sdk-alpha dist not found"
            ls -la common/dist/ || echo "common dist not found"
            exit 1
          fi
          echo "✅ Build completed successfully"
      - name: Force Build Dependencies If Missing
        run: |
          # Force build if required files don't exist, regardless of cache status
          if [ ! -f "packages/mobile-sdk-alpha/dist/cjs/index.cjs" ] || [ ! -f "common/dist/cjs/index.cjs" ]; then
            echo "❌ Required dependency files missing, forcing rebuild..."
            echo "Missing files:"
            [ ! -f "packages/mobile-sdk-alpha/dist/cjs/index.cjs" ] && echo "  - packages/mobile-sdk-alpha/dist/cjs/index.cjs"
            [ ! -f "common/dist/cjs/index.cjs" ] && echo "  - common/dist/cjs/index.cjs"
            yarn workspace @selfxyz/mobile-app run build:deps
            # Verify build completed successfully
            if [ ! -f "packages/mobile-sdk-alpha/dist/cjs/index.cjs" ] || [ ! -f "common/dist/cjs/index.cjs" ]; then
              echo "❌ Forced build failed - required files still missing"
              ls -la packages/mobile-sdk-alpha/ || echo "packages/mobile-sdk-alpha not found"
              ls -la packages/mobile-sdk-alpha/dist/ || echo "mobile-sdk-alpha dist not found"
              ls -la common/ || echo "common not found"
              ls -la common/dist/ || echo "common dist not found"
              exit 1
            fi
            echo "✅ Forced build completed successfully"
          else
            echo "✅ All required dependency files exist"
          fi
      - name: Check for nested require() in tests
        run: node scripts/check-test-requires.cjs
        working-directory: ./app
      - name: App Tests
        env:
          # Increase Node.js memory to prevent hermes-parser WASM memory errors
          NODE_OPTIONS: --max-old-space-size=4096
          # Override production NODE_ENV for tests - React's production build doesn't include testing utilities
          NODE_ENV: test
        run: |
          # Final verification from app directory perspective
          echo "Final verification before running tests (from app directory)..."
          if [ ! -f "../packages/mobile-sdk-alpha/dist/cjs/index.cjs" ] || [ ! -f "../common/dist/cjs/index.cjs" ]; then
            echo "❌ Dependencies still not found from app directory"
            ls -la ../packages/mobile-sdk-alpha/dist/ || echo "mobile-sdk-alpha dist not found"
            ls -la ../common/dist/ || echo "common dist not found"
            exit 1
          fi
          echo "✅ All dependencies verified, running tests..."
          # Run jest through yarn to avoid the build:deps step since CI already built dependencies
          yarn test:ci
        working-directory: ./app
  build-ios:
    # This is mostly covered in mobile-e2e.yml so we don't need to run it here frequently
    if: github.event_name == 'workflow_dispatch'
    # runs-on: macos-latest-large
    runs-on: namespace-profile-apple-silicon-6cpu
    needs: build-deps
    timeout-minutes: 60
    env:
      # iOS project configuration - hardcoded for CI stability
      IOS_PROJECT_NAME: "Self"
      IOS_PROJECT_SCHEME: "OpenPassport"
    steps:
      - uses: actions/checkout@v6
      - name: Read and sanitize Node.js version
        shell: bash
        run: |
          if [ ! -f .nvmrc ] || [ -z "$(cat .nvmrc)" ]; then
            echo "❌ .nvmrc is missing or empty"; exit 1;
          fi
          VERSION="$(tr -d '\r\n' < .nvmrc)"
          VERSION="${VERSION#v}"
          if ! [[ "$VERSION" =~ ^[0-9]+(\.[0-9]+){0,2}$ ]]; then
            echo "Invalid .nvmrc content: '$VERSION'"; exit 1;
          fi
          echo "NODE_VERSION=$VERSION" >> "$GITHUB_ENV"
          echo "NODE_VERSION_SANITIZED=${VERSION//\//-}" >> "$GITHUB_ENV"
      - name: Use Node.js
        uses: actions/setup-node@v4
        with:
          node-version: ${{ env.NODE_VERSION }}
      - name: Enable Corepack
        run: corepack enable
      - name: Activate Yarn 4.6.0
        run: corepack prepare yarn@4.6.0 --activate
      - name: Set up Xcode
        uses: maxim-lobanov/setup-xcode@v1
        with:
          xcode-version: ${{ env.XCODE_VERSION }}
      - name: Configure Xcode path
        run: |
          echo "🔧 Configuring Xcode path to fix iOS SDK issues..."
          # Fix for macOS 15 runner iOS SDK issues
          # See: https://github.com/actions/runner-images/issues/12758
          sudo xcode-select --switch /Applications/Xcode_${{ env.XCODE_VERSION }}.app
          echo "✅ Xcode path configured"

          # Verify Xcode setup
          echo "Xcode version:"
          xcodebuild -version
          echo "Xcode path:"
          xcode-select -p

      - name: Check Java installation
        run: |
          echo "INSTALL_JAVA=false" >> "$GITHUB_ENV"
          if command -v java &> /dev/null && java -version &> /dev/null; then
            echo "Java already installed: $(java -version 2>&1 | head -n 1)"
          else
            echo "Java not found or not working, will install..."
            echo "INSTALL_JAVA=true" >> "$GITHUB_ENV"
          fi
      - name: Setup Java environment
        if: env.INSTALL_JAVA == 'true'
        uses: actions/setup-java@v4
        with:
          distribution: "temurin"
          java-version: ${{ env.JAVA_VERSION }}
      - name: Set up Ruby
        uses: ruby/setup-ruby@v1
        with:
          ruby-version: ${{ env.RUBY_VERSION }}
          bundler-cache: false
      - name: Cache Yarn
        uses: ./.github/actions/cache-yarn
        with:
          path: |
            .yarn/cache
            node_modules
            app/node_modules
          cache-version: ${{ env.GH_CACHE_VERSION }}-${{ env.NODE_VERSION_SANITIZED }}
      - name: Cache Ruby gems
        uses: ./.github/actions/cache-bundler
        with:
          path: app/vendor/bundle
          lock-file: app/Gemfile.lock
          cache-version: ${{ env.GH_CACHE_VERSION }}-${{ env.GH_GEMS_CACHE_VERSION }}-ruby${{ env.RUBY_VERSION }}
      - name: Cache Pods
        uses: ./.github/actions/cache-pods
        with:
          path: |
            app/ios/Pods
            ~/Library/Caches/CocoaPods
          lockfile: app/ios/Podfile.lock
          cache-version: ${{ env.GH_CACHE_VERSION }}
      - name: Cache Xcode build
        uses: actions/cache@v4
        with:
          path: |
            app/ios/build
            ~/Library/Developer/Xcode/DerivedData
            ~/Library/Caches/com.apple.dt.Xcode
          key: ${{ runner.os }}-xcode-${{ env.XCODE_VERSION }}-${{ hashFiles('app/ios/Podfile.lock', 'app/ios/OpenPassport.xcworkspace/contents.xcworkspacedata', 'app/ios/Self.xcworkspace/contents.xcworkspacedata') }}
          restore-keys: |
            ${{ runner.os }}-xcode-${{ env.XCODE_VERSION }}-${{ hashFiles('app/ios/Podfile.lock') }}-
            ${{ runner.os }}-xcode-${{ env.XCODE_VERSION }}-
      - name: Cache Xcode Index
        uses: actions/cache@v4
        with:
          path: app/ios/build/Index.noindex
          key: ${{ runner.os }}-xcode-index-${{ env.XCODE_VERSION }}-${{ hashFiles('app/ios/Podfile.lock') }}
          restore-keys: |
            ${{ runner.os }}-xcode-index-${{ env.XCODE_VERSION }}-
      - name: Generate token for self repositories
        if: ${{ github.event_name != 'pull_request' || github.event.pull_request.head.repo.fork == false }}
        uses: ./.github/actions/generate-github-token
        id: github-token
        with:
          app-id: ${{ vars.GH_WORKFLOWS_CROSS_ACCESS_ID }}
          private-key: ${{ secrets.GH_WORKFLOWS_CROSS_ACCESS_KEY }}
          configure-netrc: "true"
      - name: Install Mobile Dependencies
        uses: ./.github/actions/yarn-install
      - name: Cache Built Dependencies
        id: built-deps
        uses: ./.github/actions/cache-built-deps
        with:
          cache-version: ${{ env.GH_CACHE_VERSION }}-${{ env.NODE_VERSION_SANITIZED }}
      - name: Build dependencies (cache miss)
        # if: steps.built-deps.outputs.cache-hit != 'true'
        env:
          SELFXYZ_APP_TOKEN: ${{ steps.github-token.outputs.token || '' }}
        run: |
          echo "Cache miss for built dependencies. Building now..."
          yarn workspace @selfxyz/mobile-app run build:deps
      - name: Install Ruby Dependencies
        run: |
          echo "Installing Ruby dependencies..."
          bundle config set --local path 'vendor/bundle'
          bundle install --jobs 4 --retry 3
        working-directory: ./app
      - name: Install iOS Dependencies
        uses: nick-fields/retry@v3
        with:
          timeout_minutes: 20
          max_attempts: 3
          retry_wait_seconds: 10
          command: |
            cd app/ios
            bundle exec bash scripts/pod-install-with-cache-fix.sh
        env:
          SELFXYZ_APP_TOKEN: ${{ steps.github-token.outputs.token }}
      - name: Resolve iOS workspace
        run: |
          WORKSPACE_OPEN="ios/OpenPassport.xcworkspace"
          WORKSPACE_SELF="ios/Self.xcworkspace"

          if xcodebuild -list -workspace "$WORKSPACE_OPEN" 2>/dev/null | grep -q "OpenPassport"; then
            WORKSPACE_PATH="$WORKSPACE_OPEN"
          else
            WORKSPACE_PATH="$WORKSPACE_SELF"
          fi

          echo "WORKSPACE_PATH=$WORKSPACE_PATH" >> "$GITHUB_ENV"
          echo "Resolved workspace: $WORKSPACE_PATH"
        working-directory: ./app
      - name: Verify iOS Workspace
        run: |
          echo "Verifying iOS workspace setup..."

          if [ -z "$WORKSPACE_PATH" ]; then
            echo "❌ WORKSPACE_PATH is not set"
            exit 1
          fi

          if [ ! -d "$WORKSPACE_PATH" ]; then
            echo "❌ Workspace not found at: $WORKSPACE_PATH"
            echo "Available workspaces:"
            find ios -name "*.xcworkspace" -type d
            exit 1
          fi

          if [ ! -d "ios/Pods" ]; then
            echo "❌ Pods directory is missing"
            exit 1
          fi

          # Verify scheme exists by listing available schemes
          echo "Verifying scheme availability..."
          AVAILABLE_SCHEMES=$(xcodebuild -list -workspace "$WORKSPACE_PATH" 2>/dev/null | grep -A 200 "Schemes:" | grep -v "Schemes:" | xargs)
          echo "Available schemes (first 20): $(echo $AVAILABLE_SCHEMES | cut -d' ' -f1-20)..."

          if [[ ! "$AVAILABLE_SCHEMES" =~ ${{ env.IOS_PROJECT_SCHEME }} ]]; then
            echo "❌ Scheme '${{ env.IOS_PROJECT_SCHEME }}' not found"
            echo "Full scheme list:"
            xcodebuild -list -workspace "$WORKSPACE_PATH" 2>/dev/null | grep -A 200 "Schemes:" | grep -v "Schemes:" | head -50
            exit 1
          fi

          echo "✅ iOS workspace is properly configured"
          echo "✅ Using workspace: $WORKSPACE_PATH"
          echo "✅ Using scheme: ${{ env.IOS_PROJECT_SCHEME }}"
        working-directory: ./app
      - name: Build iOS
        run: |
          echo "Building iOS app for simulator (no signing required)..."
          echo "Project: ${{ env.IOS_PROJECT_NAME }}, Scheme: ${{ env.IOS_PROJECT_SCHEME }}"
          # Build for iOS Simulator to avoid code signing issues in CI
          xcodebuild -workspace "$WORKSPACE_PATH" \
            -scheme ${{ env.IOS_PROJECT_SCHEME }} \
            -configuration Release \
            -sdk iphonesimulator \
            -destination "generic/platform=iOS Simulator" \
            -derivedDataPath ios/build \
            -jobs "$(sysctl -n hw.ncpu)" \
            -parallelizeTargets \
            -quiet || { echo "❌ iOS build failed"; exit 1; }
          echo "✅ iOS build succeeded"
        working-directory: ./app

  build-android:
    runs-on: ubuntu-latest
    needs: build-deps
    # This is mostly covered in mobile-e2e.yml so we don't need to run it here frequently
    if: github.event_name == 'workflow_dispatch'
    timeout-minutes: 60
    steps:
      - uses: actions/checkout@v6
      - name: Read and sanitize Node.js version
        shell: bash
        run: |
          if [ ! -f .nvmrc ] || [ -z "$(cat .nvmrc)" ]; then
            echo "❌ .nvmrc is missing or empty"; exit 1;
          fi
          VERSION="$(tr -d '\r\n' < .nvmrc)"
          VERSION="${VERSION#v}"
          if ! [[ "$VERSION" =~ ^[0-9]+(\.[0-9]+){0,2}$ ]]; then
            echo "Invalid .nvmrc content: '$VERSION'"; exit 1;
          fi
          echo "NODE_VERSION=$VERSION" >> "$GITHUB_ENV"
          echo "NODE_VERSION_SANITIZED=${VERSION//\//-}" >> "$GITHUB_ENV"
      - name: Use Node.js
        uses: actions/setup-node@v4
        with:
          node-version: ${{ env.NODE_VERSION }}
      - name: Enable Corepack
        run: corepack enable
      - name: Activate Yarn 4.6.0
        run: corepack prepare yarn@4.6.0 --activate
      - name: Cache Yarn
        uses: ./.github/actions/cache-yarn
        with:
          path: |
            .yarn/cache
            node_modules
            app/node_modules
          cache-version: ${{ env.GH_CACHE_VERSION }}-${{ env.NODE_VERSION_SANITIZED }}
      - name: Free up disk space
        uses: ./.github/actions/free-disk-space
      - name: Cache Gradle
        uses: ./.github/actions/cache-gradle
        with:
          cache-version: ${{ env.GH_CACHE_VERSION }}
      - name: Setup Java environment
        uses: actions/setup-java@v4
        with:
          distribution: "temurin"
          java-version: ${{ env.JAVA_VERSION }}
      - name: Setup Android SDK
        uses: android-actions/setup-android@v3
        with:
          accept-android-sdk-licenses: true
      - name: Cache NDK
        id: ndk-cache
        uses: actions/cache@v4
        with:
          path: ${{ env.ANDROID_HOME }}/ndk/${{ env.ANDROID_NDK_VERSION }}
          key: ${{ runner.os }}-ndk-${{ env.ANDROID_NDK_VERSION }}
      - name: Install NDK
        if: steps.ndk-cache.outputs.cache-hit != 'true'
        uses: nick-fields/retry@v3
        with:
          timeout_minutes: 15
          max_attempts: 3
          retry_wait_seconds: 10
          command: sdkmanager "ndk;${{ env.ANDROID_NDK_VERSION }}"
      - name: Install Mobile Dependencies
        uses: ./.github/actions/yarn-install
      - name: Cache Built Dependencies
        id: built-deps
        uses: ./.github/actions/cache-built-deps
        with:
          cache-version: ${{ env.GH_CACHE_VERSION }}-${{ env.NODE_VERSION_SANITIZED }}
      - name: Build dependencies (cache miss)
        if: steps.built-deps.outputs.cache-hit != 'true'
        run: |
          echo "Cache miss for built dependencies. Building now..."
          yarn workspace @selfxyz/mobile-app run build:deps
      - name: Generate token for self repositories
        if: ${{ github.event_name != 'pull_request' || github.event.pull_request.head.repo.fork == false }}
        uses: ./.github/actions/generate-github-token
        id: github-token
        with:
          app-id: ${{ vars.GH_WORKFLOWS_CROSS_ACCESS_ID }}
          private-key: ${{ secrets.GH_WORKFLOWS_CROSS_ACCESS_KEY }}
      - name: Setup Android private modules
        run: |
          cd ${{ env.APP_PATH }}
          PLATFORM=android node scripts/setup-private-modules.cjs
        env:
          SELFXYZ_APP_TOKEN: ${{ steps.github-token.outputs.token }}
          CI: true
      - name: Build Android (with AAPT2 symlink fix)
        run: yarn android:ci
        working-directory: ./app
      - name: Clean up Gradle build artifacts
        uses: ./.github/actions/cleanup-gradle-artifacts
