# Automated release for the Runic Gateway Android app.
#
# Trigger: pushing a version tag `v*` (e.g. `v0.1.0`). Tag-driven on purpose — the
# build never has to push to protected `main`; the tag *is* the release input.
#
# To cut a release:
#   git tag v0.1.0 && git push origin v0.1.0
#   (or create the tag from the Gitea UI). Re-build/re-release an existing tag via
#   the workflow_dispatch input below.
#
# versionName = the tag without its leading `v`; versionCode = major*10000 +
# minor*100 + patch (deterministic + monotonic, PLAN.md §10). Both are injected
# into app/build.gradle.kts for the build only — nothing is committed back to main.
#
# Prerequisites (Settings -> Actions -> Secrets on RunicGateway/Android-app):
#   REGISTRY_TOKEN  — Gitea access token with `write:repository` (create the release)
#   ANDROID_KEYSTORE_BASE64    — base64 of the release .jks (single line)
#   ANDROID_KEYSTORE_PASSWORD  — keystore password
#   ANDROID_KEY_ALIAS          — key alias (e.g. runicgateway)
#   ANDROID_KEY_PASSWORD       — key password (== store password for a PKCS12 keystore)
#
# Runner handling matches pr-checks.yml (self-hosted `ubuntu-latest`): the container
# lacks git/curl/unzip and can't reach api.adoptium.net, so we apt-install the base
# tools + JDK 17 (not actions/setup-java), install the exact SDK packages, and
# `chmod +x ./gradlew` in-step (checkout drops the exec bit).

name: Release APK

on:
  push:
    tags:
      - 'v*'
  workflow_dispatch:
    inputs:
      tag:
        description: 'Existing v* tag to (re)build and release'
        required: true

concurrency:
  group: release-apk-${{ github.event.inputs.tag || github.ref_name }}
  cancel-in-progress: false

env:
  GITEA_HOST: gitea.whitlocktech.com
  REPO: RunicGateway/Android-app
  GRADLE_MODULE: app

