name: Release

on:
  push:
    tags:
      - "v*"

permissions:
  contents: write

jobs:
  build-release:
    name: build-release
    runs-on: ${{ matrix.os }}
    strategy:
      matrix:
        include:
          - os: ubuntu-latest
            target: x86_64-unknown-linux-gnu
            artifact_name: oly
            asset_name: oly-linux-amd64.zip
          - os: macos-latest
            target: aarch64-apple-darwin
            artifact_name: oly
            asset_name: oly-macos-arm64.zip
          - os: windows-latest
            target: x86_64-pc-windows-msvc
            artifact_name: oly.exe
            asset_name: oly-windows-amd64.zip

    steps:
      - name: Checkout code
        uses: actions/checkout@v4

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

      - name: Install Node.js
        uses: actions/setup-node@v4
        with:
          node-version: 22

      - name: Resolve npm cache directory
        id: npm-cache-dir
        shell: bash
        run: echo "dir=$(npm config get cache)" >> "$GITHUB_OUTPUT"

      - name: Cache Cargo registry
        uses: actions/cache@v4
        with:
          path: |
            ~/.cargo/registry/index/
            ~/.cargo/registry/cache/
            ~/.cargo/git/db/
            ~/.cargo/git/checkouts/
          key: ${{ runner.os }}-cargo-${{ hashFiles('Cargo.lock') }}
          restore-keys: |
            ${{ runner.os }}-cargo-

      - name: Cache npm registry
        uses: actions/cache@v4
        with:
          path: ${{ steps.npm-cache-dir.outputs.dir }}
          key: ${{ runner.os }}-npm-${{ hashFiles('web/package-lock.json') }}
          restore-keys: |
            ${{ runner.os }}-npm-

      - name: Set version from tag
        if: startsWith(github.ref, 'refs/tags/')
        env:
          VERSION: ${{ github.ref_name }}
        shell: pwsh
        run: |
          $version = $env:VERSION -replace '^v', ''
          $cargoToml = 'Cargo.toml'
          $content = Get-Content $cargoToml -Raw
          $updated = [regex]::Replace($content, '(?m)^version = "[^"]+"', "version = `"$version`"", 1)

          if ($updated -eq $content) {
            throw "Failed to update Cargo.toml version from tag $version"
          }

          Set-Content $cargoToml $updated -NoNewline

      # - name: Test
      #   run: cargo test --target ${{ matrix.target }}
        
      - name: Build release binary
        env:
          NPM_CONFIG_INCLUDE: optional
        run: cargo build --release --target ${{ matrix.target }}

      - name: Package binary (zip)
        run: |
          Compress-Archive -Path "target/${{ matrix.target }}/release/${{ matrix.artifact_name }}" -DestinationPath "target/${{ matrix.target }}/release/${{ matrix.asset_name }}" -Force
        shell: pwsh

      - name: Upload workflow artifact
        uses: actions/upload-artifact@v4
        with:
          name: ${{ matrix.asset_name }}
          path: target/${{ matrix.target }}/release/${{ matrix.asset_name }}

      - name: Upload binary to Release
        uses: softprops/action-gh-release@v2
        if: startsWith(github.ref, 'refs/tags/')
        with:
          files: target/${{ matrix.target }}/release/${{ matrix.asset_name }}
          generate_release_notes: false

  update-homebrew-formula-and-cargo-toml:
    name: update-homebrew-formula-and-cargo-toml
    needs: build-release
    runs-on: ubuntu-latest
    if: startsWith(github.ref, 'refs/tags/')
    steps:
      - name: Checkout default branch
        uses: actions/checkout@v4
        with:
          ref: ${{ github.event.repository.default_branch }}

      - name: Download release artifacts
        uses: actions/download-artifact@v4
        with:
          path: dist

      - name: Update Formula/oly.rb
        env:
          VERSION: ${{ github.ref_name }}
        run: |
          VERSION="${VERSION#v}"
          MACOS_SHA="$(sha256sum dist/oly-macos-arm64.zip/oly-macos-arm64.zip | awk '{print $1}')"
          LINUX_SHA="$(sha256sum dist/oly-linux-amd64.zip/oly-linux-amd64.zip | awk '{print $1}')"
          export VERSION MACOS_SHA LINUX_SHA

          ruby <<'RUBY'
          formula_path = "Formula/oly.rb"
          formula = File.read(formula_path)
          cargo_toml_path = "Cargo.toml"
          cargo_toml = File.read(cargo_toml_path)
          version = ENV.fetch("VERSION")
          macos_sha = ENV.fetch("MACOS_SHA")
          linux_sha = ENV.fetch("LINUX_SHA")

          formula.sub!(/^  version "[^"]+"/, %(  version "#{version}"))
          formula.sub!(/(on_macos do.*?sha256 ")([^"]+)(")/m, "\\1#{macos_sha}\\3")
          formula.sub!(/(on_linux do.*?sha256 ")([^"]+)(")/m, "\\1#{linux_sha}\\3")
          cargo_toml.sub!(/^version = "[^"]+"/, %(version = "#{version}"))

          File.write(formula_path, formula)
          File.write(cargo_toml_path, cargo_toml)
          RUBY

      - name: Commit updated formula
        env:
          VERSION: ${{ github.ref_name }}
        run: |
          VERSION="${VERSION#v}"
          if git diff --quiet -- Cargo.toml Formula/oly.rb; then
            echo "Release metadata already up to date"
            exit 0
          fi

          git config user.name "github-actions[bot]"
          git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
          git add Cargo.toml Formula/oly.rb
          git commit -m "chore(release): sync release metadata for ${VERSION}"
          git push origin HEAD:${{ github.event.repository.default_branch }}

  publish-crates:
    name: publish-crates
    needs: build-release
    runs-on: ubuntu-latest
    if: startsWith(github.ref, 'refs/tags/')
    steps:
      - name: Checkout code
        uses: actions/checkout@v4

      - name: Install Rust toolchain
        uses: dtolnay/rust-toolchain@stable

      - name: Install Node.js
        uses: actions/setup-node@v4
        with:
          node-version: 22

      - name: Set version from tag
        run: |
          VERSION="${GITHUB_REF_NAME#v}"
          sed -i -E "s/^version = \"[^\"]+\"/version = \"$VERSION\"/" Cargo.toml

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

  publish-npm:
    name: publish-npm
    needs: build-release
    runs-on: ubuntu-latest
    if: startsWith(github.ref, 'refs/tags/')
    steps:
      - name: Checkout code
        uses: actions/checkout@v4

      - name: Install Node.js
        uses: actions/setup-node@v4
        with:
          node-version: 22
          registry-url: "https://registry.npmjs.org"

      - name: Set version from tag
        run: |
          VERSION="${GITHUB_REF_NAME#v}"
          npm version "$VERSION" --no-git-tag-version
        working-directory: npm

      - name: Download release artifacts
        uses: actions/download-artifact@v4
        with:
          path: dist

      - name: Bundle release binaries into npm package
        run: |
          TMP_DIR="$(mktemp -d)"
          trap 'rm -rf "$TMP_DIR"' EXIT

          LINUX_ZIP="$(find dist -type f -name 'oly-linux-amd64.zip' -print -quit)"
          MACOS_ZIP="$(find dist -type f -name 'oly-macos-arm64.zip' -print -quit)"
          WINDOWS_ZIP="$(find dist -type f -name 'oly-windows-amd64.zip' -print -quit)"

          test -n "$LINUX_ZIP"
          test -n "$MACOS_ZIP"
          test -n "$WINDOWS_ZIP"

          unzip -j "$LINUX_ZIP" oly -d "$TMP_DIR"
          mv "$TMP_DIR/oly" npm/bin/oly-linux-x64
          unzip -j "$MACOS_ZIP" oly -d "$TMP_DIR"
          mv "$TMP_DIR/oly" npm/bin/oly-darwin-arm64
          unzip -j "$WINDOWS_ZIP" oly.exe -d "$TMP_DIR"
          mv "$TMP_DIR/oly.exe" npm/bin/oly-win32-x64.exe
          chmod +x npm/bin/oly-linux-x64 npm/bin/oly-darwin-arm64

          test -f npm/bin/oly.js
          test -f npm/bin/oly-linux-x64
          test -f npm/bin/oly-darwin-arm64
          test -f npm/bin/oly-win32-x64.exe

      - name: Copy README to npm package
        run: cp README.md npm/README.md

      - name: Publish to npm
        env:
          NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
        run: npm publish --access public
        working-directory: npm

  # publish-winget:
  #   name: publish-winget
  #   needs: build-release
  #   runs-on: windows-latest
  #   if: startsWith(github.ref, 'refs/tags/')
  #   steps:
  #     - name: Submit to Windows Package Manager
  #       uses: vedantmgoyal9/winget-releaser@v2
  #       with:
  #         identifier: Slaveoftime.oly
  #         installers-regex: 'oly-windows-amd64\.zip$'
  #         token: ${{ secrets.WINGET_TOKEN }}
