name: CI/CD

on:
  push:
    branches: [ "main" ]
    tags: [ "v*" ]
  pull_request:
    branches: [ "main" ]

permissions:
  contents: write

jobs:
  test:
    name: Test before release
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Set up Go
        uses: actions/setup-go@v5
        with:
          go-version: "1.25"

      - name: Test
        run: go test -race $(go list ./... | grep -v /tests/benchmarks)

  # ── Pure Go builds (all platforms, no CGo) ──
  release-pure:
    name: Build Pure Go (${{ matrix.suffix }})
    needs: test
    runs-on: ubuntu-latest
    strategy:
      matrix:
        include:
          - goos: linux
            goarch: amd64
            suffix: linux-amd64
          - goos: linux
            goarch: arm64
            suffix: linux-arm64
          - goos: darwin
            goarch: amd64
            suffix: darwin-amd64
          - goos: darwin
            goarch: arm64
            suffix: darwin-arm64
          - goos: windows
            goarch: amd64
            suffix: windows-amd64
          - goos: windows
            goarch: arm64
            suffix: windows-arm64

    steps:
      - uses: actions/checkout@v4

      - name: Set up Go
        uses: actions/setup-go@v5
        with:
          go-version: "1.25"

      - name: Determine version
        id: version
        # Use short commit hash if not a tag
        run: |
          if [[ "${GITHUB_REF}" == refs/tags/* ]]; then
            echo "version=${GITHUB_REF#refs/tags/}" >> "$GITHUB_OUTPUT"
          else
            echo "version=$(git rev-parse --short HEAD)" >> "$GITHUB_OUTPUT"
          fi

      - name: Build gleann
        env:
          GOOS: ${{ matrix.goos }}
          GOARCH: ${{ matrix.goarch }}
          CGO_ENABLED: "0"
          VERSION: ${{ steps.version.outputs.version }}
        run: make

      - name: Compress binary
        run: |
          mkdir -p dist
          SRC="build/gleann"
          OUTNAME="gleann"
          if [ "${{ matrix.goos }}" = "windows" ]; then
            OUTNAME="gleann.exe"
          fi
          cp "${SRC}" "${OUTNAME}"
          SUFFIX="gleann-${{ steps.version.outputs.version }}-${{ matrix.suffix }}"
          if [ "${{ matrix.goos }}" = "windows" ]; then
            zip "dist/${SUFFIX}.zip" "${OUTNAME}"
          else
            tar czf "dist/${SUFFIX}.tar.gz" "${OUTNAME}"
          fi

      - name: Upload artifacts
        uses: actions/upload-artifact@v4
        with:
          name: gleann-${{ matrix.suffix }}
          path: dist/*
          retention-days: 1


  # ── FAISS + Tree-sitter build (Linux & macOS, requires CGo) ──
  release-full:
    name: Build Full (FAISS + Native) (${{ matrix.suffix }})
    needs: test
    runs-on: ${{ matrix.os }}
    strategy:
      fail-fast: false
      matrix:
        include:
          - os: ubuntu-latest
            goos: linux
            goarch: amd64
            suffix: linux-amd64
          - os: macos-latest
            goos: darwin
            goarch: arm64
            suffix: darwin-arm64

    steps:
      - uses: actions/checkout@v4

      - name: Set up Go
        uses: actions/setup-go@v5
        with:
          go-version: "1.25"

      - name: Set up Rust
        uses: actions-rs/toolchain@v1
        with:
          profile: minimal
          toolchain: stable
          override: true

      - name: Install FAISS Dependencies
        shell: bash
        run: |
          if [ "$RUNNER_OS" == "Linux" ]; then
            sudo apt-get update
            # Added libomp-dev for Linux just in case
            sudo apt-get install -y cmake build-essential libopenblas-dev libomp-dev swig
          elif [ "$RUNNER_OS" == "macOS" ]; then
            # libomp openblas swig added for macOS CXX OpenMP compatibility (AppleClang missing it by default)
            brew install cmake libomp openblas swig
          fi

      - name: Build and install FAISS from source
        shell: bash
        run: |
          if [ "$RUNNER_OS" == "macOS" ]; then
            export CC=/usr/bin/clang
            export CXX=/usr/bin/clang++
          fi
          git clone --depth 1 --branch v1.9.0 https://github.com/facebookresearch/faiss.git /tmp/faiss
          cd /tmp/faiss

          # macOS AppleClang doesn't bundle OpenMP. Tell CMake where libomp is.
          EXTRA_CMAKE_ARGS=""
          if [ "$RUNNER_OS" == "macOS" ]; then
            EXTRA_CMAKE_ARGS="-DOpenMP_ROOT=$(brew --prefix libomp)"
          fi

          cmake -B build \
            -DFAISS_ENABLE_GPU=OFF \
            -DFAISS_ENABLE_PYTHON=OFF \
            -DFAISS_ENABLE_C_API=ON \
            -DBUILD_SHARED_LIBS=ON \
            -DBUILD_TESTING=OFF \
            -DCMAKE_BUILD_TYPE=Release \
            -DCMAKE_INSTALL_PREFIX=/usr/local \
            ${EXTRA_CMAKE_ARGS}
          
          NCORES=2
          if [ "$RUNNER_OS" == "Linux" ]; then
            NCORES=$(nproc)
          elif [ "$RUNNER_OS" == "macOS" ]; then
            NCORES=$(sysctl -n hw.logicalcpu)
          fi
          
          cmake --build build -j$NCORES -- faiss faiss_c
          sudo cmake --install build

          # Build FAISS C API wrapper (not built by cmake)
          cd /tmp/faiss
          if [ "$RUNNER_OS" == "Linux" ]; then
            g++ -shared -fPIC -Wl,-rpath,'$$ORIGIN' -o libfaiss_c.so \
              c_api/*.cpp \
              -I. -I/usr/local/include \
              -L/usr/local/lib -lfaiss \
              -lstdc++ -lgomp -lopenblas
            sudo cp libfaiss_c.so /usr/local/lib/
            sudo mkdir -p /usr/local/include/faiss/c_api/impl
            sudo cp c_api/*.h /usr/local/include/faiss/c_api/
            sudo cp c_api/impl/*.h /usr/local/include/faiss/c_api/impl/
            sudo ldconfig
          elif [ "$RUNNER_OS" == "macOS" ]; then
            # Using Accelerate framework + libomp for OpenMP support
            clang++ -shared -fPIC -Wl,-rpath,@loader_path -o libfaiss_c.dylib \
              c_api/*.cpp \
              -I. -I/usr/local/include \
              -Xpreprocessor -fopenmp -I$(brew --prefix libomp)/include \
              -L/usr/local/lib -lfaiss \
              -L$(brew --prefix libomp)/lib -lomp \
              -lc++ -framework Accelerate
            sudo cp libfaiss_c.dylib /usr/local/lib/
            sudo mkdir -p /usr/local/include/faiss/c_api/impl
            sudo cp c_api/*.h /usr/local/include/faiss/c_api/
            sudo cp c_api/impl/*.h /usr/local/include/faiss/c_api/impl/
          fi

      - name: Determine version
        id: version
        shell: bash
        run: |
          if [[ "${GITHUB_REF}" == refs/tags/* ]]; then
            echo "version=${GITHUB_REF#refs/tags/}" >> "$GITHUB_OUTPUT"
          else
            echo "version=$(git rev-parse --short HEAD)" >> "$GITHUB_OUTPUT"
          fi

      - name: Build gleann-full (FAISS + Tree-sitter + Native)
        env:
          CGO_ENABLED: "1"
          GOOS: ${{ matrix.goos }}
          GOARCH: ${{ matrix.goarch }}
          LD_LIBRARY_PATH: "/usr/local/lib"
          VERSION: ${{ steps.version.outputs.version }}
          FAISS_LIB_DIR: "/usr/local/lib"
        run: make full

      - name: Rename and compress binary with shared libraries
        shell: bash
        run: |
          mkdir -p dist
          SUFFIX="gleann-full-${{ steps.version.outputs.version }}-${{ matrix.suffix }}"
          cp build/gleann-full "${SUFFIX}"
          cp /usr/local/lib/libfaiss_c.* . 2>/dev/null || true
          cp /usr/local/lib/libfaiss.*   . 2>/dev/null || true
          cp build/libgleann_core_rs.* . 2>/dev/null || true
          
          # Use different archive extensions based on OS
          if [ "$RUNNER_OS" == "Linux" ]; then
            tar czf "dist/${SUFFIX}.tar.gz" "${SUFFIX}" libfaiss*.so libgleann_core_rs.so 2>/dev/null || true
          elif [ "$RUNNER_OS" == "macOS" ]; then
            tar czf "dist/${SUFFIX}.tar.gz" "${SUFFIX}" libfaiss*.dylib libgleann_core_rs.dylib 2>/dev/null || true
          fi

      - name: Upload artifacts
        uses: actions/upload-artifact@v4
        with:
          name: gleann-full-${{ matrix.suffix }}
          path: dist/*
          retention-days: 1

  # ── Only publish to GitHub Releases for tags ──
  publish-release:
    name: Publish Release
    needs: [release-pure, release-full]
    if: startsWith(github.ref, 'refs/tags/v')
    runs-on: ubuntu-latest
    steps:
      - name: Download all artifacts
        uses: actions/download-artifact@v4
        with:
          path: dist
          merge-multiple: true

      - name: Upload release assets
        uses: softprops/action-gh-release@v2
        with:
          files: dist/*
          generate_release_notes: true
