name: Unity Build

on:
  push:
    branches: [ "main" ]
    paths:
      - ".github/workflows/unity_build.yml"
      - "src/main/csharp/net/eagle0/clients/unity/**"
      - "src/main/protobuf/net/eagle0/common/**"
      - "src/main/protobuf/net/eagle0/shardok/**"
      - "src/main/protobuf/net/eagle0/eagle/api/**"
      - "src/main/protobuf/net/eagle0/eagle/common/**"
      - "src/main/protobuf/net/eagle0/eagle/views/**"
      - "scripts/build_protos.sh"
      - "scripts/build_plugins.sh"
      - "scripts/build_windows_plugin.sh"
      - "ci/github_actions/build_unity.sh"
      - "ci/github_actions/upload_addressables.sh"
      - "ci/github_actions/ensure_unity_installed.sh"
      - "src/main/csharp/net/eagle0/clients/unity/eagle0/ProjectSettings/ProjectVersion.txt"
      - "MODULE.bazel"
      - "WORKSPACE"
  workflow_dispatch:
  pull_request:
    paths:
      - ".github/workflows/unity_build.yml"
      - "src/main/csharp/net/eagle0/clients/unity/**"
      - "src/main/protobuf/net/eagle0/common/**"
      - "src/main/protobuf/net/eagle0/shardok/**"
      - "src/main/protobuf/net/eagle0/eagle/api/**"
      - "src/main/protobuf/net/eagle0/eagle/common/**"
      - "src/main/protobuf/net/eagle0/eagle/views/**"
      - "scripts/build_protos.sh"
      - "scripts/build_plugins.sh"
      - "scripts/build_windows_plugin.sh"
      - "ci/github_actions/build_unity.sh"
      - "ci/github_actions/upload_addressables.sh"
      - "ci/github_actions/ensure_unity_installed.sh"
      - "src/main/csharp/net/eagle0/clients/unity/eagle0/ProjectSettings/ProjectVersion.txt"
      - "MODULE.bazel"
      - "WORKSPACE"

concurrency:
  group: ${{ github.workflow }}-${{ github.head_ref || github.ref }}
  cancel-in-progress: ${{ github.event_name == 'pull_request' }}

permissions:
  contents: read

env:
  # Runner-specific build directory to allow parallel builds on multiple runners
  EAGLE0_BUILD_DIR: /tmp/eagle0-${{ github.run_id }}

