name: Release

on:
  push:
    tags:
      - 'v*'

jobs:
  release-python-packages:
    runs-on: ubuntu-latest
    environment: pypi
    permissions:
      id-token: write

    steps:
    - uses: actions/checkout@v6

    - name: Install uv
      uses: astral-sh/setup-uv@v7
      with:
        enable-cache: true

    - name: Set up Python
      uses: actions/setup-python@v6
      with:
        python-version-file: ".python-version"

    # Build all packages
    - name: Build hindsight-client
      working-directory: ./hindsight-clients/python
      run: uv build --out-dir dist

    - name: Build hindsight-api-slim
      working-directory: ./hindsight-api-slim
      run: uv build --out-dir dist

    - name: Build hindsight-api
      working-directory: ./hindsight-api
      run: uv build --out-dir dist

    - name: Build hindsight-all
      working-directory: ./hindsight-all
      run: uv build --out-dir dist

    - name: Build hindsight-all-slim
      working-directory: ./hindsight-all-slim
      run: uv build --out-dir dist

    - name: Build hindsight-embed
      working-directory: ./hindsight-embed
      run: uv build --out-dir dist

    # Publish in order (client and api-slim first, then api/all wrappers which depend on them)
    - name: Publish hindsight-client to PyPI
      uses: pypa/gh-action-pypi-publish@release/v1
      with:
        packages-dir: ./hindsight-clients/python/dist
        skip-existing: true

    - name: Publish hindsight-api-slim to PyPI
      uses: pypa/gh-action-pypi-publish@release/v1
      with:
        packages-dir: ./hindsight-api-slim/dist
        skip-existing: true

    - name: Publish hindsight-api to PyPI
      uses: pypa/gh-action-pypi-publish@release/v1
      with:
        packages-dir: ./hindsight-api/dist
        skip-existing: true

    - name: Publish hindsight-all to PyPI
      uses: pypa/gh-action-pypi-publish@release/v1
      with:
        packages-dir: ./hindsight-all/dist
        skip-existing: true

    - name: Publish hindsight-all-slim to PyPI
      uses: pypa/gh-action-pypi-publish@release/v1
      with:
        packages-dir: ./hindsight-all-slim/dist
        skip-existing: true

    - name: Publish hindsight-embed to PyPI
      uses: pypa/gh-action-pypi-publish@release/v1
      with:
        packages-dir: ./hindsight-embed/dist
        skip-existing: true

    # Upload artifacts for GitHub release
    - name: Upload artifacts
      uses: actions/upload-artifact@v7
      with:
        name: python-packages
        path: |
          hindsight-clients/python/dist/*
          hindsight-api-slim/dist/*
          hindsight-api/dist/*
          hindsight-all/dist/*
          hindsight-all-slim/dist/*
          hindsight-embed/dist/*
        retention-days: 1

  release-typescript-client:
    runs-on: ubuntu-latest
    environment: npm

    steps:
    - uses: actions/checkout@v6

    - name: Set up Node.js
      uses: actions/setup-node@v6
      with:
        node-version: '20'
        registry-url: 'https://registry.npmjs.org'
        cache: 'npm'
        cache-dependency-path: package-lock.json

    - name: Install dependencies
      run: npm ci --workspace=hindsight-clients/typescript

    - name: Build
      run: npm run build --workspace=hindsight-clients/typescript

    - name: Publish to npm
      working-directory: ./hindsight-clients/typescript
      run: |
        set +e
        OUTPUT=$(npm publish --access public 2>&1)
        EXIT_CODE=$?
        echo "$OUTPUT"
        if [ $EXIT_CODE -ne 0 ]; then
          if echo "$OUTPUT" | grep -q "cannot publish over"; then
            echo "Package version already published, skipping..."
            exit 0
          fi
          exit $EXIT_CODE
        fi
      env:
        NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}

    - name: Pack for GitHub release
      working-directory: ./hindsight-clients/typescript
      run: npm pack

    - name: Upload artifacts
      uses: actions/upload-artifact@v7
      with:
        name: typescript-client
        path: hindsight-clients/typescript/*.tgz
        retention-days: 1

  release-control-plane:
    runs-on: ubuntu-latest
    environment: npm

    steps:
    - uses: actions/checkout@v6

    - name: Set up Node.js
      uses: actions/setup-node@v6
      with:
        node-version: '20'
        registry-url: 'https://registry.npmjs.org'
        cache: 'npm'
        cache-dependency-path: package-lock.json

    - name: Install dependencies
      run: npm ci

    - name: Build TypeScript client (dependency)
      run: npm run build --workspace=hindsight-clients/typescript

    - name: Fix platform-specific native modules
      run: |
        # npm ci installs from lockfile which may have wrong platform binaries
        # Delete hoisted native modules and reinstall for current platform
        rm -rf node_modules/lightningcss node_modules/@tailwindcss
        npm install lightningcss @tailwindcss/postcss @tailwindcss/node

    - name: Build
      run: npm run build --workspace=hindsight-control-plane

    - name: Verify standalone build
      run: test -f hindsight-control-plane/standalone/server.js || (echo 'standalone/server.js missing - build failed' && exit 1)

    - name: Publish to npm
      working-directory: ./hindsight-control-plane
      run: |
        set +e
        OUTPUT=$(npm publish --access public --ignore-scripts 2>&1)
        EXIT_CODE=$?
        echo "$OUTPUT"
        if [ $EXIT_CODE -ne 0 ]; then
          if echo "$OUTPUT" | grep -q "cannot publish over"; then
            echo "Package version already published, skipping..."
            exit 0
          fi
          exit $EXIT_CODE
        fi
      env:
        NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}

    - name: Pack for GitHub release
      working-directory: ./hindsight-control-plane
      run: npm pack

    - name: Upload artifacts
      uses: actions/upload-artifact@v7
      with:
        name: control-plane
        path: hindsight-control-plane/*.tgz
        retention-days: 1

  release-rust-cli:
    runs-on: ${{ matrix.os }}
    strategy:
      matrix:
        include:
          - os: ubuntu-latest
            target: x86_64-unknown-linux-gnu
            artifact_name: hindsight
            asset_name: hindsight-linux-amd64
          - os: macos-latest
            target: x86_64-apple-darwin
            artifact_name: hindsight
            asset_name: hindsight-darwin-amd64
          - os: macos-latest
            target: aarch64-apple-darwin
            artifact_name: hindsight
            asset_name: hindsight-darwin-arm64
          - os: ubuntu-24.04-arm
            target: aarch64-unknown-linux-gnu
            artifact_name: hindsight
            asset_name: hindsight-linux-arm64

    steps:
    - uses: actions/checkout@v6

    - name: Install Rust
      uses: dtolnay/rust-toolchain@stable
      with:
        targets: ${{ matrix.target }}

    - name: Build
      working-directory: hindsight-cli
      run: cargo build --release --target ${{ matrix.target }}

    - name: Prepare artifact
      run: |
        mkdir -p artifacts
        cp hindsight-cli/target/${{ matrix.target }}/release/${{ matrix.artifact_name }} artifacts/${{ matrix.asset_name }}
        chmod +x artifacts/${{ matrix.asset_name }}

    - name: Upload artifacts
      uses: actions/upload-artifact@v7
      with:
        name: rust-cli-${{ matrix.asset_name }}
        path: artifacts/${{ matrix.asset_name }}
        retention-days: 1

  release-docker-images:
    name: Release Docker (${{ matrix.image_name }}${{ matrix.tag_suffix }})
    runs-on: ubuntu-latest
    permissions:
      contents: read
      packages: write
    strategy:
      matrix:
        include:
          - target: api-only
            image_name: hindsight-api
            tag_suffix: ""
            build_args: ""
          - target: api-only
            image_name: hindsight-api
            tag_suffix: "-slim"
            build_args: |
              INCLUDE_LOCAL_MODELS=false
              PRELOAD_ML_MODELS=false
          - target: cp-only
            image_name: hindsight-control-plane
            tag_suffix: ""
            build_args: ""
          - target: standalone
            image_name: hindsight
            tag_suffix: ""
            build_args: ""
          - target: standalone
            image_name: hindsight
            tag_suffix: "-slim"
            build_args: |
              INCLUDE_LOCAL_MODELS=false
              PRELOAD_ML_MODELS=false

    steps:
    - uses: actions/checkout@v6

    - name: Free Disk Space
      uses: jlumbroso/free-disk-space@main
      with:
        tool-cache: true
        android: true
        dotnet: true
        haskell: true
        large-packages: true
        docker-images: true
        swap-storage: true

    - name: Set up QEMU
      uses: docker/setup-qemu-action@v4

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

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

    - name: Extract version from tag
      id: get_version
      run: echo "VERSION=${GITHUB_REF#refs/tags/v}" >> $GITHUB_OUTPUT

    - name: Extract metadata for release tags
      id: meta
      uses: docker/metadata-action@v6
      with:
        images: ghcr.io/${{ github.repository_owner }}/${{ matrix.image_name }}
        flavor: |
          latest=auto
          suffix=${{ matrix.tag_suffix }}
        tags: |
          type=semver,pattern={{version}},value=${{ steps.get_version.outputs.VERSION }}
          type=semver,pattern={{major}}.{{minor}},value=${{ steps.get_version.outputs.VERSION }}
          type=semver,pattern={{major}},value=${{ steps.get_version.outputs.VERSION }}
          type=raw,value=latest

    # TODO: Re-enable smoke test when disk space issue is resolved
    # # Step 1: Build for local testing (single platform, no push)
    # # This creates an identical image to what will be released, just for one platform
    # - name: Build image for testing
    #   uses: docker/build-push-action@v7
    #   with:
    #     context: .
    #     file: docker/standalone/Dockerfile
    #     target: ${{ matrix.target }}
    #     push: false
    #     load: true
    #     tags: ${{ matrix.image_name }}:test
    #     cache-from: type=gha
    #     cache-to: type=gha,mode=max

    # # Step 2: Test the image before pushing anything
    # - name: Smoke test - verify container starts
    #   env:
    #     GROQ_API_KEY: ${{ secrets.GROQ_API_KEY }}
    #   run: ./docker/test-image.sh "${{ matrix.image_name }}:test" "${{ matrix.target }}"

    # Build multi-platform and push to release tags
    - name: Build and push release images
      uses: docker/build-push-action@v7
      with:
        context: .
        file: docker/standalone/Dockerfile
        target: ${{ matrix.target }}
        build-args: ${{ matrix.build_args }}
        push: true
        platforms: linux/amd64,linux/arm64
        tags: ${{ steps.meta.outputs.tags }}
        labels: ${{ steps.meta.outputs.labels }}

  release-helm-chart:
    runs-on: ubuntu-latest
    permissions:
      contents: read
      packages: write

    steps:
    - uses: actions/checkout@v6

    - name: Install Helm
      uses: azure/setup-helm@v5
      with:
        version: 'latest'

    - name: Log in to GHCR
      run: echo "${{ secrets.GITHUB_TOKEN }}" | helm registry login ghcr.io -u ${{ github.actor }} --password-stdin

    - name: Lint Helm chart
      run: helm lint helm/hindsight

    - name: Package Helm chart
      run: helm package helm/hindsight --destination ./helm-packages

    - name: Push to GHCR OCI
      run: helm push helm-packages/*.tgz oci://ghcr.io/${{ github.repository_owner }}/charts

    - name: Upload artifacts
      uses: actions/upload-artifact@v7
      with:
        name: helm-chart
        path: helm-packages/*.tgz
        retention-days: 1

  create-github-release:
    runs-on: ubuntu-latest
    needs: [release-python-packages, release-typescript-client, release-control-plane, release-rust-cli, release-docker-images, release-helm-chart]
    permissions:
      contents: write

    steps:
    - uses: actions/checkout@v6

    - name: Extract version from tag
      id: get_version
      run: echo "VERSION=${GITHUB_REF#refs/tags/v}" >> $GITHUB_OUTPUT

    - name: Download Python packages
      uses: actions/download-artifact@v8
      with:
        name: python-packages
        path: ./artifacts/python-packages

    - name: Download TypeScript client
      uses: actions/download-artifact@v8
      with:
        name: typescript-client
        path: ./artifacts/typescript-client

    - name: Download Control Plane
      uses: actions/download-artifact@v8
      with:
        name: control-plane
        path: ./artifacts/control-plane

    - name: Download Rust CLI (Linux)
      uses: actions/download-artifact@v8
      with:
        name: rust-cli-hindsight-linux-amd64
        path: ./artifacts/rust-cli-linux

    - name: Download Rust CLI (macOS Intel)
      uses: actions/download-artifact@v8
      with:
        name: rust-cli-hindsight-darwin-amd64
        path: ./artifacts/rust-cli-darwin-amd64

    - name: Download Rust CLI (macOS ARM)
      uses: actions/download-artifact@v8
      with:
        name: rust-cli-hindsight-darwin-arm64
        path: ./artifacts/rust-cli-darwin-arm64

    - name: Download Helm chart
      uses: actions/download-artifact@v8
      with:
        name: helm-chart
        path: ./artifacts/helm-chart

    - name: Prepare release assets
      run: |
        mkdir -p release-assets
        # Python packages
        cp artifacts/python-packages/hindsight-clients/python/dist/* release-assets/ || true
        cp artifacts/python-packages/hindsight-api-slim/dist/* release-assets/ || true
        cp artifacts/python-packages/hindsight-api/dist/* release-assets/ || true
        cp artifacts/python-packages/hindsight-all/dist/* release-assets/ || true
        cp artifacts/python-packages/hindsight-all-slim/dist/* release-assets/ || true
        cp artifacts/python-packages/hindsight-embed/dist/* release-assets/ || true
        # TypeScript client
        cp artifacts/typescript-client/*.tgz release-assets/ || true
        # Control Plane
        cp artifacts/control-plane/*.tgz release-assets/ || true
        # Rust CLI binaries
        cp artifacts/rust-cli-linux/hindsight-linux-amd64 release-assets/ || true
        cp artifacts/rust-cli-darwin-amd64/hindsight-darwin-amd64 release-assets/ || true
        cp artifacts/rust-cli-darwin-arm64/hindsight-darwin-arm64 release-assets/ || true
        # Helm chart
        cp artifacts/helm-chart/*.tgz release-assets/ || true
        ls -la release-assets/

    - name: Create GitHub Release
      uses: softprops/action-gh-release@v2
      with:
        files: release-assets/*
        generate_release_notes: true
        draft: false
        prerelease: false
      env:
        GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
