name: Release (Alpha)

on:
  push:
    branches:
      - main
    paths-ignore:
      - ".husky/**"
      - ".github/workflows/deploy-docs.yml"
      - ".github/workflows/release.yml"
      - "site/**"

concurrency:
  group: release-alpha-${{ github.ref }}
  cancel-in-progress: true

jobs:
  # ─────────────────────────────────────────────────────────────
  # Phase 1: Compute the alpha version string once
  # Alpha builds use calendar semver and stay newer than the latest stable tag.
  # ─────────────────────────────────────────────────────────────
  version:
    name: Compute alpha version
    runs-on: ubuntu-latest
    outputs:
      version: ${{ steps.ver.outputs.version }}
      display_version: ${{ steps.ver.outputs.display_version }}
      tag: ${{ steps.ver.outputs.tag }}
    steps:
      - uses: actions/checkout@v4
        with:
          fetch-depth: 0

      - id: ver
        shell: bash
        run: |
          python3 <<'PY' > version.env
          import re
          import subprocess
          from datetime import datetime, timedelta, timezone

          def lines(command: list[str]) -> list[str]:
              output = subprocess.check_output(command, text=True).strip()
              return [line for line in output.splitlines() if line]

          alpha_pattern = re.compile(r"^alpha-v(\d{4}\.\d{1,2}\.\d{1,2})-alpha\.(\d+)$")

          def parse_alpha_tag(tag: str) -> tuple[str, int] | None:
              match = alpha_pattern.fullmatch(tag)
              if not match:
                  return None
              calendar_version, sequence = match.groups()
              return calendar_version, int(sequence)

          def alpha_version(calendar_version: str, sequence: int) -> str:
              return f"{calendar_version}-alpha.{sequence}"

          def alpha_tag(calendar_version: str, sequence: int) -> str:
              return f"alpha-v{calendar_version}-alpha.{sequence:04d}"

          existing_tags = [
              tag for tag in lines(["git", "tag", "--points-at", "HEAD"])
              if tag.startswith("alpha-v")
          ]

          if existing_tags:
              tag = existing_tags[0]
              parsed = parse_alpha_tag(tag)
              version = alpha_version(*parsed) if parsed is not None else tag.removeprefix("alpha-v")
          else:
              today = datetime.now(timezone.utc).date()
              stable_date = None
              stable_patterns = (
                  re.compile(r"^v(\d{4})-(\d{2})-(\d{2})$"),
                  re.compile(r"^stable-v(\d{4})\.(\d{1,2})\.(\d{1,2})$"),
              )

              stable_tags = lines([
                  "git", "for-each-ref", "--sort=-creatordate", "--format=%(refname:short)",
                  "refs/tags/v20*", "refs/tags/stable-v*",
              ])

              for stable_tag in stable_tags:
                  match = next((pattern.fullmatch(stable_tag) for pattern in stable_patterns if pattern.fullmatch(stable_tag)), None)
                  if not match:
                      continue

                  year, month, day = map(int, match.groups())
                  try:
                      stable_date = datetime(year, month, day, tzinfo=timezone.utc).date()
                  except ValueError:
                      continue
                  break

              alpha_date = today if stable_date is None or today > stable_date else stable_date + timedelta(days=1)
              calendar_version = f"{alpha_date.year}.{alpha_date.month}.{alpha_date.day}"
              sequence = len(lines(["git", "tag", "--list", f"alpha-v{calendar_version}-alpha.*"])) + 1

              version = alpha_version(calendar_version, sequence)
              tag = alpha_tag(calendar_version, sequence)

          display_match = re.fullmatch(r"(\d{4})\.(\d{1,2})\.(\d{1,2})-alpha\.(\d+)", version)
          display_version = (
              f"Alpha {int(display_match.group(1))}.{int(display_match.group(2))}.{int(display_match.group(3))}.{int(display_match.group(4))}"
              if display_match
              else version
          )

          print(f"version={version}")
          print(f"display_version={display_version}")
          print(f"tag={tag}")
          PY

          cat version.env >> "$GITHUB_OUTPUT"
          VERSION=$(grep '^version=' version.env | cut -d= -f2-)
          DISPLAY_VERSION=$(grep '^display_version=' version.env | cut -d= -f2-)
          echo "### Alpha version: \`$DISPLAY_VERSION\` (\`$VERSION\`)" >> "$GITHUB_STEP_SUMMARY"

  # -------------------------------------------------------------
  # Phase 2: Build shared release artifacts
  # -------------------------------------------------------------
  build-artifacts:
    name: Build release artifacts
    needs: version
    uses: ./.github/workflows/release-build-artifacts.yml
    with:
      version: ${{ needs.version.outputs.version }}
      macos_bundles: app
      upload_macos_dmg: false
    secrets: inherit

  # ─────────────────────────────────────────────────────────────
  # Phase 3: Publish GitHub Release
  # No lipo/re-signing — use the per-arch artifacts directly
  # ─────────────────────────────────────────────────────────────
  release:
    name: GitHub Release (alpha)
    needs: [version, build-artifacts]
    runs-on: ubuntu-latest
    permissions:
      contents: write
      pages: write
    steps:
      - uses: actions/checkout@v4
        with:
          fetch-depth: 0

      - name: Download all artifacts
        uses: actions/download-artifact@v4

      - name: Normalize macOS updater artifact names
        run: |
          normalize_updater() {
            local arch="$1"
            local normalized_updater="$2"
            local artifact_dir="updater-${arch}"
            local updater_file
            updater_file=$(find "$artifact_dir" -maxdepth 1 -name "*.app.tar.gz" -print -quit)
            if [ -z "$updater_file" ]; then
              echo "::error::Missing macOS updater artifact in ${artifact_dir}" >&2
              return 1
            fi

            local sig_file="${updater_file}.sig"
            if [ ! -f "$sig_file" ]; then
              echo "::error::Missing macOS updater signature for ${updater_file}" >&2
              return 1
            fi

            local normalized_sig="${normalized_updater}.sig"
            if [ "$updater_file" != "$normalized_updater" ]; then
              mv "$updater_file" "$normalized_updater"
            fi
            if [ "$sig_file" != "$normalized_sig" ]; then
              mv "$sig_file" "$normalized_sig"
            fi
          }

          normalize_updater aarch64 "updater-aarch64/Tolaria_${{ needs.version.outputs.version }}_macOS_Silicon.app.tar.gz"
          normalize_updater x86_64 "updater-x86_64/Tolaria_${{ needs.version.outputs.version }}_macOS_Intel.app.tar.gz"

      - name: Generate release notes
        run: |
          PREV_TAG=$(python3 <<'PY'
          import re
          import subprocess

          current_tag = '${{ needs.version.outputs.tag }}'
          pattern = re.compile(r'^alpha-v(\d{4})\.(\d{1,2})\.(\d{1,2})-alpha\.(\d+)$')

          output = subprocess.check_output(['git', 'tag', '--list', 'alpha-v*'], text=True).strip()
          tags = [line for line in output.splitlines() if line and line != current_tag]

          parsed_tags = []
          for tag in tags:
              match = pattern.fullmatch(tag)
              if not match:
                  continue
              year, month, day, sequence = map(int, match.groups())
              parsed_tags.append(((year, month, day, sequence), tag))

          print(max(parsed_tags)[1] if parsed_tags else '')
          PY
          )
          if [ -z "$PREV_TAG" ]; then
            NOTES=$(git log --oneline --no-merges -20)
          else
            NOTES=$(git log --oneline --no-merges "${PREV_TAG}..HEAD")
          fi
          {
            echo "## What's Changed (Alpha)"
            echo ""
            echo "$NOTES" | while IFS= read -r line; do echo "- $line"; done
            echo ""
            echo "---"
            echo "**Alpha build — updated on every push to \`main\`**"
            echo ""
            echo "**Includes macOS (Apple Silicon and Intel), Linux x64, and Windows x64 bundles**"
            echo ""
            echo "*Built from \`$(git rev-parse --short HEAD)\` on $(date -u +%Y-%m-%d)*"
          } > release_notes.md

      - name: Build alpha-latest.json
        run: |
          VERSION="${{ needs.version.outputs.version }}"
          TAG="${{ needs.version.outputs.tag }}"
          REPO="${GITHUB_REPOSITORY}"
          REPO_NAME="${REPO#*/}"
          PAGES_URL="https://refactoringhq.github.io/${REPO_NAME}/"

          find_required() {
            for pattern in "$@"; do
              set -- $pattern
              if [ -e "$1" ]; then
                printf '%s\n' "$1"
                return 0
              fi
            done
            return 1
          }

          ARM_SIG_FILE=$(find_required "updater-aarch64/*.app.tar.gz.sig")
          ARM_UPDATER_FILE="${ARM_SIG_FILE%.sig}"
          ARM_SIG=$(cat "$ARM_SIG_FILE")
          ARM_UPDATER=$(basename "$ARM_UPDATER_FILE")

          INTEL_SIG_FILE=$(find_required "updater-x86_64/*.app.tar.gz.sig")
          INTEL_UPDATER_FILE="${INTEL_SIG_FILE%.sig}"
          INTEL_SIG=$(cat "$INTEL_SIG_FILE")
          INTEL_UPDATER=$(basename "$INTEL_UPDATER_FILE")

          LINUX_SIG_FILE=$(find_required "linux-x86_64-bundles/*/*.AppImage.sig" "linux-x86_64-bundles/*/*.AppImage.tar.gz.sig" "linux-x86_64-bundles/*/*.deb.sig" "linux-x86_64-bundles/*.AppImage.sig" "linux-x86_64-bundles/*.AppImage.tar.gz.sig" "linux-x86_64-bundles/*.deb.sig")
          LINUX_UPDATER_FILE="${LINUX_SIG_FILE%.sig}"
          LINUX_SIG=$(cat "$LINUX_SIG_FILE")
          LINUX_UPDATER=$(basename "$LINUX_UPDATER_FILE")
          LINUX_DOWNLOAD=$(basename "$(find_required "linux-x86_64-bundles/*/*.AppImage" "linux-x86_64-bundles/*/*.deb" "linux-x86_64-bundles/*/*.AppImage.tar.gz" "linux-x86_64-bundles/*.AppImage" "linux-x86_64-bundles/*.deb" "linux-x86_64-bundles/*.AppImage.tar.gz")")

          WINDOWS_SIG_FILE=$(find_required "windows-x86_64-bundles/*/*-setup.exe.sig" "windows-x86_64-bundles/*/*.msi.sig" "windows-x86_64-bundles/*/*.nsis.zip.sig" "windows-x86_64-bundles/*/*.msi.zip.sig" "windows-x86_64-bundles/*-setup.exe.sig" "windows-x86_64-bundles/*.msi.sig" "windows-x86_64-bundles/*.nsis.zip.sig" "windows-x86_64-bundles/*.msi.zip.sig")
          WINDOWS_UPDATER_FILE="${WINDOWS_SIG_FILE%.sig}"
          WINDOWS_SIG=$(cat "$WINDOWS_SIG_FILE")
          WINDOWS_UPDATER=$(basename "$WINDOWS_UPDATER_FILE")
          WINDOWS_DOWNLOAD=$(basename "$(find_required "windows-x86_64-bundles/*/*-setup.exe" "windows-x86_64-bundles/*/*.msi" "windows-x86_64-bundles/*/*.nsis.zip" "windows-x86_64-bundles/*/*.msi.zip" "windows-x86_64-bundles/*-setup.exe" "windows-x86_64-bundles/*.msi" "windows-x86_64-bundles/*.nsis.zip" "windows-x86_64-bundles/*.msi.zip")")

          cat > alpha-latest.json << EOF
          {
            "version": "${VERSION}",
            "notes": "Alpha build. See ${PAGES_URL} for full release notes.",
            "pub_date": "$(date -u +%Y-%m-%dT%H:%M:%SZ)",
            "platforms": {
              "darwin-aarch64": {
                "signature": "${ARM_SIG}",
                "url": "https://github.com/${REPO}/releases/download/${TAG}/${ARM_UPDATER}",
                "download_url": "https://github.com/${REPO}/releases/download/${TAG}/${ARM_UPDATER}"
              },
              "darwin-x86_64": {
                "signature": "${INTEL_SIG}",
                "url": "https://github.com/${REPO}/releases/download/${TAG}/${INTEL_UPDATER}",
                "download_url": "https://github.com/${REPO}/releases/download/${TAG}/${INTEL_UPDATER}"
              },
              "linux-x86_64": {
                "signature": "${LINUX_SIG}",
                "url": "https://github.com/${REPO}/releases/download/${TAG}/${LINUX_UPDATER}",
                "download_url": "https://github.com/${REPO}/releases/download/${TAG}/${LINUX_DOWNLOAD}"
              },
              "windows-x86_64": {
                "signature": "${WINDOWS_SIG}",
                "url": "https://github.com/${REPO}/releases/download/${TAG}/${WINDOWS_UPDATER}",
                "download_url": "https://github.com/${REPO}/releases/download/${TAG}/${WINDOWS_DOWNLOAD}"
              }
            }
          }
          EOF
          echo "alpha-latest.json:"; cat alpha-latest.json

      - name: Publish GitHub Release
        uses: softprops/action-gh-release@3bb12739c298aeb8a4eeaf626c5b8d85266b0e65
        with:
          tag_name: ${{ needs.version.outputs.tag }}
          name: Tolaria ${{ needs.version.outputs.display_version }}
          body_path: release_notes.md
          draft: false
          prerelease: true
          files: |
            updater-aarch64/*.app.tar.gz
            updater-aarch64/*.app.tar.gz.sig
            updater-x86_64/*.app.tar.gz
            updater-x86_64/*.app.tar.gz.sig
            linux-x86_64-bundles/*.deb
            linux-x86_64-bundles/*.deb.sig
            linux-x86_64-bundles/*.rpm
            linux-x86_64-bundles/*.AppImage
            linux-x86_64-bundles/*.AppImage.sig
            linux-x86_64-bundles/*.AppImage.tar.gz
            linux-x86_64-bundles/*.AppImage.tar.gz.sig
            linux-x86_64-bundles/*/*.deb
            linux-x86_64-bundles/*/*.deb.sig
            linux-x86_64-bundles/*/*.rpm
            linux-x86_64-bundles/*/*.AppImage
            linux-x86_64-bundles/*/*.AppImage.sig
            linux-x86_64-bundles/*/*.AppImage.tar.gz
            linux-x86_64-bundles/*/*.AppImage.tar.gz.sig
            windows-x86_64-bundles/*.exe
            windows-x86_64-bundles/*.exe.sig
            windows-x86_64-bundles/*.msi
            windows-x86_64-bundles/*.msi.sig
            windows-x86_64-bundles/*.zip
            windows-x86_64-bundles/*.zip.sig
            windows-x86_64-bundles/*/*.exe
            windows-x86_64-bundles/*/*.exe.sig
            windows-x86_64-bundles/*/*.msi
            windows-x86_64-bundles/*/*.msi.sig
            windows-x86_64-bundles/*/*.zip
            windows-x86_64-bundles/*/*.zip.sig
            alpha-latest.json

  # ─────────────────────────────────────────────────────────────
  # Phase 4: Update GitHub Pages with docs, release history, and download assets
  # ─────────────────────────────────────────────────────────────
  pages:
    name: Update docs and release pages
    needs: [version, release]
    runs-on: ubuntu-latest
    permissions:
      contents: read
      pages: write
      id-token: write
    environment:
      name: github-pages
      url: ${{ steps.deployment.outputs.page_url }}
    concurrency:
      group: github-pages
      cancel-in-progress: false
    steps:
      - uses: actions/checkout@v4

      - name: Setup pnpm
        uses: pnpm/action-setup@f40ffcd9367d9f12939873eb1018b921a783ffaa
        with:
          version: 10

      - name: Setup Node.js
        uses: actions/setup-node@v4
        with:
          node-version: '22'
          cache: 'pnpm'

      - name: Setup Bun
        uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6
        with:
          bun-version: latest

      - name: Setup Pages
        uses: actions/configure-pages@v5

      - name: Install dependencies
        run: pnpm install --frozen-lockfile

      - name: Build docs and release pages
        env:
          GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
        run: |
          VITEPRESS_BASE="/" pnpm docs:build
          mkdir -p _site/alpha _site/stable _site/release-notes
          cp -R site/.vitepress/dist/. _site/
          if [ -d release-notes ]; then cp release-notes/*.md _site/release-notes/ 2>/dev/null || true; fi
          gh api -H "Accept: application/vnd.github.html+json" repos/${{ github.repository }}/releases --paginate > _site/releases.json
          STABLE_TAG=$(gh release list --repo ${{ github.repository }} --exclude-drafts --exclude-pre-releases --limit 1 --json tagName --jq '.[0].tagName // ""')

          gh release download --repo ${{ github.repository }} "${{ needs.version.outputs.tag }}" --pattern "alpha-latest.json" --output _site/alpha/latest.json || echo '{}' > _site/alpha/latest.json
          if [ -n "$STABLE_TAG" ]; then
            gh release download --repo ${{ github.repository }} "$STABLE_TAG" --pattern "stable-latest.json" --output _site/stable/latest.json || echo '{}' > _site/stable/latest.json
          else
            echo '{}' > _site/stable/latest.json
          fi
          bun scripts/build-release-download-page.ts --latest-json _site/stable/latest.json --releases-json _site/releases.json --output-file _site/stable/download/index.html
          bun scripts/build-release-history-page.ts --releases-json _site/releases.json --output-file _site/releases/index.html
          mkdir -p _site/download
          cp _site/stable/download/index.html _site/download/index.html

          cp _site/alpha/latest.json _site/latest.json
          cp _site/alpha/latest.json _site/latest-canary.json

      - name: Upload Pages artifact
        uses: actions/upload-pages-artifact@v4
        with:
          path: ./_site

      - name: Deploy to GitHub Pages
        id: deployment
        uses: actions/deploy-pages@v4
