name: Build & Release

on:
  push:
    tags:
      - 'v*'
  workflow_dispatch:
    inputs:
      version:
        description: 'Version to build (e.g., v1.0.0)'
        required: false

env:
  RUST_BACKTRACE: 1
  CARGO_TERM_COLOR: always

jobs:
  test:
    name: Run Tests
    runs-on: linux/amd64
    container:
      image: gitea.tourolle.paris/dtourolle/jellytau-builder:latest
    steps:
      - name: Checkout repository
        uses: actions/checkout@v4

      - name: Cache Rust dependencies
        uses: actions/cache@v3
        with:
          path: |
            ~/.cargo/registry
            ~/.cargo/git
            src-tauri/target
          key: ${{ runner.os }}-cargo-host-${{ hashFiles('**/Cargo.lock') }}
          restore-keys: |
            ${{ runner.os }}-cargo-host-

      - name: Cache Node dependencies
        uses: actions/cache@v3
        with:
          path: |
            ~/.bun/install/cache
            node_modules
          key: ${{ runner.os }}-bun-${{ hashFiles('**/bun.lock') }}
          restore-keys: |
            ${{ runner.os }}-bun-

      - name: Install dependencies
        run: bun install

      - name: Run frontend tests
        run: |
          bunx svelte-kit sync
          bun run test --run
        continue-on-error: false

      - name: Run Rust tests
        run: bun run test:rust
        continue-on-error: false

      - name: Check TypeScript
        run: bun run check
        continue-on-error: false

  build-linux:
    name: Build Linux
    runs-on: linux/amd64
    needs: test
    container:
      image: gitea.tourolle.paris/dtourolle/jellytau-builder:latest
    steps:
      - name: Checkout repository
        uses: actions/checkout@v4

      - name: Cache Rust dependencies
        uses: actions/cache@v3
        with:
          path: |
            ~/.cargo/registry
            ~/.cargo/git
            src-tauri/target
          key: ${{ runner.os }}-cargo-host-${{ hashFiles('**/Cargo.lock') }}
          restore-keys: |
            ${{ runner.os }}-cargo-host-

      - name: Cache Node dependencies
        uses: actions/cache@v3
        with:
          path: |
            ~/.bun/install/cache
            node_modules
          key: ${{ runner.os }}-bun-${{ hashFiles('**/bun.lock') }}
          restore-keys: |
            ${{ runner.os }}-bun-

      - name: Install dependencies
        run: bun install

      # The Linux job previously had no version step at all, so a tagged release
      # built Linux packages from whatever version happened to be committed.
      - name: Set app version from tag
        run: ./scripts/set-version.sh "${GITHUB_REF#refs/tags/}"
        if: startsWith(github.ref, 'refs/tags/v')

      - name: Build for Linux
        run: bun run tauri build
        env:
          TAURI_SKIP_UPDATER: true

      - name: Prepare Linux artifacts
        run: |
          mkdir -p dist/linux
          # Copy AppImage
          if [ -f "src-tauri/target/release/bundle/appimage/jellytau_"*.AppImage ]; then
            cp src-tauri/target/release/bundle/appimage/jellytau_*.AppImage dist/linux/
          fi
          # Copy .deb if built
          if [ -f "src-tauri/target/release/bundle/deb/jellytau_"*.deb ]; then
            cp src-tauri/target/release/bundle/deb/jellytau_*.deb dist/linux/
          fi
          ls -lah dist/linux/

      - name: Upload Linux build artifact
        uses: actions/upload-artifact@v3
        with:
          name: jellytau-linux
          path: dist/linux/
          retention-days: 30

  build-windows:
    name: Build Windows
    runs-on: linux/amd64
    needs: test
    # Cross-compiled from Linux via the official Tauri path (MSVC + cargo-xwin),
    # baked into the builder image. No toolchain installs here — the image has
    # cargo-xwin, clang/clang-cl, lld, llvm, nsis and the msvc target.
    container:
      image: gitea.tourolle.paris/dtourolle/jellytau-builder:latest
    steps:
      - name: Checkout repository
        uses: actions/checkout@v4

      - name: Cache Rust dependencies
        uses: actions/cache@v3
        with:
          path: |
            ~/.cargo/registry
            ~/.cargo/git
            ~/.cache/cargo-xwin
            src-tauri/target
          key: ${{ runner.os }}-cargo-windows-${{ hashFiles('**/Cargo.lock') }}
          restore-keys: |
            ${{ runner.os }}-cargo-windows-

      - name: Cache Node dependencies
        uses: actions/cache@v3
        with:
          path: |
            ~/.bun/install/cache
            node_modules
          key: ${{ runner.os }}-bun-${{ hashFiles('**/bun.lock') }}
          restore-keys: |
            ${{ runner.os }}-bun-

      # The tag is the single source of truth for a release version; the script
      # stamps every file that carries it (package.json, tauri.conf.json,
      # Cargo.toml, Cargo.lock). This step used to sed only tauri.conf.json, so
      # the other three shipped whatever was committed.
      - name: Set app version from tag
        run: ./scripts/set-version.sh "${GITHUB_REF#refs/tags/}"
        if: startsWith(github.ref, 'refs/tags/v')

      - name: Build Windows (NSIS installer + exe)
        run: OUTPUT_DIR="$PWD/dist/windows" WIN_BUNDLES=nsis ./scripts/build-windows-cross.sh

      - name: List Windows artifacts
        run: ls -lah dist/windows/

      - name: Upload Windows build artifact
        uses: actions/upload-artifact@v3
        with:
          name: jellytau-windows
          path: dist/windows/
          retention-days: 30

  build-android:
    name: Build Android
    runs-on: linux/amd64
    needs: test
    container:
      image: gitea.tourolle.paris/dtourolle/jellytau-builder:latest
    env:
      ANDROID_HOME: /opt/android-sdk
      ANDROID_SDK_ROOT: /opt/android-sdk
      ANDROID_NDK_HOME: /opt/android-sdk/ndk/27.0.11902837
    steps:
      - name: Checkout repository
        uses: actions/checkout@v4

      - name: Cache Rust dependencies
        uses: actions/cache@v3
        with:
          path: |
            ~/.cargo/registry
            ~/.cargo/git
            src-tauri/target
          key: ${{ runner.os }}-cargo-android-${{ hashFiles('**/Cargo.lock') }}
          restore-keys: |
            ${{ runner.os }}-cargo-android-

      - name: Cache Node dependencies
        uses: actions/cache@v3
        with:
          path: |
            ~/.bun/install/cache
            node_modules
          key: ${{ runner.os }}-bun-${{ hashFiles('**/bun.lock') }}
          restore-keys: |
            ${{ runner.os }}-bun-

      - name: Install dependencies
        run: bun install

      # Stamp before `android init`: it derives its generated project (including
      # the initial versionCode) from tauri.conf.json.
      - name: Set app version from tag
        run: ./scripts/set-version.sh "${GITHUB_REF#refs/tags/}"
        if: startsWith(github.ref, 'refs/tags/v')

      - name: Initialize Android project
        run: bun run tauri android init

      # Re-run after init: tauri.properties only exists now, and its
      # autogenerated versionCode (0.0.15 -> 15) is both tiny and NOT monotonic
      # against the 1000 floor already shipped in the field. The script rewrites
      # it as 1000 + major*10000 + minor*100 + patch. Runs unconditionally so
      # untagged builds get a sane code too, derived from git describe.
      - name: Pin a monotonic Android versionCode
        run: ./scripts/set-version.sh "${GITHUB_REF#refs/tags/}"

      - name: Sync custom Android sources & gradle config
        run: ./scripts/sync-android-sources.sh

      - name: Write signing keystore
        run: |
          echo "${{ secrets.ANDROID_KEYSTORE_BASE64 }}" | base64 -d > "$RUNNER_TEMP/jellytau-release.jks"
          cat > src-tauri/gen/android/keystore.properties <<EOF
          storeFile=$RUNNER_TEMP/jellytau-release.jks
          storePassword=${{ secrets.ANDROID_KEYSTORE_PASSWORD }}
          keyAlias=${{ secrets.ANDROID_KEY_ALIAS }}
          keyPassword=${{ secrets.ANDROID_KEY_PASSWORD }}
          EOF

      - name: Build signed Android APK
        run: bun run tauri android build --apk true --target aarch64

      - name: Collect & verify signed APK
        run: |
          mkdir -p dist/android
          APK=$(find src-tauri/gen/android/app/build/outputs/apk -name '*-release.apk' | head -1)
          if [ -z "$APK" ]; then echo "❌ No release APK produced"; exit 1; fi
          cp "$APK" dist/android/jellytau-release.apk
          APKSIGNER=$(find "$ANDROID_SDK_ROOT/build-tools" -name apksigner | sort -V | tail -1)
          echo "🔏 Verifying signature with $APKSIGNER"
          "$APKSIGNER" verify --print-certs dist/android/jellytau-release.apk
          ls -lah dist/android/

      - name: Upload Android build artifact
        uses: actions/upload-artifact@v3
        with:
          name: jellytau-android
          path: dist/android/
          retention-days: 30

  create-release:
    name: Create Release
    runs-on: linux/amd64
    needs: [build-linux, build-windows, build-android]
    if: startsWith(github.ref, 'refs/tags/v')
    container:
      image: gitea.tourolle.paris/dtourolle/jellytau-builder:latest
    steps:
      - name: Checkout repository
        uses: actions/checkout@v4

      - name: Get version from tag
        id: tag_name
        run: |
          echo "VERSION=${GITHUB_REF#refs/tags/}" >> $GITHUB_OUTPUT
          echo "RELEASE_NAME=JellyTau ${GITHUB_REF#refs/tags/}" >> $GITHUB_OUTPUT

      - name: Download Linux artifacts
        uses: actions/download-artifact@v3
        with:
          name: jellytau-linux
          path: artifacts/linux/

      - name: Download Windows artifacts
        uses: actions/download-artifact@v3
        with:
          name: jellytau-windows
          path: artifacts/windows/

      - name: Download Android artifacts
        uses: actions/download-artifact@v3
        with:
          name: jellytau-android
          path: artifacts/android/

      - name: Prepare release notes
        id: release_notes
        run: |
          VERSION="${{ steps.tag_name.outputs.VERSION }}"
          echo "## JellyTau $VERSION Release" > release_notes.md
          echo "" >> release_notes.md
          echo "### Downloads" >> release_notes.md
          echo "" >> release_notes.md
          echo "#### Linux" >> release_notes.md
          echo "- **AppImage** - Run directly on most Linux distributions" >> release_notes.md
          echo "- **DEB** - Install via \`sudo dpkg -i jellytau_*.deb\` (Ubuntu/Debian)" >> release_notes.md
          echo "" >> release_notes.md
          echo "#### Windows" >> release_notes.md
          echo "- **Installer (.exe)** - Run \`jellytau_*-setup.exe\` (NSIS). Unsigned — SmartScreen may warn on first run." >> release_notes.md
          echo "" >> release_notes.md
          echo "#### Android" >> release_notes.md
          echo "- **APK** - Install via \`adb install jellytau-release.apk\` or sideload via file manager" >> release_notes.md
          echo "- **AAB** - Upload to Google Play Console or testing platforms" >> release_notes.md
          echo "" >> release_notes.md
          echo "### What's New" >> release_notes.md
          echo "" >> release_notes.md
          echo "See [CHANGELOG.md](CHANGELOG.md) for detailed changes." >> release_notes.md
          echo "" >> release_notes.md
          echo "### Installation" >> release_notes.md
          echo "" >> release_notes.md
          echo "#### Linux (AppImage)" >> release_notes.md
          echo "\`\`\`bash" >> release_notes.md
          echo "chmod +x jellytau_*.AppImage" >> release_notes.md
          echo "./jellytau_*.AppImage" >> release_notes.md
          echo "\`\`\`" >> release_notes.md
          echo "" >> release_notes.md
          echo "#### Linux (DEB)" >> release_notes.md
          echo "\`\`\`bash" >> release_notes.md
          echo "sudo dpkg -i jellytau_*.deb" >> release_notes.md
          echo "jellytau" >> release_notes.md
          echo "\`\`\`" >> release_notes.md
          echo "" >> release_notes.md
          echo "#### Android" >> release_notes.md
          echo "- Sideload: Download APK and install via file manager or ADB" >> release_notes.md
          echo "- Play Store: Coming soon" >> release_notes.md
          echo "" >> release_notes.md
          echo "### Known Issues" >> release_notes.md
          echo "" >> release_notes.md
          echo "See [GitHub Issues](../../issues) for reported bugs." >> release_notes.md
          echo "" >> release_notes.md
          echo "### Requirements" >> release_notes.md
          echo "" >> release_notes.md
          echo "**Linux:**" >> release_notes.md
          echo "- 64-bit Linux system" >> release_notes.md
          echo "- GLIBC 2.29+" >> release_notes.md
          echo "" >> release_notes.md
          echo "**Android:**" >> release_notes.md
          echo "- Android 8.0 or higher" >> release_notes.md
          echo "- 50MB free storage" >> release_notes.md
          echo "" >> release_notes.md
          echo "---" >> release_notes.md
          echo "Built with Tauri, SvelteKit, and Rust" >> release_notes.md

      - name: Publish Gitea release & upload assets
        env:
          # GITEA_TOKEN (a PAT) is preferred; falls back to the auto-provided token.
          GITEA_TOKEN: ${{ secrets.GITEA_TOKEN }}
          AUTO_TOKEN: ${{ secrets.GITHUB_TOKEN }}
        run: |
          set -e
          command -v jq >/dev/null || { echo "❌ jq is required on the runner"; exit 1; }
          VERSION="${{ steps.tag_name.outputs.VERSION }}"
          API="${GITHUB_SERVER_URL}/api/v1"
          REPO="${GITHUB_REPOSITORY}"
          TOKEN="${GITEA_TOKEN:-$AUTO_TOKEN}"
          case "$VERSION" in *rc*|*beta*|*alpha*) PRE=true;; *) PRE=false;; esac

          PAYLOAD=$(jq -n \
            --arg tag "$VERSION" \
            --arg name "JellyTau $VERSION" \
            --rawfile body release_notes.md \
            --argjson pre "$PRE" \
            '{tag_name:$tag, name:$name, body:$body, draft:false, prerelease:$pre}')

          echo "📦 Creating release $VERSION on $REPO"
          # -f drops on HTTP error; capture status so an existing release (409) is handled gracefully.
          HTTP=$(curl -sS -o resp.json -w '%{http_code}' -X POST "$API/repos/$REPO/releases" \
            -H "Authorization: token $TOKEN" \
            -H "Content-Type: application/json" \
            -d "$PAYLOAD")
          if [ "$HTTP" = "201" ]; then
            RELEASE_ID=$(jq -r '.id' resp.json)
          elif [ "$HTTP" = "409" ]; then
            echo "ℹ️  Release $VERSION already exists; fetching its id to upload assets"
            RELEASE_ID=$(curl -fsS "$API/repos/$REPO/releases/tags/$VERSION" \
              -H "Authorization: token $TOKEN" | jq -r '.id')
          else
            echo "❌ Failed to create release (HTTP $HTTP):"; cat resp.json; exit 1
          fi
          echo "Release id=$RELEASE_ID"

          for f in artifacts/android/* artifacts/linux/* artifacts/windows/*; do
            [ -f "$f" ] || continue
            echo "⬆️  Uploading $(basename "$f")"
            curl -fsS -X POST \
              "$API/repos/$REPO/releases/$RELEASE_ID/assets?name=$(basename "$f")" \
              -H "Authorization: token $TOKEN" \
              -F "attachment=@$f" >/dev/null
          done
          echo "✅ Release $VERSION published with assets"
