version: '3'

includes:
  common: ../Taskfile.yml

vars:
  APP_ID: '{{.APP_ID | default "com.wails.app"}}'
  MIN_SDK: '21'
  TARGET_SDK: '34'
  # The emulator runs the host architecture; physical devices are arm64
  HOST_ARCH:
    sh: '[ "$(uname -m)" = "x86_64" ] && echo "amd64" || echo "arm64"'
  # System-image ABI for the host, used in the "create an AVD" hint below.
  ANDROID_ABI:
    sh: '[ "$(uname -m)" = "arm64" ] && echo "arm64-v8a" || echo "x86_64"'
  # SDK location: $ANDROID_HOME / $ANDROID_SDK_ROOT, else the per-OS default
  # (macOS: ~/Library/Android/sdk, Linux/other: ~/Android/Sdk)
  SDK_ROOT:
    sh: 'echo "${ANDROID_HOME:-${ANDROID_SDK_ROOT:-$([ -d "$HOME/Library/Android/sdk" ] && echo "$HOME/Library/Android/sdk" || echo "$HOME/Android/Sdk")}}"'
  ADB:
    sh: 'command -v adb || echo "${ANDROID_HOME:-${ANDROID_SDK_ROOT:-$([ -d "$HOME/Library/Android/sdk" ] && echo "$HOME/Library/Android/sdk" || echo "$HOME/Android/Sdk")}}/platform-tools/adb"'
  EMULATOR:
    sh: 'command -v emulator || echo "${ANDROID_HOME:-${ANDROID_SDK_ROOT:-$([ -d "$HOME/Library/Android/sdk" ] && echo "$HOME/Library/Android/sdk" || echo "$HOME/Android/Sdk")}}/emulator/emulator"'

