name: Release

on:
  push:
    branches: [main]

permissions:
  contents: write

env:
  CARGO_TERM_COLOR: always

jobs:
  check-version:
    name: Check for version bump
    runs-on: ubuntu-latest
    outputs:
      version: ${{ steps.version.outputs.version }}
      tag: ${{ steps.version.outputs.tag }}
      should_release: ${{ steps.check.outputs.should_release }}
    steps:
      - uses: actions/checkout@v5
        with:
          fetch-depth: 0

      - name: Get version from Cargo.toml
        id: version
        run: |
          VERSION=$(grep '^version' Cargo.toml | head -1 | sed 's/.*"\(.*\)".*/\1/')
          echo "version=$VERSION" >> "$GITHUB_OUTPUT"
          echo "tag=v$VERSION" >> "$GITHUB_OUTPUT"

      - name: Check if tag exists
        id: check
        run: |
          TAG="v$(grep '^version' Cargo.toml | head -1 | sed 's/.*"\(.*\)".*/\1/')"
          if git rev-parse "$TAG" >/dev/null 2>&1; then
            echo "should_release=false" >> "$GITHUB_OUTPUT"
            echo "Tag $TAG already exists, skipping release"
          else
            echo "should_release=true" >> "$GITHUB_OUTPUT"
            echo "Tag $TAG does not exist, will release"
          fi

  test:
    name: Test
    needs: check-version
    if: needs.check-version.outputs.should_release == 'true'
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v5

      - name: Install Rust
        run: |
          rustup toolchain install stable --profile default --component clippy
          rustup default stable

      - name: Cache cargo registry and build
        uses: actions/cache@v5
        with:
          path: |
            ~/.cargo/registry
            ~/.cargo/git
            target
          key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }}
          restore-keys: ${{ runner.os }}-cargo-

      - uses: extractions/setup-just@v4

      - name: Run checks
        run: just check

  build-binaries:
    name: Build ${{ matrix.target }}
    needs: [check-version, test]
    if: needs.check-version.outputs.should_release == 'true'
    strategy:
      matrix:
        include:
          - target: x86_64-unknown-linux-gnu
            os: ubuntu-latest
          - target: aarch64-unknown-linux-gnu
            os: ubuntu-latest
          - target: x86_64-apple-darwin
            os: macos-latest
          - target: aarch64-apple-darwin
            os: macos-latest
          - target: x86_64-pc-windows-msvc
            os: windows-latest
    runs-on: ${{ matrix.os }}
    steps:
      - uses: actions/checkout@v5

      - name: Install Rust
        run: |
          rustup toolchain install stable --profile minimal
          rustup default stable
          rustup target add ${{ matrix.target }}

      - name: Install cross-compilation tools
        if: matrix.target == 'aarch64-unknown-linux-gnu'
        run: |
          sudo apt-get update
          sudo apt-get install -y gcc-aarch64-linux-gnu
          echo "CARGO_TARGET_AARCH64_UNKNOWN_LINUX_GNU_LINKER=aarch64-linux-gnu-gcc" >> "$GITHUB_ENV"

      - name: Build
        run: cargo build --release --target ${{ matrix.target }}

      - name: Package (unix)
        if: runner.os != 'Windows'
        run: |
          cd target/${{ matrix.target }}/release
          tar czf ../../../phx-${{ matrix.target }}.tar.gz phx
          cd ../../..

      - name: Package (windows)
        if: runner.os == 'Windows'
        shell: pwsh
        run: |
          Compress-Archive -Path "target/${{ matrix.target }}/release/phx.exe" -DestinationPath "phx-${{ matrix.target }}.zip"

      - name: Upload artifact
        uses: actions/upload-artifact@v7
        with:
          name: binary-${{ matrix.target }}
          path: phx-${{ matrix.target }}.*

  publish-crate:
    name: Publish to crates.io
    needs: [check-version, test]
    if: needs.check-version.outputs.should_release == 'true'
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v5

      - name: Install Rust
        run: |
          rustup toolchain install stable --profile minimal
          rustup default stable

      - name: Cache cargo registry and build
        uses: actions/cache@v5
        with:
          path: |
            ~/.cargo/registry
            ~/.cargo/git
            target
          key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }}
          restore-keys: ${{ runner.os }}-cargo-

      - uses: extractions/setup-just@v4

      - name: Publish
        run: just publish
        env:
          CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }}

  github-release:
    name: Create GitHub Release
    needs: [check-version, test, publish-crate, build-binaries]
    if: needs.check-version.outputs.should_release == 'true'
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v5
        with:
          fetch-depth: 0

      - name: Download all artifacts
        uses: actions/download-artifact@v8
        with:
          pattern: binary-*
          merge-multiple: true

      - name: Create tag
        run: |
          git tag ${{ needs.check-version.outputs.tag }}
          git push origin ${{ needs.check-version.outputs.tag }}

      - name: Create GitHub Release
        env:
          GH_TOKEN: ${{ github.token }}
        run: |
          PREV_TAG=$(git tag --sort=-v:refname | grep -v "^${{ needs.check-version.outputs.tag }}$" | head -1)
          if [ -z "$PREV_TAG" ]; then
            NOTES=$(git log --pretty=format:"- %s" HEAD)
          else
            NOTES=$(git log --pretty=format:"- %s" "$PREV_TAG"..HEAD)
          fi

          gh release create "${{ needs.check-version.outputs.tag }}" \
            phx-*.tar.gz phx-*.zip \
            --title "${{ needs.check-version.outputs.tag }}" \
            --notes "## phx ${{ needs.check-version.outputs.version }}

          \`\`\`sh
          cargo binstall phx
          # or
          cargo install phx@${{ needs.check-version.outputs.version }}
          \`\`\`

          ### Changes

          $NOTES"
