name: Release

on:
  push:
    tags:
      - "v*.*.*"
      - "v*"

permissions:
  contents: write
  packages: write

env:
  REGISTRY: ghcr.io

jobs:
  build:
    name: Build wheel (${{ matrix.os }}, ${{ matrix.target }})
    strategy:
      fail-fast: false
      matrix:
        include:
          - os: ubuntu-latest
            target: x86_64
          - os: windows-latest
            target: x86_64
          - os: macos-14
            target: aarch64
    runs-on: ${{ matrix.os }}
    steps:
      - uses: actions/checkout@v6

      - uses: actions/setup-python@v6
        with:
          python-version: '3.12'

      - name: Build wheel with maturin
        uses: PyO3/maturin-action@v1
        with:
          target: ${{ matrix.target }}
          args: --release --out dist --features ${{ matrix.os == 'macos-14' && 'wgpu' || 'cuda,wgpu' }}
          manylinux: auto

      - name: Upload wheel as artifact
        uses: actions/upload-artifact@v7
        with:
          name: wheels-${{ matrix.os }}-${{ matrix.target }}
          path: dist/*.whl
          retention-days: 7

  publish-pypi:
    name: Publish to PyPI
    needs: build
    runs-on: ubuntu-latest
    environment: release
    if: startsWith(github.ref, 'refs/tags/v') && !contains(github.ref, '-')
    steps:
      - name: Download all wheels
        uses: actions/download-artifact@v8
        with:
          pattern: wheels-*
          path: dist/
          merge-multiple: true

      - name: Publish to PyPI
        uses: pypa/gh-action-pypi-publish@release/v1
        with:
          password: ${{ secrets.PYPI_API_TOKEN }}
          skip-existing: true

  publish-docker:
    name: Publish Docker Images
    needs: build
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v6

      - name: Extract version and image name
        id: version
        run: |
          echo "VERSION=${GITHUB_REF_NAME#v}" >> $GITHUB_OUTPUT
          echo "IMAGE_NAME=${GITHUB_REPOSITORY,,}" >> $GITHUB_OUTPUT

      - name: Set up Docker Buildx
        uses: docker/setup-buildx-action@v3

      - name: Log in to GitHub Container Registry
        uses: docker/login-action@v3
        with:
          registry: ${{ env.REGISTRY }}
          username: ${{ github.actor }}
          password: ${{ secrets.GITHUB_TOKEN }}

      - name: Build and push backend
        uses: docker/build-push-action@v6
        with:
          context: .
          file: ./Dockerfile
          push: true
          tags: |
            ${{ env.REGISTRY }}/${{ steps.version.outputs.IMAGE_NAME }}:${{ steps.version.outputs.VERSION }}
            ${{ env.REGISTRY }}/${{ steps.version.outputs.IMAGE_NAME }}:latest
          cache-from: type=gha
          cache-to: type=gha,mode=max

      - name: Build and push frontend
        uses: docker/build-push-action@v6
        with:
          context: ./frontend
          file: ./frontend/Dockerfile.prod
          push: true
          tags: |
            ${{ env.REGISTRY }}/${{ steps.version.outputs.IMAGE_NAME }}-frontend:${{ steps.version.outputs.VERSION }}
            ${{ env.REGISTRY }}/${{ steps.version.outputs.IMAGE_NAME }}-frontend:latest
          cache-from: type=gha
          cache-to: type=gha,mode=max

  create-release:
    name: Create GitHub Release
    needs: [build, publish-pypi, publish-docker]
    if: always() && needs.build.result == 'success' && needs.publish-pypi.result == 'success'
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v6

      - name: Download all wheels
        uses: actions/download-artifact@v8
        with:
          pattern: wheels-*
          path: dist/
          merge-multiple: true

      - name: Extract changelog for version
        id: changelog
        run: |
          VERSION=${GITHUB_REF_NAME#v}
          awk -v ver="$VERSION" '
            /^## \[/ {
              if (found) exit;
              if ($0 ~ "\\[" ver "\\]") {
                found=1;
                next;
              }
            }
            found && /^## \[/ { exit }
            found { print }
          ' CHANGELOG.md > release_notes.md

      - name: Create GitHub Release
        uses: softprops/action-gh-release@v2
        with:
          files: dist/*.whl
          body_path: release_notes.md
          draft: false
          prerelease: ${{ contains(github.ref_name, '-') }}
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

  post-deploy-verify:
    name: Post-Deploy Verification
    needs: [create-release]
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v6

      - uses: actions/setup-python@v6
        with:
          python-version: '3.12'

      - name: Wait for PyPI propagation (with retry)
        run: |
          VERSION=${GITHUB_REF_NAME#v}
          echo "Waiting for llmhosts==${VERSION} to appear on PyPI..."
          for i in $(seq 1 10); do
            if pip index versions llmhosts --no-cache-dir 2>/dev/null | grep -q "${VERSION}"; then
              echo "Found llmhosts==${VERSION} on PyPI (attempt ${i})"
              break
            fi
            echo "Attempt ${i}/10: not yet visible, waiting 30s..."
            sleep 30
          done

      - name: Verify PyPI install
        run: |
          pip install llmhosts==${GITHUB_REF_NAME#v}
          llmhosts --version
          llmhosts doctor --check-only 2>&1 || true
          python3 -c "
          from llmhosts.inference.core import LLMHOSTS_CORE_AVAILABLE, LLMHostsInferenceEngine
          if not LLMHOSTS_CORE_AVAILABLE:
              raise SystemExit('FATAL: Rust Core engine missing — pure-Python wheel was published instead of native wheel')
          print('Rust Core engine: AVAILABLE')
          print('Compiled backends:', LLMHostsInferenceEngine.available_backends())
          "
          echo "::notice::PyPI package verified with native Rust extensions"

      - name: Verify Docker image
        run: |
          IMAGE_NAME="${GITHUB_REPOSITORY,,}"
          docker pull ghcr.io/${IMAGE_NAME}:${GITHUB_REF_NAME#v} || echo "::warning::Docker image not yet available"
          echo "::notice::Post-deploy verification complete"