jobs:
  windows-unity:
    runs-on: [self-hosted, macOS, unity-windows]
    outputs:
      deployed_version: ${{ steps.get-version.outputs.deployed_version }}

    steps:
      - name: Prune stale PR refs
        run: |
          # Self-hosted runners persist .git between runs. When a PR is updated,
          # old local refs (refs/remotes/pull/*/merge) may point to commits whose
          # objects were never fetched or have been pruned. Remove these stale refs
          # before checkout to prevent "missing object" errors.
          if [ -d ".git" ]; then
            echo "Pruning stale PR refs..."
            git for-each-ref --format='%(refname)' refs/remotes/pull/ 2>/dev/null | \
              xargs -r git update-ref -d 2>/dev/null || true
          fi
      - uses: actions/checkout@v6
        with:
          lfs: false  # Fetch LFS after checkout to avoid stale ref issues
          clean: false  # Library/ persists between runs on self-hosted runners

      - name: Clean stale files
        run: |
          git clean -ffd
          LIBRARY_DIR="src/main/csharp/net/eagle0/clients/unity/eagle0/Library"
          VERSION_CACHE="$LIBRARY_DIR/.last_unity_version"
          PROJECT_VERSION_FILE="src/main/csharp/net/eagle0/clients/unity/eagle0/ProjectSettings/ProjectVersion.txt"
          CURRENT_VERSION=$(grep "m_EditorVersion:" "$PROJECT_VERSION_FILE" | head -1 | sed 's/m_EditorVersion: //')

          # Nuke Library/ when Unity version changes to avoid import loops
          if [ -f "$VERSION_CACHE" ]; then
            CACHED_VERSION=$(cat "$VERSION_CACHE")
            if [ "$CACHED_VERSION" != "$CURRENT_VERSION" ]; then
              echo "Unity version changed ($CACHED_VERSION -> $CURRENT_VERSION) -- clearing Library/"
              rm -rf "$LIBRARY_DIR"
            fi
          fi

          # Only clear Bee/ when C# files were added/deleted/renamed (structural
          # changes that stale the DAG). Content-only modifications are handled by
          # Bee's incremental compilation. See persist_library.sh for background.
          BEE_DIR="$LIBRARY_DIR/Bee"
          SHA_FILE="$LIBRARY_DIR/.last_built_sha"
          if [ -f "$SHA_FILE" ] && [ -d "$BEE_DIR" ]; then
            LAST_SHA=$(cat "$SHA_FILE")
            if git diff --diff-filter=ADR --name-only "$LAST_SHA" HEAD -- '*.cs' '*.csproj' '*.asmdef' 2>/dev/null | grep -q .; then
              echo "C# files added/deleted/renamed since $LAST_SHA -- clearing Bee/"
              rm -rf "$BEE_DIR"
            else
              echo "No structural C# changes since $LAST_SHA -- keeping Bee/"
            fi
          else
            echo "No previous build SHA or no Bee/ -- clearing Bee/ as safe default"
            rm -rf "$BEE_DIR"
          fi

      - name: Fetch LFS files
        run: ./ci/github_actions/fetch_lfs.sh
      - name: Ensure Unity version installed
        run: ./ci/github_actions/ensure_unity_installed.sh windows
      - name: Build Windows unity
        run: ./ci/github_actions/build_unity.sh "${{ env.EAGLE0_BUILD_DIR }}/eagle0WIN"

      - name: Save build SHA for Bee/ cache invalidation
        if: success()
        run: git rev-parse HEAD > src/main/csharp/net/eagle0/clients/unity/eagle0/Library/.last_built_sha

      - name: Save Unity version for Library/ cache invalidation
        if: success()
        run: |
          grep "m_EditorVersion:" src/main/csharp/net/eagle0/clients/unity/eagle0/ProjectSettings/ProjectVersion.txt | head -1 | sed 's/m_EditorVersion: //' > src/main/csharp/net/eagle0/clients/unity/eagle0/Library/.last_unity_version

      - name: Upload Addressables to CDN
        if: success() && github.ref == 'refs/heads/main' && github.event_name != 'pull_request'
        env:
          ACCESS_KEY_ID: ${{ secrets.ACCESS_KEY_ID }}
          SECRET_KEY: ${{ secrets.SECRET_KEY }}
        run: ./ci/github_actions/upload_addressables.sh StandaloneWindows64
      - name: Deploy Windows unity
        if: success() && github.ref == 'refs/heads/main' && github.event_name != 'pull_request'
        env:
          ACCESS_KEY_ID: ${{ secrets.ACCESS_KEY_ID }}
          SECRET_KEY: ${{ secrets.SECRET_KEY }}
        run: bazel run //src/main/go/net/eagle0/build/unity3d_windows_build_handler:unity3d_windows_build_handler -- "${{ env.EAGLE0_BUILD_DIR }}/eagle0WIN" "/tmp/unity_manifest.txt"

      - name: Update unified manifest
        if: success() && github.ref == 'refs/heads/main' && github.event_name != 'pull_request'
        env:
          ACCESS_KEY_ID: ${{ secrets.ACCESS_KEY_ID }}
          SECRET_KEY: ${{ secrets.SECRET_KEY }}
          MANIFEST_SIGNING_KEY: ${{ secrets.MANIFEST_SIGNING_KEY }}
        run: |
          # Write signing key to temp file (if available)
          SIGNING_ARGS=""
          if [ -n "$MANIFEST_SIGNING_KEY" ]; then
            echo "$MANIFEST_SIGNING_KEY" > /tmp/manifest_signing_key
            chmod 600 /tmp/manifest_signing_key
            SIGNING_ARGS="/tmp/manifest_signing_key"
            echo "Manifest signing key available"
          else
            echo "Warning: MANIFEST_SIGNING_KEY not set, manifest will be unsigned"
          fi

          # Update the v2 manifest at installer/v2/eagle0_manifest.txt
          bazel run //src/main/go/net/eagle0/build/manifest_manager:manifest_manager -- unity3d-v2 /tmp/unity_manifest.txt $SIGNING_ARGS

          # Cleanup
          rm -f /tmp/manifest_signing_key

      - name: Export deployed version
        id: get-version
        if: success() && github.ref == 'refs/heads/main' && github.event_name != 'pull_request'
        run: |
          VERSION=$(grep "^version=" /tmp/unity_manifest.txt | cut -d= -f2 || date +%Y.%m.%d)
          echo "deployed_version=$VERSION" >> $GITHUB_OUTPUT

      - name: Archive build log
        if: success() || failure()
        uses: actions/upload-artifact@v7
        with:
          name: editor_win.log
          path: ${{ env.EAGLE0_BUILD_DIR }}/editor_win.log
          retention-days: 3
      - name: Cleanup build directory
        if: always()
        run: rm -rf "${{ env.EAGLE0_BUILD_DIR }}"

  notify-windows:
    needs: windows-unity
    if: needs.windows-unity.outputs.deployed_version != ''
    runs-on: ubuntu-latest
    steps:
      - name: Notify clients of update
        env:
          NOTIFY_SECRET: ${{ secrets.EAGLE_NOTIFY_SECRET }}
        run: |
          curl -X POST "https://admin.eagle0.net/notify-update?platform=windows&version=${{ needs.windows-unity.outputs.deployed_version }}&required=false" \
            -H "X-Notify-Secret: $NOTIFY_SECRET" \
            --fail --silent --show-error || echo "Warning: Failed to notify clients (non-fatal)"
