name: CLI

on:
  push:
    branches: [ main ]
    tags:
      - "v*"     # Official releases — full pipeline (build + release + publish)
      - "test-*" # Test builds — build & smoke-check only, no release, no publish
  pull_request:

jobs:
  # Build native CLI binaries for x64 platforms
  native:
    name: native-${{ matrix.os }}
    runs-on: ${{ matrix.os }}
    strategy:
      fail-fast: false
      matrix:
        os: [ ubuntu-latest, macos-latest, windows-latest ]

    steps:
      - name: Checkout
        uses: actions/checkout@v6

      - name: Set up GraalVM (JDK 21)
        uses: graalvm/setup-graalvm@v1
        with:
          java-version: '21'
          distribution: 'graalvm-community'
          github-token: ${{ secrets.GITHUB_TOKEN }}

      - name: Align temp and Gradle dirs on D drive (Windows)
        if: runner.os == 'Windows'
        shell: pwsh
        run: |
          echo "TMP=$env:RUNNER_TEMP"        >> $env:GITHUB_ENV
          echo "TEMP=$env:RUNNER_TEMP"       >> $env:GITHUB_ENV
          echo "GRADLE_USER_HOME=$env:RUNNER_TEMP\gradle" >> $env:GITHUB_ENV

      - name: Setup Gradle
        uses: gradle/actions/setup-gradle@v5
        with:
          cache-read-only: ${{ github.ref != 'refs/heads/main' && !startsWith(github.ref, 'refs/tags/') }}

      # Generate metadata first, then compile
      - name: Generate Graal metadata (Linux/macOS)
        if: runner.os != 'Windows'
        env:
          DISABLE_DOCKER_TESTS: 'true'
        run: ./gradlew --no-daemon -Dorg.gradle.jvmargs="-Xmx2g" -PDISABLE_DOCKER_TESTS=true clean :cli:test

      - name: Generate Graal metadata (Windows)
        if: runner.os == 'Windows'
        shell: pwsh
        env:
          DISABLE_DOCKER_TESTS: 'true'
        run: .\gradlew.bat --no-daemon "-Dorg.gradle.jvmargs=-Xmx2g" -PDISABLE_DOCKER_TESTS=true "-Djava.io.tmpdir=$env:RUNNER_TEMP" clean :cli:test

      - name: Upload test report
        if: always()
        uses: actions/upload-artifact@v6
        with:
          name: test-report-${{ github.job }}-${{ runner.os }}-attempt${{ github.run_attempt }}
          path: cli/build/reports/tests/test
          retention-days: 7

      - name: Build native image (Linux)
        if: runner.os == 'Linux'
        run: ./gradlew --no-daemon -Dorg.gradle.jvmargs="-Xmx2g" -Pgraalvm.native-image.memory=6g :cli:nativeCompile

      - name: Build native image (macOS)
        if: runner.os == 'macOS'
        run: ./gradlew --no-daemon -Dorg.gradle.jvmargs="-Xmx1g" -Pgraalvm.native-image.memory=6500m :cli:nativeCompile

      - name: Build native image (Windows)
        if: runner.os == 'Windows'
        shell: pwsh
        run: .\gradlew.bat --no-daemon "-Dorg.gradle.jvmargs=-Xmx2g" "-Djava.io.tmpdir=$env:RUNNER_TEMP" :cli:nativeCompile

      - name: Locate native binary path
        id: paths
        shell: bash
        run: |
          OUT_DIR="cli/build/native/nativeCompile"
          if [[ "$RUNNER_OS" == "Windows" ]]; then
            BIN="$(ls "$OUT_DIR"/*.exe)"
          else
            BIN="$(ls "$OUT_DIR"/* | head -n 1)"
          fi
          echo "bin=$BIN" >> $GITHUB_OUTPUT
          echo "out_dir=$OUT_DIR" >> $GITHUB_OUTPUT
          echo "Found: $BIN"

      # NEW: smoke check (POSIX)
      - name: Smoke check
        if: runner.os != 'Windows'
        shell: bash
        run: |
          set -euo pipefail
          BIN="${{ steps.paths.outputs.bin }}"
          chmod +x "$BIN" || true
          echo "Running: $BIN --version"
          # Capture output and exit code
          set +e
          OUT="$("$BIN" --version 2>&1)"; CODE=$?
          set -e
          echo "----- output -----"
          echo "$OUT"
          echo "------------------"
          # Fail if non-zero OR if output shows Graal proxy/reflection error
          if [[ $CODE -ne 0 ]] || echo "$OUT" | grep -qiE 'MissingReflectionRegistrationError|NoSuchMethodError|ClassNotFoundException|Exception in thread'; then
            echo "Smoke check failed (code=$CODE)."
            exit 1
          fi

      # NEW: smoke check (Windows)
      - name: Smoke check
        if: runner.os == 'Windows'
        shell: pwsh
        run: |
          $bin = "${{ steps.paths.outputs.bin }}"
          Write-Host "Running: $bin --version"
          $output = & $bin --version 2>&1
          $code = $LASTEXITCODE
          Write-Host "----- output -----"
          Write-Host $output
          Write-Host "------------------"
          if ($code -ne 0 -or ($output -match 'MissingReflectionRegistrationError|NoSuchMethodError|ClassNotFoundException|Exception in thread')) {
            Write-Error "Smoke check failed (code=$code)."
            exit 1
          }

      - name: Upload artifact
        uses: actions/upload-artifact@v6
        with:
          name: ${{ github.event.repository.name }}-${{ matrix.os }}
          path: ${{ steps.paths.outputs.bin }}
          if-no-files-found: error
          retention-days: 14


  # Build native CLI binary for ARM64 on hosted ARM runner
  native-arm:
    name: native-ubuntu-24.04-arm
    if: startsWith(github.ref, 'refs/tags/v')
    runs-on: ubuntu-24.04-arm
    steps:
      - name: Checkout
        uses: actions/checkout@v6

      - name: Set up GraalVM (JDK 21)
        uses: graalvm/setup-graalvm@v1
        with:
          java-version: '21'
          distribution: 'graalvm-community'

      - name: Setup Gradle
        uses: gradle/actions/setup-gradle@v5

      - name: Generate Graal metadata (arm64)
        env:
          DISABLE_DOCKER_TESTS: 'true'
        run: ./gradlew --no-daemon -Dorg.gradle.jvmargs="-Xmx2g" -PDISABLE_DOCKER_TESTS=true clean :cli:test

      - name: Build native image (arm64)
        run: ./gradlew --no-daemon -Dorg.gradle.jvmargs="-Xmx2g" -Pgraalvm.native-image.memory=6g :cli:nativeCompile

      - name: Locate native binary path
        id: arm_paths
        shell: bash
        run: |
          OUT_DIR="cli/build/native/nativeCompile"
          BIN="$(ls "$OUT_DIR"/* | head -n 1)"
          echo "bin=$BIN" >> $GITHUB_OUTPUT
          echo "Found: $BIN"

      # NEW: smoke check
      - name: Smoke check
        shell: bash
        run: |
          set -euo pipefail
          BIN="${{ steps.arm_paths.outputs.bin }}"
          chmod +x "$BIN" || true
          echo "Running: $BIN --version"
          set +e
          OUT="$("$BIN" --version 2>&1)"; CODE=$?
          set -e
          echo "----- output -----"
          echo "$OUT"
          echo "------------------"
          if [[ $CODE -ne 0 ]] || echo "$OUT" | grep -qiE 'MissingReflectionRegistrationError|NoSuchMethodError|ClassNotFoundException|Exception in thread'; then
            echo "Smoke check failed (code=$CODE)."
            exit 1
          fi

      - name: Upload arm64 artifact
        uses: actions/upload-artifact@v6
        with:
          name: ${{ github.event.repository.name }}-ubuntu-24.04-arm
          path: ${{ steps.arm_paths.outputs.bin }}
          if-no-files-found: error
          retention-days: 14

  # Package CLI binaries and create GitHub release
  release:
    if: startsWith(github.ref, 'refs/tags/v')
    needs: [ native, native-arm ]
    runs-on: ubuntu-latest
    permissions:
      contents: write
    steps:
      - name: Download all artifacts
        uses: actions/download-artifact@v8
        with:
          path: dist

      - name: Package archives
        id: pkg
        shell: bash
        run: |
          set -euo pipefail
          files=()

          # macOS
          MAC_DIR=$(compgen -G 'dist/*macos*' || true)
          if [[ -n "${MAC_DIR:-}" ]]; then
            BIN=$(ls "$MAC_DIR"/* | head -n1)
            chmod +x "$BIN"
            TMPDIR="$(mktemp -d)"; cp "$BIN" "$TMPDIR/askimo"
            tar -C "$TMPDIR" -czf "askimo-darwin-arm64.tar.gz" askimo
            files+=("askimo-darwin-arm64.tar.gz")
          fi

          # Linux amd64
          LIN_AMD_DIR=$(compgen -G 'dist/*ubuntu-latest*' || true)
          if [[ -n "${LIN_AMD_DIR:-}" ]]; then
            BIN=$(ls "$LIN_AMD_DIR"/* | head -n1)
            chmod +x "$BIN"
            TMPDIR="$(mktemp -d)"; cp "$BIN" "$TMPDIR/askimo"
            tar -C "$TMPDIR" -czf "askimo-linux-x64.tar.gz" askimo
            files+=("askimo-linux-x64.tar.gz")
          fi

          # Linux arm64 (from native-arm)
          LIN_ARM_DIR=$(compgen -G 'dist/*ubuntu-24.04-arm*' || true)
          if [[ -n "${LIN_ARM_DIR:-}" ]]; then
            BIN=$(ls "$LIN_ARM_DIR"/* | head -n1)
            chmod +x "$BIN"
            TMPDIR="$(mktemp -d)"; cp "$BIN" "$TMPDIR/askimo"
            tar -C "$TMPDIR" -czf "askimo-linux-arm64.tar.gz" askimo
            files+=("askimo-linux-arm64.tar.gz")
          fi

          # Windows
          WIN_DIR=$(compgen -G 'dist/*windows*' || true)
          if [[ -n "${WIN_DIR:-}" ]]; then
            EXE=$(ls "$WIN_DIR"/*.exe | head -n1)
            zip -j "askimo-windows-x64.zip" "$EXE"
            files+=("askimo-windows-x64.zip")
          fi

          if (( ${#files[@]} )); then
            shasum -a 256 "${files[@]}" > SHA256SUMS.txt
            files+=("SHA256SUMS.txt")
          fi

          {
            echo "files<<EOF"
            printf '%s\n' "${files[@]}"
            echo "EOF"
          } >> "$GITHUB_OUTPUT"

      - name: Create GitHub Release
        uses: softprops/action-gh-release@v2
        with:
          files: ${{ steps.pkg.outputs.files }}
          generate_release_notes: false
          append_body: false
          draft: true
          prerelease: ${{ contains(github.ref, '-rc') || contains(github.ref, '-beta') || contains(github.ref, '-alpha') }}
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

  # Publish CLI packages — runs only after release job completes successfully
  publish:
    name: Publish packages (homebrew / scoop)
    if: startsWith(github.ref, 'refs/tags/v')
    needs: [ release ]
    runs-on: ubuntu-latest
    permissions:
      contents: write
    steps:
      - name: Install jq
        run: sudo apt-get update && sudo apt-get install -y jq

      - name: Collect release assets & checksums
        id: meta
        env:
          GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
        shell: bash
        run: |
          set -euo pipefail
          TAG="${{ github.ref_name }}"          # e.g. v1.2.25
          VERSION_PLAIN="${TAG#v}"              # e.g. 1.2.25
          API="https://api.github.com/repos/${GITHUB_REPOSITORY}/releases/tags/${TAG}"

          echo "Tag     : $TAG"

          ARM_NAME="askimo-darwin-arm64.tar.gz"
          AMD_NAME="askimo-darwin-amd64.tar.gz"
          WIN_NAME="askimo-windows-x64.zip"

          JSON="$(curl -sSL -H "Authorization: Bearer $GH_TOKEN" "$API")"
          echo "Assets  :"
          echo "$JSON" | jq -r '.assets[].name'
          echo ""

          get_url() { echo "$JSON" | jq -r --arg N "$1" '.assets[] | select(.name==$N) | .browser_download_url // empty'; }

          MAC_ARM_URL="$(get_url "$ARM_NAME")"
          MAC_AMD_URL="$(get_url "$AMD_NAME")"
          WIN_URL="$(get_url "$WIN_NAME")"
          SHA_URL="$(get_url "SHA256SUMS.txt")"

          if [ -z "${SHA_URL:-}" ]; then
            echo "❌ SHA256SUMS.txt not found in release assets" >&2; exit 1
          fi

          curl -sSL -o SHA256SUMS.txt "$SHA_URL"
          cat SHA256SUMS.txt

          get_sha() { grep -E "  ${1//./\\.}$" SHA256SUMS.txt | awk '{print $1}' || true; }

          {
            echo "tag=$TAG"
            echo "version=$VERSION_PLAIN"
            # homebrew
            echo "mac_arm_url=${MAC_ARM_URL:-}"
            echo "mac_arm_sha=$(get_sha "$ARM_NAME")"
            echo "mac_amd_url=${MAC_AMD_URL:-}"
            echo "mac_amd_sha=$(get_sha "$AMD_NAME")"
            # scoop
            echo "win_url=${WIN_URL:-}"
            echo "win_sha=$(get_sha "$WIN_NAME")"
          } >> "$GITHUB_OUTPUT"

      # ── Homebrew tap ──────────────────────────────────────────────────────
      - name: Checkout tap repo
        if: steps.meta.outputs.mac_arm_url != ''
        uses: actions/checkout@v6
        with:
          repository: ${{ vars.TAP_REPO || 'askimo-ai/homebrew-askimo' }}
          token: ${{ secrets.TAP_PUSH_TOKEN }}
          path: tap
          fetch-depth: 0

      - name: Write homebrew formula
        if: steps.meta.outputs.mac_arm_url != ''
        shell: bash
        run: |
          set -euo pipefail
          mkdir -p tap/Formula

          VERSION="${{ steps.meta.outputs.version }}"
          ARM_URL="${{ steps.meta.outputs.mac_arm_url }}"
          ARM_SHA="${{ steps.meta.outputs.mac_arm_sha }}"
          AMD_URL="${{ steps.meta.outputs.mac_amd_url }}"
          AMD_SHA="${{ steps.meta.outputs.mac_amd_sha }}"

          cat > tap/Formula/askimo.rb <<'RUBY'
          # typed: false
          # frozen_string_literal: true

          class Askimo < Formula
            desc "AI-powered terminal assistant for multiple LLM providers"
            homepage "https://github.com/askimo-ai/askimo"
            license "AGPLv3"

            on_macos do
          RUBY

          cat >> tap/Formula/askimo.rb <<RUBY
              on_arm do
                version "${VERSION}"
                url "${ARM_URL}"
                sha256 "${ARM_SHA}"
              end
          RUBY

          if [ -n "${AMD_URL}" ] && [ -n "${AMD_SHA}" ]; then
          cat >> tap/Formula/askimo.rb <<RUBY

              on_intel do
                version "${VERSION}"
                url "${AMD_URL}"
                sha256 "${AMD_SHA}"
              end
          RUBY
          fi

          cat >> tap/Formula/askimo.rb <<'RUBY'
            end

            def install
              bin.install "askimo"
            end

            test do
              output = shell_output("#{bin}/askimo --version")
              assert_match "askimo", output.downcase
            end
          end
          RUBY

          echo "==== tap/Formula/askimo.rb ===="
          nl -ba tap/Formula/askimo.rb
          ruby -c tap/Formula/askimo.rb

      - name: Commit & push homebrew formula
        if: steps.meta.outputs.mac_arm_url != ''
        working-directory: tap
        env:
          GIT_AUTHOR_NAME: github-actions[bot]
          GIT_AUTHOR_EMAIL: github-actions[bot]@users.noreply.github.com
          GIT_COMMITTER_NAME: github-actions[bot]
          GIT_COMMITTER_EMAIL: github-actions[bot]@users.noreply.github.com
        shell: bash
        run: |
          set -euo pipefail
          git add Formula/askimo.rb
          if git diff --cached --quiet; then
            echo "No changes to commit."
          else
            git commit -m "askimo ${{ steps.meta.outputs.version }}: update formula for ${{ steps.meta.outputs.tag }}"
            git push
          fi

      # ── Scoop bucket ──────────────────────────────────────────────────────
      - name: Checkout scoop bucket
        if: steps.meta.outputs.win_url != ''
        uses: actions/checkout@v6
        with:
          repository: askimo-ai/scoop-askimo
          token: ${{ secrets.TAP_PUSH_TOKEN }}
          ref: main
          path: bucket
          fetch-depth: 0

      - name: Write scoop manifest
        if: steps.meta.outputs.win_url != ''
        shell: bash
        run: |
          set -euo pipefail
          mkdir -p bucket/bucket
          cat > bucket/bucket/askimo.json <<EOF
          {
            "version": "${{ steps.meta.outputs.version }}",
            "description": "AI-powered terminal assistant for multiple LLM providers",
            "homepage": "https://github.com/askimo-ai/askimo",
            "license": "AGPLv3",
            "architecture": {
              "64bit": {
                "url": "${{ steps.meta.outputs.win_url }}",
                "hash": "${{ steps.meta.outputs.win_sha }}",
                "bin": "askimo.exe"
              }
            }
          }
          EOF
          cat bucket/bucket/askimo.json

      - name: Commit & push scoop manifest
        if: steps.meta.outputs.win_url != ''
        working-directory: bucket
        env:
          TOKEN: ${{ secrets.TAP_PUSH_TOKEN }}
        shell: bash
        run: |
          set -euo pipefail
          git config user.name  "github-actions"
          git config user.email "actions@users.noreply.github.com"
          git add bucket/askimo.json
          if ! git diff --cached --quiet; then
            git commit -m "askimo ${{ steps.meta.outputs.tag }}"
            git remote set-url origin "https://x-access-token:${TOKEN}@github.com/askimo-ai/scoop-askimo.git"
            git push origin HEAD:main
          else
            echo "No changes to commit."
          fi

