name: PyPI Release

on:
  release:
    types: [published]
  workflow_dispatch:

permissions:
  contents: read

jobs:
  linux:
    runs-on: ${{ matrix.platform.runner }}
    strategy:
      matrix:
        platform:
          - runner: ubuntu-22.04
            target: x86_64
            manylinux: auto
          - runner: ubuntu-22.04
            target: aarch64
            manylinux: 2_28
            container: quay.io/pypa/manylinux_2_28_aarch64
            qemu: true
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-python@v5
        with:
          python-version: 3.x
      - name: Set up QEMU
        if: ${{ matrix.platform.qemu }}
        uses: docker/setup-qemu-action@v3
      - name: Build wheels
        uses: PyO3/maturin-action@v1
        with:
          target: ${{ matrix.platform.target }}
          args: --release --out dist --find-interpreter --features openblas
          manylinux: ${{ matrix.platform.manylinux }}
          container: ${{ matrix.platform.container }}
          before-script-linux: |
            set -e
            if command -v dnf >/dev/null 2>&1; then
              dnf install -y openblas-devel
            elif command -v yum >/dev/null 2>&1; then
              yum install -y openblas-devel
            elif command -v apt-get >/dev/null 2>&1; then
              apt-get update
              apt-get install -y libopenblas-dev
            else
              echo "No supported package manager found to install OpenBLAS" >&2
              exit 1
            fi
      - name: Upload wheels
        uses: actions/upload-artifact@v4
        with:
          name: wheels-linux-${{ matrix.platform.target }}
          path: dist

  macos:
    runs-on: ${{ matrix.platform.runner }}
    strategy:
      matrix:
        platform:
          - runner: macos-14
            target: aarch64
            mac_arch: arm64
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-python@v5
        with:
          python-version: 3.x
      - name: Install OpenBLAS
        run: |
          set -euo pipefail
          brew install openblas gcc
          openblas_prefix="$(brew --prefix openblas)"
          gcc_prefix="$(brew --prefix gcc)"
          gcc_lib_dir="$gcc_prefix/lib/gcc/current"
          {
            echo "OPENBLAS_DIR=$openblas_prefix"
            echo "PKG_CONFIG_PATH=$openblas_prefix/lib/pkgconfig"
            echo "MACOS_DYLIB_PATHS=$gcc_lib_dir:$openblas_prefix/lib"
          } >> "$GITHUB_ENV"
      - name: Build wheels
        uses: PyO3/maturin-action@v1
        with:
          target: ${{ matrix.platform.target }}
          args: --release --out dist --find-interpreter --features openblas --auditwheel skip
      - name: Bundle OpenBLAS into wheels
        run: |
          python -m pip install --upgrade pip delocate
          mkdir -p dist/repaired
          for whl in dist/*.whl; do
            DYLD_LIBRARY_PATH="$MACOS_DYLIB_PATHS" \
            DYLD_FALLBACK_LIBRARY_PATH="$MACOS_DYLIB_PATHS" \
              python -m delocate.cmd.delocate_wheel --require-archs ${{ matrix.platform.mac_arch }} -w dist/repaired -v "$whl"
          done
          rm -f dist/*.whl
          mv dist/repaired/*.whl dist/
          rmdir dist/repaired
      - name: Upload wheels
        uses: actions/upload-artifact@v4
        with:
          name: wheels-macos-${{ matrix.platform.target }}
          path: dist

  windows:
    # The Windows wheel is built WITHOUT the `openblas` feature: `openblas-src`
    # does not support `static` on `*-windows-*` targets, and the prebuilt
    # MinGW `.lib` does not link cleanly with MSVC. Consequence: 15 classes
    # gated behind `openblas` (ADF/KPSS/PhillipsPerron/ERS/LeybourneMcCabe,
    # EngleGranger/Johansen/Granger/GaussianHmm, HarRv, PCA/FamaMacBeth/
    # PairsStrategy, NelsonSiegel.fit_curve) are absent from the Windows wheel.
    # Sampling, pricing, calibration, copulas and non-BLAS stats remain.
    # Windows users who need the BLAS-gated surface should source-build with
    # vcpkg (see README "OpenBLAS" section).
    runs-on: windows-latest
    strategy:
      matrix:
        platform:
          - target: x64
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-python@v5
        with:
          python-version: 3.x
          architecture: ${{ matrix.platform.target }}
      - name: Build wheels (no openblas)
        uses: PyO3/maturin-action@v1
        with:
          target: ${{ matrix.platform.target }}
          args: --release --out dist --find-interpreter
      - name: Upload wheels
        uses: actions/upload-artifact@v4
        with:
          name: wheels-windows-${{ matrix.platform.target }}
          path: dist

  sdist:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - name: Build sdist
        uses: PyO3/maturin-action@v1
        with:
          command: sdist
          args: --out dist
      - name: Upload sdist
        uses: actions/upload-artifact@v4
        with:
          name: wheels-sdist
          path: dist

  publish:
    name: Publish to PyPI
    runs-on: ubuntu-latest
    needs: [linux, macos, windows, sdist]
    environment: pypi
    permissions:
      id-token: write
    steps:
      - name: Download all wheels
        uses: actions/download-artifact@v4
        with:
          pattern: wheels-*
          merge-multiple: true
          path: dist
      - name: Publish to PyPI
        uses: PyO3/maturin-action@v1
        with:
          command: upload
          args: --non-interactive --skip-existing dist/*
