name: Release

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

permissions:
  contents: write
  # `models: read` is required for the dogfooded release-notes step,
  # which calls the just-built chronikl binary against GitHub Models.
  models: read

jobs:
  build:
    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

    outputs:
      is_prerelease: ${{ steps.detect.outputs.is_prerelease }}
    runs-on: ${{ matrix.os }}
    steps:
      - uses: actions/checkout@v6

      - uses: dtolnay/rust-toolchain@stable
        with:
          targets: ${{ 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: Set version from tag
        id: detect
        run: |
          VERSION="${GITHUB_REF_NAME#v}"
          # BSD sed (macOS) requires a backup extension with -i; GNU sed does not.
          if [[ "$OSTYPE" == darwin* ]]; then
            sed -i '' "1,/^version = .*/s/^version = .*/version = \"${VERSION}\"/" Cargo.toml
          else
            sed -i "1,/^version = .*/s/^version = .*/version = \"${VERSION}\"/" Cargo.toml
          fi

          # Detect pre-release suffix (e.g. -alpha, -beta.1, -rc.2)
          if [[ "$VERSION" == *-* ]]; then
            echo "IS_PRERELEASE=true" >> "$GITHUB_ENV"
            echo "is_prerelease=true" >> "$GITHUB_OUTPUT"
          else
            echo "IS_PRERELEASE=false" >> "$GITHUB_ENV"
            echo "is_prerelease=false" >> "$GITHUB_OUTPUT"
          fi

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

      - name: Strip binary
        run: |
          BIN="target/${{ matrix.target }}/release/chronikl"
          if [ "${{ matrix.target }}" = "aarch64-unknown-linux-gnu" ]; then
            aarch64-linux-gnu-strip "$BIN"
          else
            strip "$BIN"
          fi

      - name: Package
        run: |
          ARCHIVE="chronikl-${{ matrix.target }}.tar.gz"
          tar czf "$ARCHIVE" -C "target/${{ matrix.target }}/release" chronikl
          echo "ARCHIVE=$ARCHIVE" >> "$GITHUB_ENV"

      - name: Upload artifact
        uses: actions/upload-artifact@v6
        with:
          name: chronikl-${{ matrix.target }}
          path: ${{ env.ARCHIVE }}

      - name: Upload patched manifest
        if: matrix.target == 'x86_64-unknown-linux-gnu'
        uses: actions/upload-artifact@v6
        with:
          name: patched-manifest
          path: Cargo.toml

  release:
    needs: build
    runs-on: ubuntu-latest
    env:
      FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true
    steps:
      - uses: actions/checkout@v6
        with:
          fetch-depth: 0

      - uses: actions/download-artifact@v7
        with:
          path: artifacts
          merge-multiple: true

      - name: Generate checksums
        run: |
          cd artifacts
          sha256sum *.tar.gz > SHA256SUMS

      - name: Extract chronikl binary for self-use
        run: |
          tar xzf artifacts/chronikl-x86_64-unknown-linux-gnu.tar.gz
          chmod +x chronikl

      # Dogfood: the just-built chronikl writes its own release notes,
      # talking to GitHub Models via the workflow's GITHUB_TOKEN (no
      # extra secret required — `permissions: models: read` is enough).
      # Falls back to raw git log if chronikl fails for any reason.
      - name: Build changelog with chronikl
        id: changelog
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
          CHRONIKL_PROVIDER: github-models
          CHRONIKL_MODEL: openai/gpt-4o-mini
        run: |
          # Find the previous tag (excluding floating major/minor tags like v0, v0.2)
          PREV_TAG=$(git tag --list 'v*.*.*' --sort=-v:refname \
            | grep -v "^${GITHUB_REF_NAME}$" | head -n 1)

          if [ -n "$PREV_TAG" ]; then
            FROM="$PREV_TAG"
            DIFF_URL="${GITHUB_SERVER_URL}/${GITHUB_REPOSITORY}/compare/${PREV_TAG}...${GITHUB_REF_NAME}"
          else
            # No prior tag yet — diff against the root commit.
            FROM=$(git rev-list --max-parents=0 HEAD | head -n 1)
            DIFF_URL=""
          fi

          if ./chronikl --from "$FROM" --to "$GITHUB_REF_NAME" \
               --voice prose --rich-context \
               --output release_notes.md; then
            echo "chronikl: generated release notes ($(wc -c < release_notes.md) bytes)"
          else
            echo "chronikl failed; falling back to raw git log"
            {
              echo "## What's Changed"
              echo ""
              git log "${FROM}..${GITHUB_REF_NAME}" --pretty=format:"- %s (%h)" --no-merges
              echo ""
              if [ -n "$DIFF_URL" ]; then
                echo ""
                echo "**Full Changelog**: ${DIFF_URL}"
              fi
            } > release_notes.md
          fi

      - name: Create release
        uses: softprops/action-gh-release@v2
        with:
          body_path: release_notes.md
          prerelease: ${{ needs.build.outputs.is_prerelease == 'true' }}
          files: |
            artifacts/*.tar.gz
            artifacts/SHA256SUMS

  publish:
    needs: [release, build]
    if: needs.build.outputs.is_prerelease != 'true'
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v6

      - name: Download patched manifest
        uses: actions/download-artifact@v7
        with:
          name: patched-manifest

      - uses: dtolnay/rust-toolchain@stable

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

  update-homebrew:
    needs: [release, build]
    if: needs.build.outputs.is_prerelease != 'true'
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v6
        with:
          repository: nsrosenqvist/homebrew-chronikl
          token: ${{ secrets.HOMEBREW_TAP_TOKEN }}

      - name: Update formula
        run: |
          VERSION="${GITHUB_REF_NAME#v}"
          TAG="${GITHUB_REF_NAME}"
          FORMULA="Formula/chronikl.rb"

          # Download the checksums file from the release
          curl -sSfL "https://github.com/nsrosenqvist/chronikl/releases/download/${TAG}/SHA256SUMS" -o SHA256SUMS

          # Extract per-target checksums
          SHA_MACOS_ARM=$(grep 'aarch64-apple-darwin' SHA256SUMS | awk '{print $1}')
          SHA_MACOS_X86=$(grep 'x86_64-apple-darwin' SHA256SUMS | awk '{print $1}')
          SHA_LINUX_ARM=$(grep 'aarch64-unknown-linux-gnu' SHA256SUMS | awk '{print $1}')
          SHA_LINUX_X86=$(grep 'x86_64-unknown-linux-gnu' SHA256SUMS | awk '{print $1}')

          # Update version
          sed -i "s/version \".*\"/version \"${VERSION}\"/" "$FORMULA"

          # Replace sha256 values in order of appearance in the formula:
          # 1. macOS ARM, 2. macOS Intel, 3. Linux ARM, 4. Linux Intel
          python3 -c "
          import re
          shas = ['${SHA_MACOS_ARM}', '${SHA_MACOS_X86}', '${SHA_LINUX_ARM}', '${SHA_LINUX_X86}']
          with open('${FORMULA}') as f:
              content = f.read()
          i = 0
          def replace_sha(m):
              global i
              s = 'sha256 \"' + shas[i] + '\"'
              i += 1
              return s
          content = re.sub(r'sha256 \"[a-fA-F0-9]+\"', replace_sha, content)
          # Also handle PLACEHOLDER values (first-time setup)
          content = re.sub(r'sha256 \"PLACEHOLDER\"', replace_sha, content)
          with open('${FORMULA}', 'w') as f:
              f.write(content)
          "

      - name: Commit and push
        run: |
          git config user.name "github-actions[bot]"
          git config user.email "github-actions[bot]@users.noreply.github.com"
          git add Formula/chronikl.rb
          git diff --cached --quiet && echo "No changes" && exit 0
          git commit -m "chronikl ${GITHUB_REF_NAME}"
          git push

  update-floating-tags:
    needs: [release, build]
    if: needs.build.outputs.is_prerelease != 'true'
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v6
        with:
          fetch-depth: 0

      - name: Update floating major and minor version tags
        run: |
          VERSION="${GITHUB_REF_NAME#v}"
          MAJOR="v${VERSION%%.*}"
          MINOR="v${VERSION%.*}"

          for TAG in "$MAJOR" "$MINOR"; do
            git tag -f "$TAG" "$GITHUB_REF_NAME"
            git push origin "$TAG" --force
          done

  notify-web:
    needs: [release, build]
    if: needs.build.outputs.is_prerelease != 'true'
    runs-on: ubuntu-latest
    steps:
      - name: Notify chronikl-web of new release
        uses: peter-evans/repository-dispatch@v3
        with:
          token: ${{ secrets.CHRONIKL_WEB_DISPATCH_TOKEN }}
          repository: nsrosenqvist/chronikl-web
          event-type: release-published
          client-payload: '{"tag": "${{ github.ref_name }}"}'
