name: Release

on:
  push:
    tags:
      - 'v[0-9]+.*'

env:
  CARGO_TERM_COLOR: always
  CARGO_INCREMENTAL: 0

jobs:
  prepare:
    name: Prepare Release
    runs-on: ubuntu-latest
    outputs:
      version: ${{ steps.version.outputs.version }}
    steps:
      - name: Extract version from tag
        id: version
        run: |
          VERSION="${GITHUB_REF_NAME#v}"
          echo "version=$VERSION" >> $GITHUB_OUTPUT
          echo "Release version: $VERSION"

  validate:
    name: Validate Release
    needs: prepare
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v6

      - name: Verify version matches Cargo.toml
        run: |
          CARGO_VERSION=$(grep '^version = ' Cargo.toml | head -1 | sed 's/version = "\(.*\)"/\1/')
          if [ "$CARGO_VERSION" != "${{ needs.prepare.outputs.version }}" ]; then
            echo "Error: Tag version (${{ needs.prepare.outputs.version }}) does not match Cargo.toml version ($CARGO_VERSION)"
            exit 1
          fi
          echo "Version verified: $CARGO_VERSION"

      - name: Check CHANGELOG has entry
        run: |
          if ! grep -q "\[${{ needs.prepare.outputs.version }}\]" CHANGELOG.md; then
            echo "Error: CHANGELOG.md does not contain an entry for version ${{ needs.prepare.outputs.version }}"
            exit 1
          fi
          echo "CHANGELOG entry found"

  format:
    name: Format
    needs: validate
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v6
      - uses: dtolnay/rust-toolchain@nightly
        with:
          components: rustfmt
      - run: cargo fmt --all --check

  clippy:
    name: Clippy
    needs: validate
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v6
      - uses: dtolnay/rust-toolchain@stable
        with:
          components: clippy
      - uses: Swatinem/rust-cache@v2
      - run: cargo clippy --all-targets --all-features -- -D warnings

  test:
    name: Test Suite
    needs: validate
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v6
      - uses: dtolnay/rust-toolchain@stable
      - uses: Swatinem/rust-cache@v2
      - name: Run tests
        run: cargo test --all-features

  docs:
    name: Documentation
    needs: validate
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v6
      - uses: dtolnay/rust-toolchain@stable
      - uses: Swatinem/rust-cache@v2
      - name: Build docs
        run: cargo doc --all-features --no-deps
        env:
          RUSTDOCFLAGS: -D warnings

  publish:
    name: Publish to crates.io
    needs: [prepare, format, clippy, test, docs]
    runs-on: ubuntu-latest
    environment: release
    steps:
      - uses: actions/checkout@v6
      - uses: dtolnay/rust-toolchain@stable
      - uses: Swatinem/rust-cache@v2

      - name: Update lockfile
        run: cargo update --workspace

      - name: Publish
        run: cargo publish --allow-dirty
        env:
          CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }}

  github-release:
    name: Create GitHub Release
    needs: [prepare, publish]
    runs-on: ubuntu-latest
    permissions:
      contents: write
    steps:
      - uses: actions/checkout@v6

      - name: Extract changelog
        id: changelog
        run: |
          VERSION="${{ needs.prepare.outputs.version }}"
          CHANGELOG=$(awk "/## \[$VERSION\]/{flag=1; next} /## \[/{flag=0} flag" CHANGELOG.md)
          echo "changelog<<EOF" >> $GITHUB_OUTPUT
          echo "$CHANGELOG" >> $GITHUB_OUTPUT
          echo "EOF" >> $GITHUB_OUTPUT

      - name: Create release
        uses: softprops/action-gh-release@v2
        with:
          tag_name: v${{ needs.prepare.outputs.version }}
          name: v${{ needs.prepare.outputs.version }}
          body: ${{ steps.changelog.outputs.changelog }}
          draft: false
          prerelease: ${{ contains(needs.prepare.outputs.version, '-') }}
