name: CI

on:
  push:
    branches: ["main"]
  pull_request:
    branches: ["main"]

env:
  CARGO_TERM_COLOR: always

jobs:
  # General build and test job, runs on both pushes and PRs.
  build_and_test:
    runs-on: ubuntu-latest

    steps:
      - uses: actions/checkout@v4
        with:
          submodules: recursive

      # Use rustup to add the wasm32 toolchain
      - uses: dtolnay/rust-toolchain@v1
        with:
          toolchain: stable
          target: wasm32-unknown-unknown

      - name: Cache Cargo registry
        uses: actions/cache@v4
        with:
          path: ~/.cargo/registry
          key: ${{ runner.os }}-cargo-registry-${{ hashFiles('**/Cargo.lock') }}
          restore-keys: |
            ${{ runner.os }}-cargo-registry-

      - name: Cache Cargo index
        uses: actions/cache@v4
        with:
          path: ~/.cargo/git
          key: ${{ runner.os }}-cargo-index-${{ hashFiles('**/Cargo.lock') }}
          restore-keys: |
            ${{ runner.os }}-cargo-index-

      - name: Format check
        run: cargo fmt --all -- --check

      - name: Linting
        run: cargo clippy --all-targets -- -D warnings

      - name: Test
        run: cargo test --locked

      # WASM / JS tests
      - name: Install wasm-pack
        run: curl https://rustwasm.github.io/wasm-pack/installer/init.sh -sSf | sh

      - name: Setup Node.js
        uses: actions/setup-node@v4
        with:
          node-version: "24"

      - name: Build & Test WASM
        run: wasm-pack test --headless --firefox

      - name: Run JS integration tests
        run: |
          npm install
          npm ci
          npm run build:wasm
          npm test

  # For PRs: check that the version was bumped.
  # On main: always succeed (version already validated before merge).
  check-version-bump:
    name: Check Version Bump
    runs-on: ubuntu-latest
    steps:
      - name: Checkout
        if: github.event_name == 'pull_request'
        uses: actions/checkout@v4
        with:
          fetch-depth: 0

      - name: Check version bump
        if: github.event_name == 'pull_request'
        run: |
          CURRENT_VERSION=$(grep '^version' Cargo.toml | head -1 | sed 's/version = "//;s/"//')
          MAIN_VERSION=$(git show origin/main:Cargo.toml | grep '^version' | head -1 | sed 's/version = "//;s/"//')
          echo "Main version:    $MAIN_VERSION"
          echo "Current version: $CURRENT_VERSION"
          if [ "$CURRENT_VERSION" = "$MAIN_VERSION" ]; then
            echo "::error::Version not bumped. Please update the version in Cargo.toml before merging."
            exit 1
          fi
          echo "Version bumped: $MAIN_VERSION -> $CURRENT_VERSION ✓"

  publish:
    name: Publish
    if: >
      (github.event_name == 'push' && github.ref == 'refs/heads/main') ||
      (github.event_name == 'pull_request')
    runs-on: ubuntu-latest
    needs: [build_and_test, check-version-bump]
    permissions:
      id-token: write  # Required for OIDC (crates.io trusted publish)
      contents: write
      packages: write
    steps:
      - uses: actions/checkout@v4

      - uses: dtolnay/rust-toolchain@v1
        with:
          toolchain: stable
          target: wasm32-unknown-unknown

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

      - name: Get version
        id: version
        run: |
          VERSION=$(grep '^version' Cargo.toml | head -1 | sed 's/version = "//;s/"//')
          echo "version=${VERSION}" >> "${GITHUB_OUTPUT}"
          echo "Release version: ${VERSION}"

      - name: Compute publish parameters
        id: params
        run: |
          if [[ "${{ github.event_name }}" == "pull_request" ]]; then
            echo "is_release=false" >> "$GITHUB_OUTPUT"
          else
            echo "is_release=true" >> "$GITHUB_OUTPUT"
          fi

      # Rust Crate (release only)
      - name: Package crate
        if: steps.params.outputs.is_release == 'true'
        run: cargo package --locked

      # WASM / NPM
      - name: Install wasm-pack
        run: curl https://rustwasm.github.io/wasm-pack/installer/init.sh -sSf | sh

      - name: Build WASM for release
        run: |
          wasm-pack build --release --scope=semio-ai -- --locked
          wasm-pack pack

      - name: Tag and push
        if: steps.params.outputs.is_release == 'true'
        run: |
          TAG="v${{ steps.version.outputs.version }}"
          git config user.name "github-actions[bot]"
          git config user.email "github-actions[bot]@users.noreply.github.com"
          git tag "${TAG}"
          git push origin "${TAG}"

      - name: Create GitHub Release
        if: steps.params.outputs.is_release == 'true'
        env:
          GH_TOKEN: ${{ github.token }}
        run: |
          CRATE_FILE=$(ls target/package/*.crate | head -1)
          NPM_TGZ=$(ls pkg/*.tgz | head -1)
          TAG="v${{ steps.version.outputs.version }}"

          if gh release view "${TAG}" > /dev/null 2>&1; then
            echo "Release ${TAG} already exists — skipping GitHub release creation."
          else
            gh release create "${TAG}" \
              --title "Release ${TAG}" \
              --generate-notes \
              "${CRATE_FILE}" \
              "${NPM_TGZ}"
          fi

      - name: Authenticate with crates.io (Trusted Publisher)
        if: steps.params.outputs.is_release == 'true'
        uses: rust-lang/crates-io-auth-action@v1
        id: crates-auth

      - name: Publish to crates.io
        if: steps.params.outputs.is_release == 'true'
        env:
          CARGO_REGISTRY_TOKEN: ${{ steps.crates-auth.outputs.token }}
        run: cargo publish