jobs:
  release:
    runs-on: ubuntu-latest
    # Fail fast on a genuinely wedged run (e.g. a stalled SDK/network download on
    # the self-hosted runner) instead of hanging forever and — because concurrency
    # is `cancel-in-progress: false` — blocking every later release behind it.
    timeout-minutes: 30
    steps:
      - name: Install base tools + JDK 17
        run: |
          apt-get update
          apt-get install -y git curl unzip jq openjdk-17-jdk-headless
          echo "JAVA_HOME=/usr/lib/jvm/java-17-openjdk-amd64" >> "$GITHUB_ENV"

      - name: Check out the release tag (full history for the changelog)
        uses: actions/checkout@v4
        with:
          ref: ${{ github.event.inputs.tag || github.ref_name }}
          fetch-depth: 0

      # ── Derive version + changelog straight from the tag ─────────────────
      - name: Plan the release (version + changelog from the tag)
        id: plan
        run: |
          set -euo pipefail
          mkdir -p dist
          git fetch --tags --force >/dev/null 2>&1 || true

          TAG="${{ github.event.inputs.tag || github.ref_name }}"
          case "$TAG" in
            v[0-9]*) : ;;
            *) echo "::error::expected a v* version tag, got '$TAG'"; exit 1 ;;
          esac
          VERSION="${TAG#v}"

          # versionCode: deterministic + monotonic from the semver (PLAN.md §10).
          IFS=. read -r MA MI PA <<< "$VERSION"
          : "${MA:=0}"; : "${MI:=0}"; : "${PA:=0}"
          VERSION_CODE=$(( MA*10000 + MI*100 + PA ))

          # Changelog: conventional-commit subjects since the previous v* tag.
          PREV_TAG="$(git describe --tags --match 'v*' --abbrev=0 "${TAG}^" 2>/dev/null || true)"
          if [ -n "$PREV_TAG" ]; then RANGE="${PREV_TAG}..${TAG}"; else RANGE="${TAG}"; fi
          SUBJECTS="$(git log --no-merges --format='%s' $RANGE || true)"

          {
            echo "## Runic Gateway Android ${TAG}"
            echo
            FEATS="$(echo "$SUBJECTS" | grep -E '^feat'        || true)"
            FIXES="$(echo "$SUBJECTS" | grep -E '^(fix|perf)'  || true)"
            [ -n "$FEATS" ] && { echo "### Features";  echo "$FEATS" | sed 's/^/- /'; echo; }
            [ -n "$FIXES" ] && { echo "### Fixes";     echo "$FIXES" | sed 's/^/- /'; echo; }
            echo "### All changes"
            if [ -n "$PREV_TAG" ]; then echo "Since ${PREV_TAG}:"; fi
            echo "$SUBJECTS" | sed 's/^/- /'
            echo
            echo "---"
            echo "Signed APK — sideload on Android 10+ (§10). The app self-configures its shard site on first run."
          } > dist/CHANGELOG.md

          echo "version=${VERSION}"          >> "$GITHUB_OUTPUT"
          echo "versionCode=${VERSION_CODE}" >> "$GITHUB_OUTPUT"
          echo "tag=${TAG}"                  >> "$GITHUB_OUTPUT"
          echo "==> tag=${TAG} version=${VERSION} code=${VERSION_CODE} prev_tag=${PREV_TAG:-<none>}"

      # ── SDK + signing keystore ───────────────────────────────────────────
      - name: Set up Android SDK
        uses: android-actions/setup-android@v3
        with:
          # Only put cmdline-tools on PATH. The action's default package set drags in
          # the whole emulator + the legacy `tools` package (hundreds of MB, network-
          # bound on this runner) that a headless APK build never uses. The next step
          # installs exactly the packages we need.
          packages: ''

      - name: Install Android SDK packages
        run: |
          set +o pipefail
          yes | sdkmanager "platform-tools" "platforms;android-35" "build-tools;35.0.0"

      - name: Decode signing keystore
        env:
          ANDROID_KEYSTORE_BASE64: ${{ secrets.ANDROID_KEYSTORE_BASE64 }}
        run: |
          set -euo pipefail
          if [ -z "${ANDROID_KEYSTORE_BASE64:-}" ]; then
            echo "::error::ANDROID_KEYSTORE_BASE64 secret is not set — cannot build a signed release."
            exit 1
          fi
          printf '%s' "$ANDROID_KEYSTORE_BASE64" | base64 -d > "${RUNNER_TEMP}/release.jks"
          echo "ANDROID_KEYSTORE_FILE=${RUNNER_TEMP}/release.jks" >> "$GITHUB_ENV"

      # ── Set the version, build the signed APK ────────────────────────────
      - name: Set the app version to match the tag
        run: |
          set -euo pipefail
          VERSION="${{ steps.plan.outputs.version }}"
          VERSION_CODE="${{ steps.plan.outputs.versionCode }}"
          # Replace only the version defaults (the `?: "x.y.z"` / `?: N` fallbacks).
          sed -i -E "s/(\?: )\"[0-9]+\.[0-9]+\.[0-9]+\"/\1\"${VERSION}\"/" "${GRADLE_MODULE}/build.gradle.kts"
          sed -i -E "s/(toIntOrNull\(\) \?: )[0-9]+/\1${VERSION_CODE}/"     "${GRADLE_MODULE}/build.gradle.kts"
          grep -nE "versionCode = |versionName = " "${GRADLE_MODULE}/build.gradle.kts"

      - name: Unit tests + signed release APK
        env:
          ANDROID_KEYSTORE_PASSWORD: ${{ secrets.ANDROID_KEYSTORE_PASSWORD }}
          ANDROID_KEY_ALIAS: ${{ secrets.ANDROID_KEY_ALIAS }}
          ANDROID_KEY_PASSWORD: ${{ secrets.ANDROID_KEY_PASSWORD }}
        run: |
          set -euo pipefail
          chmod +x ./gradlew
          ./gradlew --no-daemon :${GRADLE_MODULE}:testDebugUnitTest :${GRADLE_MODULE}:assembleRelease

      - name: Package APK + SHA256SUMS
        run: |
          set -euo pipefail
          SRC="${GRADLE_MODULE}/build/outputs/apk/release/app-release.apk"
          test -f "$SRC" || { echo "::error::release APK not found at $SRC"; exit 1; }
          cp "$SRC" "dist/runic-gateway-${{ steps.plan.outputs.version }}.apk"
          ( cd dist && sha256sum "runic-gateway-${{ steps.plan.outputs.version }}.apk" > SHA256SUMS )
          ls -l dist && cat dist/SHA256SUMS

      # ── Create the Gitea release + upload assets (no push to main) ───────
      - name: Create Gitea release and upload assets
        env:
          REGISTRY_TOKEN: ${{ secrets.REGISTRY_TOKEN }}
        run: |
          set -euo pipefail
          TAG="${{ steps.plan.outputs.tag }}"
          API="https://${GITEA_HOST}/api/v1/repos/${REPO}"
          BODY="$(cat dist/CHANGELOG.md)"
          CI_TOKEN="$(printf '%s' "${REGISTRY_TOKEN}" | tr -d '\r\n')"

          REL_ID="$(curl -sSf -X POST "${API}/releases" \
            -H "Authorization: token ${CI_TOKEN}" \
            -H "Content-Type: application/json" \
            -d "$(jq -n --arg tag "$TAG" --arg body "$BODY" \
                   '{tag_name:$tag, name:$tag, body:$body, draft:false, prerelease:false}')" \
            | jq -r '.id')"
          echo "Created release ${TAG} (id=${REL_ID})"

          for f in "runic-gateway-${{ steps.plan.outputs.version }}.apk" SHA256SUMS; do
            curl -sSf -X POST "${API}/releases/${REL_ID}/assets?name=${f}" \
              -H "Authorization: token ${CI_TOKEN}" \
              -F "attachment=@dist/${f}" >/dev/null
            echo "  uploaded ${f}"
          done
