name: Release

on:
  push:
    branches:
      - main
      - develop

concurrency:
  group: ${{ github.workflow }}-${{ github.ref_name }}
  cancel-in-progress: true

jobs:
  # Semantic versioning analysis (main only)
  semantic-release:
    if: github.ref == 'refs/heads/main'
    runs-on: ubuntu-latest
    outputs:
      new_release_published: ${{ steps.semantic.outputs.new_release_published }}
      new_release_version: ${{ steps.semantic.outputs.new_release_version }}
      new_release_git_tag: ${{ steps.semantic.outputs.new_release_git_tag }}

    permissions:
      contents: write

    steps:
      - name: 📥 Checkout code
        uses: actions/checkout@v5
        with:
          fetch-depth: 0

      - name: 🐍 Set up Node.js
        uses: actions/setup-node@v6
        with:
          node-version: '20'

      - name: 📦 Install semantic-release
        run: |
          npm install -g semantic-release@22 \
            @semantic-release/github@9 \
            conventional-changelog-conventionalcommits@7

      - name: 🏷️ Semantic Release
        id: semantic
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
        run: |
          # Run semantic-release and capture output
          npx semantic-release --debug > release_output.log 2>&1 || true

          # Check if a release was created
          if grep -q "Published release" release_output.log; then
            echo "new_release_published=true" >> $GITHUB_OUTPUT
            # Extract version from the log
            VERSION=$(grep "Published release" release_output.log | sed -n 's/.*Published release \([0-9]\+\.[0-9]\+\.[0-9]\+\).*/\1/p')
            echo "new_release_version=$VERSION" >> $GITHUB_OUTPUT
            echo "new_release_git_tag=v$VERSION" >> $GITHUB_OUTPUT
            echo "✅ Released version $VERSION"
          else
            echo "new_release_published=false" >> $GITHUB_OUTPUT
            echo "ℹ️ No release created (no releasable changes found)"
          fi

          # Show the full log for debugging
          cat release_output.log

  # Build desktop apps for all platforms
  build-windows:
    runs-on: windows-latest
    needs: [semantic-release]
    if: always() && (needs.semantic-release.result == 'success' || needs.semantic-release.result == 'skipped')

    steps:
      - name: 📥 Checkout code
        uses: actions/checkout@v5

      - name: 🐍 Set up Python
        uses: actions/setup-python@v6
        with:
          python-version: '3.11'
          cache: pip
          cache-dependency-path: requirements.txt

      - name: 📝 Generate version file
        id: version
        shell: pwsh
        run: |
          if ("${{ github.ref }}" -eq "refs/heads/main" -and "${{ needs.semantic-release.outputs.new_release_published }}" -eq "true") {
            $version = "${{ needs.semantic-release.outputs.new_release_version }}"
            $channel = "stable"
          } else {
            $version = "dev-$($env:GITHUB_SHA.Substring(0,7))"
            $channel = "develop"
          }
          @"
          # Auto-generated at build time
          VERSION = "$version"
          RELEASE_CHANNEL = "$channel"
          "@ | Out-File -FilePath src/jarvis/_version.py -Encoding utf8
          Write-Host "Generated version file with VERSION=$version, RELEASE_CHANNEL=$channel"
          echo "app_version=$version" >> $env:GITHUB_OUTPUT

      - name: 📦 Install dependencies
        run: |
          python -m pip install --upgrade pip
          # Install requirements but skip heavy optional packages (PyTorch, etc.)
          # Filter out chatterbox-tts, mlx-whisper, and nvidia-* (CUDA libs are
          # downloaded by the installer on-demand, not bundled in the build)
          Get-Content requirements.txt | Where-Object { $_ -notmatch '^(chatterbox-tts|mlx-whisper|nvidia-)' } | Set-Content requirements-desktop.txt
          pip install -r requirements-desktop.txt
          pip install pyinstaller

      - name: 🎨 Generate icons
        run: |
          python src/desktop_app/desktop_assets/generate_icons.py

      - name: 🔨 Build executable (onedir)
        run: |
          pyinstaller jarvis_desktop.spec

      - name: 🛠️ Install Inno Setup
        run: |
          choco install innosetup -y

      - name: 📦 Build Windows installer
        run: |
          & "C:\Program Files (x86)\Inno Setup 6\ISCC.exe" /DMyAppVersion="${{ steps.version.outputs.app_version }}" installer\windows\jarvis_setup.iss

      - name: 📦 Package installer as Jarvis-Windows-x64.zip
        run: |
          # Rename installer to Jarvis.exe for backwards compatibility with old updaters
          Copy-Item dist\Jarvis-Setup-x64.exe dist\Jarvis.exe
          cd dist
          Compress-Archive -Path Jarvis.exe -DestinationPath Jarvis-Windows-x64.zip

      - name: 📤 Upload Windows artifact
        uses: actions/upload-artifact@v7
        with:
          name: Jarvis-Windows
          path: dist/Jarvis-Windows-x64.zip

  build-macos:
    runs-on: ${{ matrix.os }}
    needs: [semantic-release]
    if: always() && (needs.semantic-release.result == 'success' || needs.semantic-release.result == 'skipped')
    strategy:
      fail-fast: false
      matrix:
        include:
          - os: macos-latest  # Apple Silicon (arm64)
            arch: arm64
          - os: macos-15-intel  # Intel (x64)
            arch: x64

    steps:
      - name: 📥 Checkout code
        uses: actions/checkout@v5

      - name: 🐍 Set up Python
        uses: actions/setup-python@v6
        with:
          python-version: '3.11'
          cache: pip
          cache-dependency-path: requirements.txt

      - name: 📝 Generate version file
        run: |
          if [ "${{ github.ref }}" = "refs/heads/main" ] && [ "${{ needs.semantic-release.outputs.new_release_published }}" = "true" ]; then
            VERSION="${{ needs.semantic-release.outputs.new_release_version }}"
            CHANNEL="stable"
          else
            VERSION="dev-${GITHUB_SHA:0:7}"
            CHANNEL="develop"
          fi
          cat > src/jarvis/_version.py << EOF
          # Auto-generated at build time
          VERSION = "$VERSION"
          RELEASE_CHANNEL = "$CHANNEL"
          EOF
          echo "Generated version file with VERSION=$VERSION, RELEASE_CHANNEL=$CHANNEL"

      - name: 📦 Install dependencies
        run: |
          python -m pip install --upgrade pip
          # Install requirements but skip heavy optional packages (PyTorch/Chatterbox)
          # MLX Whisper is only included on arm64 - it requires Apple Silicon
          if [ "${{ matrix.arch }}" = "arm64" ]; then
            grep -v -E '^chatterbox-tts' requirements.txt > requirements-desktop.txt
          else
            grep -v -E '^(chatterbox-tts|mlx-whisper)' requirements.txt > requirements-desktop.txt
          fi
          pip install -r requirements-desktop.txt
          pip install pyinstaller

      - name: 🎨 Generate icons
        run: |
          python src/desktop_app/desktop_assets/generate_icons.py

      - name: 🔨 Build application
        run: |
          pyinstaller jarvis_desktop.spec

      # Note: Ad-hoc code signing is intentionally skipped
      # codesign --force --deep breaks Qt WebEngine's symlink structure
      # causing crashes when QWebEngineView is shown.
      # See: https://github.com/pyinstaller/pyinstaller/issues/6612
      # Users can bypass Gatekeeper by right-clicking and selecting "Open"

      - name: 📦 Package macOS build
        run: |
          cd dist
          # `ditto -c -k --keepParent` preserves the symlinks, xattrs, and
          # permissions that Qt/Qt WebEngine frameworks rely on. Plain
          # `zip -r` follows symlinks, producing a zip that extracts into a
          # bundle macOS refuses to launch ("Jarvis.app can't be opened").
          ditto -c -k --keepParent Jarvis.app Jarvis-macOS-${{ matrix.arch }}.zip

      - name: 📤 Upload macOS artifact
        uses: actions/upload-artifact@v7
        with:
          name: Jarvis-macOS-${{ matrix.arch }}
          path: dist/Jarvis-macOS-${{ matrix.arch }}.zip

  build-linux:
    runs-on: ubuntu-latest
    needs: [semantic-release]
    if: always() && (needs.semantic-release.result == 'success' || needs.semantic-release.result == 'skipped')

    steps:
      - name: 🧹 Free up disk space
        run: |
          # Remove unnecessary large packages to free up disk space
          sudo rm -rf /usr/share/dotnet
          sudo rm -rf /usr/local/lib/android
          sudo rm -rf /opt/ghc
          sudo rm -rf /opt/hostedtoolcache/CodeQL
          sudo docker image prune --all --force
          df -h

      - name: 📥 Checkout code
        uses: actions/checkout@v5

      - name: 🐍 Set up Python
        uses: actions/setup-python@v6
        with:
          python-version: '3.11'
          cache: pip
          cache-dependency-path: requirements.txt

      - name: 📦 Install system dependencies
        run: |
          sudo apt-get update
          sudo apt-get install -y libxcb-cursor0 libxkbcommon-x11-0 libxcb-icccm4 libxcb-image0 libxcb-keysyms1 libxcb-randr0 libxcb-render-util0 libxcb-shape0 portaudio19-dev binutils

      - name: 📝 Generate version file
        run: |
          if [ "${{ github.ref }}" = "refs/heads/main" ] && [ "${{ needs.semantic-release.outputs.new_release_published }}" = "true" ]; then
            VERSION="${{ needs.semantic-release.outputs.new_release_version }}"
            CHANNEL="stable"
          else
            VERSION="dev-${GITHUB_SHA:0:7}"
            CHANNEL="develop"
          fi
          cat > src/jarvis/_version.py << EOF
          # Auto-generated at build time
          VERSION = "$VERSION"
          RELEASE_CHANNEL = "$CHANNEL"
          EOF
          echo "Generated version file with VERSION=$VERSION, RELEASE_CHANNEL=$CHANNEL"

      - name: 📦 Install Python dependencies
        run: |
          python -m pip install --upgrade pip
          # Install requirements but skip heavy optional packages (PyTorch, etc.)
          grep -v -E '^(chatterbox-tts|mlx-whisper)' requirements.txt > requirements-desktop.txt
          pip install -r requirements-desktop.txt
          pip install pyinstaller

      - name: 🎨 Generate icons
        run: |
          python src/desktop_app/desktop_assets/generate_icons.py

      - name: 🔨 Build executable
        run: |
          pyinstaller jarvis_desktop.spec

      - name: 📦 Package Linux build
        run: |
          cd dist
          # Package the Jarvis directory (not a single file anymore)
          tar -czf Jarvis-Linux-x64.tar.gz Jarvis/

      - name: 📤 Upload Linux artifact
        uses: actions/upload-artifact@v7
        with:
          name: Jarvis-Linux
          path: dist/Jarvis-Linux-x64.tar.gz

  # Create versioned release (main only, if semantic-release published)
  release-main:
    needs: [semantic-release, build-windows, build-macos, build-linux]
    runs-on: ubuntu-latest
    # Run even if some builds failed - upload whatever succeeded
    if: always() && needs.semantic-release.result == 'success' && needs.semantic-release.outputs.new_release_published == 'true'

    permissions:
      contents: write

    steps:
      - name: 📥 Download all artifacts
        uses: actions/download-artifact@v8
        with:
          path: artifacts

      - name: 📋 List available artifacts
        run: |
          echo "Available artifacts:"
          find artifacts -type f \( -name "*.zip" -o -name "*.tar.gz" \) | sort

      - name: 📎 Attach binaries to release
        uses: softprops/action-gh-release@v3
        with:
          tag_name: ${{ needs.semantic-release.outputs.new_release_git_tag }}
          # Use glob to upload only artifacts that exist
          files: |
            artifacts/**/*.zip
            artifacts/**/*.tar.gz
          fail_on_unmatched_files: false
          append_body: true
          body: |

            ---

            ### ⚡ Prerequisites
            - [Ollama](https://ollama.com/download) (all platforms)

            ### 📦 Downloads
            | Platform | File | Notes |
            |----------|------|-------|
            | **Windows** | `Jarvis-Windows-x64.zip` | Extract → Run `Jarvis.exe` |
            | **macOS (Apple Silicon)** | `Jarvis-macOS-arm64.zip` | Extract → Move to Applications → Right-click → Open |
            | **macOS (Intel)** | `Jarvis-macOS-x64.zip` | Extract → Move to Applications → Right-click → Open |
            | **Linux** | `Jarvis-Linux-x64.tar.gz` | `tar -xzf` → Run `./Jarvis/Jarvis` |
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

  # Create/update latest pre-release (develop only)
  release-develop:
    needs: [build-windows, build-macos, build-linux]
    runs-on: ubuntu-latest
    # Run even if some builds failed - upload whatever succeeded
    if: always() && github.ref == 'refs/heads/develop'

    permissions:
      contents: write

    steps:
      - name: 📥 Checkout code
        uses: actions/checkout@v5
        with:
          fetch-depth: 0  # Full history for changelog generation
          fetch-tags: true  # Ensure all tags are fetched

      - name: 📝 Generate changelog from main
        id: changelog
        run: |
          # Get the latest tag on main (most recent stable release)
          LATEST_TAG=$(git describe --tags --abbrev=0 origin/main 2>/dev/null || echo "")

          if [ -z "$LATEST_TAG" ]; then
            echo "No tags found, using full develop history"
            COMPARE_REF="origin/main"
            SINCE_TEXT="main branch"
          else
            COMPARE_REF="$LATEST_TAG"
            SINCE_TEXT="$LATEST_TAG"
          fi

          echo "Generating changelog comparing to: $COMPARE_REF"

          # Generate changelog grouped by type
          {
            echo "CHANGELOG<<CHANGELOG_EOF"
            echo ""
            echo "## 📋 Changelog (since $SINCE_TEXT)"
            echo ""

            # Features
            FEATURES=$(git log "$COMPARE_REF"..HEAD --pretty=format:"* %s ([%h](https://github.com/${{ github.repository }}/commit/%H))" --grep="^feat" --regexp-ignore-case 2>/dev/null || true)
            if [ -n "$FEATURES" ]; then
              echo "### ✨ Features"
              echo ""
              echo "$FEATURES"
              echo ""
            fi

            # Bug fixes
            FIXES=$(git log "$COMPARE_REF"..HEAD --pretty=format:"* %s ([%h](https://github.com/${{ github.repository }}/commit/%H))" --grep="^fix" --regexp-ignore-case 2>/dev/null || true)
            if [ -n "$FIXES" ]; then
              echo "### 🐛 Bug Fixes"
              echo ""
              echo "$FIXES"
              echo ""
            fi

            # Refactoring
            REFACTOR=$(git log "$COMPARE_REF"..HEAD --pretty=format:"* %s ([%h](https://github.com/${{ github.repository }}/commit/%H))" --grep="^refactor" --regexp-ignore-case 2>/dev/null || true)
            if [ -n "$REFACTOR" ]; then
              echo "### ♻️ Code Refactoring"
              echo ""
              echo "$REFACTOR"
              echo ""
            fi

            # Documentation
            DOCS=$(git log "$COMPARE_REF"..HEAD --pretty=format:"* %s ([%h](https://github.com/${{ github.repository }}/commit/%H))" --grep="^docs" --regexp-ignore-case 2>/dev/null || true)
            if [ -n "$DOCS" ]; then
              echo "### 📝 Documentation"
              echo ""
              echo "$DOCS"
              echo ""
            fi

            # Other changes (chore, style, test, etc.)
            # Get all commits, then exclude the ones we already captured
            OTHER=$(git log "$COMPARE_REF"..HEAD --pretty=format:"%s|%h|%H" 2>/dev/null | grep -v -i -E "^(feat|fix|refactor|docs)" | while IFS='|' read -r subject short full; do
              if [ -n "$subject" ]; then
                echo "* $subject ([$short](https://github.com/${{ github.repository }}/commit/$full))"
              fi
            done || true)
            if [ -n "$OTHER" ]; then
              echo "### 🔧 Other Changes"
              echo ""
              echo "$OTHER"
              echo ""
            fi

            echo "CHANGELOG_EOF"
          } >> $GITHUB_OUTPUT

      - name: 📥 Download all artifacts
        uses: actions/download-artifact@v8
        with:
          path: artifacts

      - name: 📋 List available artifacts
        run: |
          echo "Available artifacts:"
          find artifacts -type f \( -name "*.zip" -o -name "*.tar.gz" \) | sort

      - name: 📝 Create/Update Latest Release
        uses: softprops/action-gh-release@v3
        with:
          tag_name: latest
          name: Latest Development Build
          # Use glob to upload only artifacts that exist
          files: |
            artifacts/**/*.zip
            artifacts/**/*.tar.gz
          fail_on_unmatched_files: false
          draft: false
          prerelease: true
          body: |
            🚀 **Latest development build from develop branch**

            This is an automated build from the latest commit on develop.
            These builds may be unstable. For stable releases, use versioned releases.

            ---
            ${{ steps.changelog.outputs.CHANGELOG }}
            ---

            ### ⚡ Prerequisites
            - [Ollama](https://ollama.com/download) (all platforms)

            ### 📦 Downloads
            | Platform | File | Notes |
            |----------|------|-------|
            | **Windows** | `Jarvis-Windows-x64.zip` | Extract → Run `Jarvis.exe` |
            | **macOS (Apple Silicon)** | `Jarvis-macOS-arm64.zip` | Extract → Move to Applications → Right-click → Open |
            | **macOS (Intel)** | `Jarvis-macOS-x64.zip` | Extract → Move to Applications → Right-click → Open |
            | **Linux** | `Jarvis-Linux-x64.tar.gz` | `tar -xzf` → Run `./Jarvis/Jarvis` |

            **Branch**: develop
            **Commit**: ${{ github.sha }}
            **Date**: ${{ github.event.head_commit.timestamp }}
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

