name: CI

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

env:
  CARGO_TERM_COLOR: always
  FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true
  RUST_EXPERIMENTAL_FEATURES: >-
    parallel
    alpha-dft alpha-reaxff alpha-mlff alpha-obara-saika alpha-cga alpha-gsm alpha-sdr
    beta-kpm beta-mbh beta-randnla beta-riemannian beta-cpm
  PYTHON_BINDING_FEATURES: >-
    parallel
    alpha-dft alpha-reaxff alpha-mlff alpha-obara-saika alpha-cga alpha-gsm alpha-sdr
    beta-kpm beta-mbh beta-randnla beta-riemannian beta-cpm
  WASM_WEB_FEATURES: >-
    parallel experimental-gpu
    alpha-dft alpha-reaxff alpha-mlff alpha-obara-saika alpha-cga alpha-gsm alpha-sdr
    alpha-dynamics-live alpha-imd
    beta-kpm beta-mbh beta-randnla beta-riemannian beta-cpm
  WASM_NODE_FEATURES: >-
    alpha-dft alpha-reaxff alpha-mlff alpha-obara-saika alpha-cga alpha-gsm alpha-sdr
    alpha-dynamics-live alpha-imd
    beta-kpm beta-mbh beta-randnla beta-riemannian beta-cpm

jobs:
  test:
    name: Test (${{ matrix.os }} / ${{ matrix.suite }})
    runs-on: ${{ matrix.os }}
    strategy:
      fail-fast: true
      matrix:
        include:
          - os: ubuntu-latest
            suite: full
          - os: macos-latest
            suite: smoke
          - os: windows-latest
            suite: smoke
    steps:
      - uses: actions/checkout@v6
      - uses: dtolnay/rust-toolchain@stable
      - uses: Swatinem/rust-cache@v2
      - name: Unit tests
        if: matrix.suite == 'full'
        run: cargo test --lib --release
      - name: Experimental feature smoke
        if: matrix.suite == 'full'
        run: cargo test --lib --release --features "${RUST_EXPERIMENTAL_FEATURES}"
      - name: Smoke battery
        run: cargo test --release --test ci -- --nocapture

  lint:
    name: Lint
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v6
      - uses: dtolnay/rust-toolchain@stable
        with:
          components: clippy, rustfmt
      - uses: Swatinem/rust-cache@v2
      - run: cargo fmt --all -- --check
      - run: cargo clippy --all-targets --features "${RUST_EXPERIMENTAL_FEATURES}" -- -D warnings

  wasm:
    name: WASM Build
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v6
      - uses: dtolnay/rust-toolchain@stable
        with:
          targets: wasm32-unknown-unknown
      - uses: Swatinem/rust-cache@v2
      - name: Install wasm-pack
        run: curl https://rustwasm.github.io/wasm-pack/installer/init.sh -sSf | sh
      - name: Build canonical WASM packages
        run: cd crates/wasm && bash build.sh --web-features "${WASM_WEB_FEATURES}" --node-features "${WASM_NODE_FEATURES}"
      - name: Check alpha/beta subpath packaging
        run: |
          test -f crates/wasm/pkg/alpha/index.js
          test -f crates/wasm/pkg/alpha/index.d.ts
          test -f crates/wasm/pkg/beta/index.js
          test -f crates/wasm/pkg/beta/index.d.ts
      - name: Verify Node.js install
        run: cd crates/wasm/pkg-node && node -e "const sci = require('./sci_form_wasm.js'); const alpha = require('./alpha'); const beta = require('./beta'); const r = JSON.parse(sci.embed('CCO', 42)); console.assert(r.num_atoms === 9, 'Expected 9 atoms'); console.assert(typeof sci.alpha_compute_dft === 'function', 'missing alpha root export'); console.assert(typeof sci.beta_compute_kpm_dos === 'function', 'missing beta root export'); console.assert(typeof alpha.alpha_modules_info === 'function', 'missing alpha subpath export'); console.assert(typeof beta.beta_modules_info === 'function', 'missing beta subpath export'); console.log('✓ Node.js WASM works with alpha/beta exports');"
      - uses: actions/upload-artifact@v6
        with:
          name: wasm-pkg
          path: crates/wasm/pkg/
      - uses: actions/upload-artifact@v6
        with:
          name: wasm-pkg-node
          path: crates/wasm/pkg-node/

  python:
    name: Python Wheels (${{ matrix.os }})
    runs-on: ${{ matrix.os }}
    strategy:
      matrix:
        os: [ubuntu-latest, macos-latest, windows-latest]
    steps:
      - uses: actions/checkout@v6
      - uses: actions/setup-python@v6
        with:
          python-version: "3.11"
          cache: pip
      - uses: dtolnay/rust-toolchain@stable
      - uses: Swatinem/rust-cache@v2
      - name: Install maturin
        run: pip install maturin
      - name: Build wheel
        run: cd crates/python && maturin build --release --features "${PYTHON_BINDING_FEATURES}"
      - uses: actions/upload-artifact@v6
        with:
          name: python-wheel-${{ matrix.os }}
          path: target/wheels/*.whl