tasks:
  install:deps:
    summary: Check and install Android development dependencies
    cmds:
      - go run build/android/scripts/deps/install_deps.go
    env:
      TASK_FORCE_YES: '{{if .YES}}true{{else}}false{{end}}'
    prompt: This will check and install Android development dependencies. Continue?

  build:
    summary: Creates a debug build of the application for Android
    deps:
      - task: common:go:mod:tidy
      - task: generate:android:overlay
      - task: common:build:frontend
        vars:
          BUILD_FLAGS:
            ref: .BUILD_FLAGS
          PRODUCTION:
            ref: .PRODUCTION
    cmds:
      - echo "Building Android app {{.APP_NAME}}..."
      - task: compile:go:shared
        vars:
          ARCH: '{{.ARCH | default .HOST_ARCH}}'
    vars:
      BUILD_FLAGS: '{{if eq .PRODUCTION "true"}}-tags production,android -trimpath -buildvcs=false -ldflags="-w -s"{{else}}-tags android,debug -buildvcs=false -gcflags=all="-l"{{end}}'
    env:
      PRODUCTION: '{{.PRODUCTION | default "false"}}'

  compile:go:shared:
    summary: Compile Go code to shared library (.so)
    cmds:
      - |
        # Locate the NDK: $ANDROID_NDK_HOME, or the newest installed NDK
        NDK_ROOT="$ANDROID_NDK_HOME"
        if [ -z "$NDK_ROOT" ]; then
          SDK_ROOT="{{.SDK_ROOT}}"
          NDK_ROOT=$(ls -d "$SDK_ROOT"/ndk/* 2>/dev/null | sort -V | tail -1)
        fi
        if [ -z "$NDK_ROOT" ] || [ ! -d "$NDK_ROOT" ]; then
          echo "Error: Android NDK not found"
          echo "Install one with: sdkmanager 'ndk;26.3.11579264' (or set ANDROID_NDK_HOME)"
          exit 1
        fi

        # Determine toolchain based on host OS
        case "$(uname -s)" in
          Darwin) HOST_TAG="darwin-x86_64" ;;
          Linux)  HOST_TAG="linux-x86_64" ;;
          *)      echo "Unsupported host OS"; exit 1 ;;
        esac

        TOOLCHAIN="$NDK_ROOT/toolchains/llvm/prebuilt/$HOST_TAG"

        # Set compiler based on architecture
        case "{{.ARCH}}" in
          arm64)
            export CC="$TOOLCHAIN/bin/aarch64-linux-android{{.MIN_SDK}}-clang"
            export CXX="$TOOLCHAIN/bin/aarch64-linux-android{{.MIN_SDK}}-clang++"
            export GOARCH=arm64
            JNI_DIR="arm64-v8a"
            ;;
          amd64|x86_64)
            export CC="$TOOLCHAIN/bin/x86_64-linux-android{{.MIN_SDK}}-clang"
            export CXX="$TOOLCHAIN/bin/x86_64-linux-android{{.MIN_SDK}}-clang++"
            export GOARCH=amd64
            JNI_DIR="x86_64"
            ;;
          *)
            echo "Unsupported architecture: {{.ARCH}}"
            exit 1
            ;;
        esac

        export CGO_ENABLED=1
        export GOOS=android

        mkdir -p {{.BIN_DIR}}
        mkdir -p build/android/app/src/main/jniLibs/$JNI_DIR

        go build -buildmode=c-shared -overlay build/android/overlay.json {{.BUILD_FLAGS}} \
          -o build/android/app/src/main/jniLibs/$JNI_DIR/libwails.so
    vars:
      BUILD_FLAGS: '{{if eq .PRODUCTION "true"}}-tags production,android -trimpath -buildvcs=false -ldflags="-w -s"{{else}}-tags android,debug -buildvcs=false -gcflags=all="-l"{{end}}'

  compile:go:all-archs:
    summary: Compile Go code for all Android architectures (fat APK)
    cmds:
      - task: compile:go:shared
        vars:
          ARCH: arm64
      - task: compile:go:shared
        vars:
          ARCH: amd64

  package:
    summary: Packages a production build of the application into a signed release APK
    deps:
      - task: build
        vars:
          PRODUCTION: "true"
          ARCH: '{{.ARCH | default .HOST_ARCH}}'
    cmds:
      - task: assemble:apk:release

  package:fat:
    summary: Packages a production build for all architectures (fat APK)
    deps:
      - task: build
        vars:
          PRODUCTION: "true"
          ARCH: arm64
    cmds:
      - task: compile:go:shared
        vars:
          ARCH: amd64
          PRODUCTION: "true"
      - task: assemble:apk:release

  assemble:apk:
    summary: Assembles a debug APK using Gradle
    preconditions:
      - sh: 'command -v java >/dev/null || [ -n "$JAVA_HOME" ]'
        msg: "Java not found. Install a JDK (e.g. brew install openjdk@21) and/or set JAVA_HOME"
    cmds:
      - |
        cd build/android
        # The exec bit is lost when gradlew is extracted from the embedded
        # build assets, so restore it before invoking the wrapper.
        chmod +x ./gradlew
        ./gradlew assembleDebug
        cp app/build/outputs/apk/debug/app-debug.apk "../../{{.BIN_DIR}}/{{.APP_NAME}}.apk"
        echo "APK created: {{.BIN_DIR}}/{{.APP_NAME}}.apk"

  assemble:apk:release:
    summary: Assembles a release APK using Gradle (signed with the debug keystore unless ANDROID_KEYSTORE_FILE is set)
    preconditions:
      - sh: 'command -v java >/dev/null || [ -n "$JAVA_HOME" ]'
        msg: "Java not found. Install a JDK (e.g. brew install openjdk@21) and/or set JAVA_HOME"
    cmds:
      - |
        cd build/android
        # The exec bit is lost when gradlew is extracted from the embedded
        # build assets, so restore it before invoking the wrapper.
        chmod +x ./gradlew
        ./gradlew assembleRelease
        cp app/build/outputs/apk/release/app-release.apk "../../{{.BIN_DIR}}/{{.APP_NAME}}.apk"
        echo "Release APK created: {{.BIN_DIR}}/{{.APP_NAME}}.apk"

  generate:android:overlay:
    internal: true
    summary: Generate Go build overlay that registers the Android main
    sources:
      - build/config.yml
    generates:
      - build/android/overlay.json
      - build/android/gen/main_android.gen.go
    cmds:
      - wails3 android overlay:gen -out build/android/overlay.json -config build/config.yml

  generate:android:bindings:
    internal: true
    summary: Generates bindings for Android
    sources:
      - "**/*.go"
      - go.mod
      - go.sum
    generates:
      - frontend/bindings/**/*
    cmds:
      # Bindings are generated from the Go AST; CGO is disabled so the NDK
      # is not required for this step
      - wails3 generate bindings -f '-tags android' -clean=true
    env:
      GOOS: android
      CGO_ENABLED: 0

  ensure-emulator:
    internal: true
    summary: Ensure Android Emulator is running
    silent: true
    cmds:
      - |
        # Check if an emulator is already running
        if "{{.ADB}}" devices | grep -q "emulator"; then
          echo "Emulator already running"
          exit 0
        fi

        # Get first available AVD
        AVD_NAME=$("{{.EMULATOR}}" -list-avds | tail -1)
        if [ -z "$AVD_NAME" ]; then
          echo "No Android Virtual Devices found. Create one with:"
          echo "  avdmanager create avd --name wails --package 'system-images;android-34;google_apis;{{.ANDROID_ABI}}' --device pixel_7"
          exit 1
        fi

        echo "Starting emulator: $AVD_NAME"
        # Start the emulator daemonized so it outlives this task step. go-task's
        # shell tracks background jobs by PID and reaps them when the command's
        # interpreter finishes (which nohup/setsid alone don't prevent — the kill
        # is direct), so a bare `emulator &` is gone before the later
        # install/launch steps run. Launch it from a short-lived child shell that
        # backgrounds the emulator and exits immediately: the emulator is then
        # reparented to init/launchd and go-task's shell has no handle to reap it.
        nohup sh -c "'{{.EMULATOR}}' -avd '$AVD_NAME' -no-snapshot-load </dev/null >/dev/null 2>&1 &" >/dev/null 2>&1

        # Wait for emulator to boot (max 120 seconds)
        echo "Waiting for emulator to boot..."
        "{{.ADB}}" wait-for-device

        for i in $(seq 1 120); do
          BOOT_COMPLETED=$("{{.ADB}}" shell getprop sys.boot_completed 2>/dev/null | tr -d '\r')
          if [ "$BOOT_COMPLETED" = "1" ]; then
            echo "Emulator booted successfully"
            exit 0
          fi
          sleep 1
        done

        echo "Emulator boot timeout"
        exit 1
    preconditions:
      - sh: '[ -x "{{.ADB}}" ] || command -v adb'
        msg: "adb not found. Install the Android SDK platform-tools (or set ANDROID_HOME)"
      - sh: '[ -x "{{.EMULATOR}}" ] || command -v emulator'
        msg: "emulator not found. Install the Android SDK emulator package (or set ANDROID_HOME)"

  deploy-emulator:
    summary: Deploy the packaged release APK to the Android Emulator
    deps: [package]
    cmds:
      - task: ensure-emulator
      - '"{{.ADB}}" uninstall {{.APP_ID}} 2>/dev/null || true'
      - '"{{.ADB}}" install "{{.BIN_DIR}}/{{.APP_NAME}}.apk"'
      - '"{{.ADB}}" shell am start -n {{.APP_ID}}/com.wails.app.MainActivity'

  run:
    summary: Build, install and launch a debug build in the Android Emulator
    deps:
      - task: ensure-emulator
      - task: build
    cmds:
      - task: assemble:apk
      - '"{{.ADB}}" uninstall {{.APP_ID}} 2>/dev/null || true'
      - '"{{.ADB}}" install "{{.BIN_DIR}}/{{.APP_NAME}}.apk"'
      - '"{{.ADB}}" shell am start -n {{.APP_ID}}/com.wails.app.MainActivity'

  logs:
    summary: Stream Android logcat filtered to this app
    cmds:
      - '"{{.ADB}}" logcat -v time | grep -E "(Wails|{{.APP_NAME}})" || true'

  logs:all:
    summary: Stream all Android logcat (verbose)
    cmds:
      - '"{{.ADB}}" logcat -v time'

  clean:
    summary: Clean build artifacts
    cmds:
      - rm -rf {{.BIN_DIR}}
      - rm -rf build/android/app/build
      - rm -rf build/android/app/src/main/jniLibs/*/libwails.so
      - rm -rf build/android/.gradle
