name: Build Mobile

on:
  workflow_call:
    inputs:
      ref:
        required: false
        type: string
      environment:
        description: 'Target environment'
        required: true
        default: 'development'
        type: string
    secrets:
      KEY_JKS:
        required: true
      ALIAS:
        required: true
      ANDROID_KEY_PASSWORD:
        required: true
      ANDROID_STORE_PASSWORD:
        required: true
      APP_STORE_CONNECT_API_KEY_ID:
        required: true
      APP_STORE_CONNECT_API_KEY_ISSUER_ID:
        required: true
      APP_STORE_CONNECT_API_KEY:
        required: true
      MATCH_PASSWORD:
        required: true
      MATCH_GIT_BASIC_AUTHORIZATION:
        required: true
      FASTLANE_TEAM_ID:
        required: true
  pull_request:
  push:
    branches: [main]

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

permissions: {}

jobs:
  pre-job:
    runs-on: ubuntu-latest
    permissions:
      contents: read
    outputs:
      should_run: ${{ steps.check.outputs.should_run }}
    steps:
      - id: token
        uses: immich-app/devtools/actions/create-workflow-token@da177fa133657503ddb7503f8ba53dccefec5da1 # create-workflow-token-action-v1.0.0
        with:
          app-id: ${{ secrets.PUSH_O_MATIC_APP_ID }}
          private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }}

      - name: Check what should run
        id: check
        uses: immich-app/devtools/actions/pre-job@08bac802a312fc89808e0dd589271ca0974087b5 # pre-job-action-v2.0.0
        with:
          github-token: ${{ steps.token.outputs.token }}
          filters: |
            mobile:
              - 'mobile/**'
          force-filters: |
            - '.github/workflows/build-mobile.yml'
          force-events: 'workflow_call,workflow_dispatch'

  build-sign-android:
    name: Build and sign Android
    needs: pre-job
    permissions:
      contents: read
    # Skip when PR from a fork
    if: ${{ !github.event.pull_request.head.repo.fork && github.actor != 'dependabot[bot]' && fromJSON(needs.pre-job.outputs.should_run).mobile == true }}
    runs-on: mich

    steps:
      - id: token
        uses: immich-app/devtools/actions/create-workflow-token@da177fa133657503ddb7503f8ba53dccefec5da1 # create-workflow-token-action-v1.0.0
        with:
          app-id: ${{ secrets.PUSH_O_MATIC_APP_ID }}
          private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }}

      - uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1
        with:
          ref: ${{ inputs.ref || github.sha }}
          persist-credentials: false
          token: ${{ steps.token.outputs.token }}

      - name: Create the Keystore
        env:
          KEY_JKS: ${{ secrets.KEY_JKS }}
        working-directory: ./mobile
        run: printf "%s" $KEY_JKS | base64 -d > android/key.jks

      - uses: actions/setup-java@f2beeb24e141e01a676f977032f5a29d81c9e27e # v5.1.0
        with:
          distribution: 'zulu'
          java-version: '17'

      - name: Restore Gradle Cache
        id: cache-gradle-restore
        uses: actions/cache/restore@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0
        with:
          path: |
            ~/.gradle/caches
            ~/.gradle/wrapper
            ~/.android/sdk
            mobile/android/.gradle
            mobile/.dart_tool
          key: build-mobile-gradle-${{ runner.os }}-main

      - name: Setup Flutter SDK
        uses: subosito/flutter-action@fd55f4c5af5b953cc57a2be44cb082c8f6635e8e # v2.21.0
        with:
          channel: 'stable'
          flutter-version-file: ./mobile/pubspec.yaml
          cache: true

      - name: Setup Android SDK
        uses: android-actions/setup-android@9fc6c4e9069bf8d3d10b2204b1fb8f6ef7065407 # v3.2.2
        with:
          packages: ''

      - name: Get Packages
        working-directory: ./mobile
        run: flutter pub get

      - name: Generate translation file
        run: dart run easy_localization:generate -S ../i18n && dart run bin/generate_keys.dart
        working-directory: ./mobile

      - name: Generate platform APIs
        run: make pigeon
        working-directory: ./mobile

      - name: Build Android App Bundle
        working-directory: ./mobile
        env:
          ALIAS: ${{ secrets.ALIAS }}
          ANDROID_KEY_PASSWORD: ${{ secrets.ANDROID_KEY_PASSWORD }}
          ANDROID_STORE_PASSWORD: ${{ secrets.ANDROID_STORE_PASSWORD }}
          IS_MAIN: ${{ github.ref == 'refs/heads/main' }}
        run: |
          if [[ $IS_MAIN == 'true' ]]; then
            flutter build apk --release
            flutter build apk --release --split-per-abi --target-platform android-arm,android-arm64,android-x64
          else
            flutter build apk --debug --split-per-abi --target-platform android-arm64
          fi

      - name: Publish Android Artifact
        uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0
        with:
          name: release-apk-signed
          path: mobile/build/app/outputs/flutter-apk/*.apk

      - name: Save Gradle Cache
        id: cache-gradle-save
        uses: actions/cache/save@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0
        if: github.ref == 'refs/heads/main'
        with:
          path: |
            ~/.gradle/caches
            ~/.gradle/wrapper
            ~/.android/sdk
            mobile/android/.gradle
            mobile/.dart_tool
          key: ${{ steps.cache-gradle-restore.outputs.cache-primary-key }}

  build-sign-ios:
    name: Build and sign iOS
    needs: pre-job
    permissions:
      contents: read
    # Run on main branch or workflow_dispatch, or on PRs/other branches (build only, no upload)
    if: ${{ !github.event.pull_request.head.repo.fork && fromJSON(needs.pre-job.outputs.should_run).mobile == true }}
    runs-on: macos-latest

    steps:
      - name: Generate token for ios-certs repo
        id: token
        uses: actions/create-github-app-token@v2
        with:
          app-id: ${{ secrets.PUSH_O_MATIC_APP_ID }}
          private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }}
          owner: immich-app
          repositories: immich,ios-certs

      - name: Set up match authorization
        id: match-auth
        run: |
          # Create base64-encoded authorization for match
          echo "base64_token=$(echo -n 'x-access-token:${{ steps.token.outputs.token }}' | base64)" >> $GITHUB_OUTPUT

      - name: Checkout code
        uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5
        with:
          ref: ${{ inputs.ref || github.sha }}
          persist-credentials: false

      - name: Setup Flutter SDK
        uses: subosito/flutter-action@fd55f4c5af5b953cc57a2be44cb082c8f6635e8e # v2
        with:
          channel: 'stable'
          flutter-version-file: ./mobile/pubspec.yaml
          cache: true

      - name: Install Flutter dependencies
        working-directory: ./mobile
        run: flutter pub get

      - name: Generate translation files
        run: dart run easy_localization:generate -S ../i18n && dart run bin/generate_keys.dart
        working-directory: ./mobile

      - name: Generate platform APIs
        run: make pigeon
        working-directory: ./mobile

      - name: Setup Ruby
        uses: ruby/setup-ruby@v1
        with:
          ruby-version: '3.3'
          bundler-cache: true
          working-directory: ./mobile/ios

      - name: Install CocoaPods dependencies
        working-directory: ./mobile/ios
        run: |
          pod install

      - name: Create API Key
        env:
          API_KEY_ID: ${{ secrets.APP_STORE_CONNECT_API_KEY_ID }}
          API_KEY_ISSUER_ID: ${{ secrets.APP_STORE_CONNECT_API_KEY_ISSUER_ID }}
          API_KEY_CONTENT: ${{ secrets.APP_STORE_CONNECT_API_KEY }}
        working-directory: ./mobile/ios
        run: |
          mkdir -p ~/.appstoreconnect/private_keys
          echo "$API_KEY_CONTENT" | base64 --decode > ~/.appstoreconnect/private_keys/AuthKey_${API_KEY_ID}.p8

      - name: Create keychain for match
        env:
          KEYCHAIN_PASSWORD: ${{ github.run_id }}
        run: |
          # Create a temporary keychain for CI
          security create-keychain -p "$KEYCHAIN_PASSWORD" build.keychain
          security default-keychain -s build.keychain
          security unlock-keychain -p "$KEYCHAIN_PASSWORD" build.keychain
          security set-keychain-settings -t 3600 -u build.keychain

      - name: Build and deploy to TestFlight
        env:
          FASTLANE_TEAM_ID: ${{ secrets.FASTLANE_TEAM_ID }}
          MATCH_PASSWORD: ${{ secrets.MATCH_PASSWORD }}
          MATCH_GIT_BASIC_AUTHORIZATION: ${{ steps.match-auth.outputs.base64_token }}
          KEYCHAIN_NAME: build.keychain
          KEYCHAIN_PASSWORD: ${{ github.run_id }}
          APP_STORE_CONNECT_API_KEY_ID: ${{ secrets.APP_STORE_CONNECT_API_KEY_ID }}
          APP_STORE_CONNECT_API_KEY_ISSUER_ID: ${{ secrets.APP_STORE_CONNECT_API_KEY_ISSUER_ID }}
          ENVIRONMENT: ${{ inputs.environment || 'development' }}
          GITHUB_REF: ${{ github.ref }}
        working-directory: ./mobile/ios
        run: |
          # Only upload to TestFlight on main branch
          if [[ "$GITHUB_REF" == "refs/heads/main" ]]; then
            if [[ "$ENVIRONMENT" == "development" ]]; then
              bundle exec fastlane gha_testflight_dev
            else
              bundle exec fastlane gha_release_prod
            fi
          else
            # Build only, no TestFlight upload for non-main branches
            bundle exec fastlane gha_build_only
          fi

      - name: Clean up keychain
        if: always()
        run: |
          security delete-keychain build.keychain || true

      - name: Upload IPA artifact
        uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0
        with:
          name: ios-release-ipa
          path: mobile/ios/Runner.ipa
