name: Release

on:
  release:
    types: [created]
  workflow_dispatch:
    inputs:
      version:
        description: "Version tag (leave empty to use version from build.zig.zon)"
        required: false
        type: string
      tag:
        description: "npm dist-tag (e.g. dev, latest). Defaults to dev"
        required: false
        type: string
        default: dev
      target:
        description: "Release target"
        required: true
        type: choice
        default: npm
        options:
          - npm
          - gh

jobs:
  resolve-version:
    name: Resolve Version
    runs-on: ubuntu-latest
    outputs:
      version: ${{ steps.resolve.outputs.version }}
      dist-tag: ${{ steps.resolve.outputs.dist_tag }}
    steps:
      - name: Resolve and validate version
        id: resolve
        run: |
          RAW_VERSION="${{ inputs.version || github.event.release.tag_name }}"
          if [ -z "$RAW_VERSION" ]; then
            echo "No version provided, will use version from build.zig.zon"
            exit 0
          fi
          # Trim v prefix
          VERSION="${RAW_VERSION#v}"
          # Validate semver (with optional pre-release and build metadata)
          if ! echo "$VERSION" | grep -qE '^[0-9]+\.[0-9]+\.[0-9]+(-[a-zA-Z0-9.]+)?(\+[a-zA-Z0-9.]+)?$'; then
            echo "::error::Invalid semantic version: '$RAW_VERSION' (resolved to '$VERSION')"
            exit 1
          fi
          echo "version=$VERSION" >> "$GITHUB_OUTPUT"

          # Resolve npm dist-tag: use input if provided, detect from version, default to latest
          INPUT_TAG="${{ inputs.tag }}"
          if [ -n "$INPUT_TAG" ]; then
            DIST_TAG="$INPUT_TAG"
          elif echo "$VERSION" | grep -qE '^[0-9]+\.[0-9]+\.[0-9]+-'; then
            # Prerelease version: extract identifier (e.g. "dev" from "0.1.0-dev.123")
            DIST_TAG=$(echo "$VERSION" | sed -E 's/^[0-9]+\.[0-9]+\.[0-9]+-([a-zA-Z]+).*/\1/')
          else
            DIST_TAG="latest"
          fi
          echo "dist_tag=$DIST_TAG" >> "$GITHUB_OUTPUT"
          echo "Resolved dist-tag: $DIST_TAG"

  build-release:
    name: Build Release Binaries
    needs: resolve-version
    runs-on: ubuntu-latest
    strategy:
      matrix:
        target:
          - name: linux-x64
            os: linux
            arch: x86_64
            ext: ""
          - name: linux-aarch64
            os: linux
            arch: aarch64
            ext: ""
          - name: macos-x64
            os: macos
            arch: x86_64
            ext: ""
          - name: macos-aarch64
            os: macos
            arch: aarch64
            ext: ""
          - name: windows-x64
            os: windows
            arch: x86_64
            ext: ".exe"
          - name: windows-aarch64
            os: windows
            arch: aarch64
            ext: ".exe"

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

      - name: Setup Zig
        uses: mlugg/setup-zig@v2
        with:
          version: 0.15.2

      - name: Cache Zig build artifacts
        uses: actions/cache@v4
        with:
          path: |
            ~/.cache/zig
            zig-out
          key: ${{ runner.os }}-zig-release-${{ hashFiles('**/build.zig', '**/build.zig.zon', '**/*.zig') }}
          restore-keys: |
            ${{ runner.os }}-zig-release-

      - name: Set version
        if: needs.resolve-version.outputs.version != ''
        run: bash tools/version "${{ needs.resolve-version.outputs.version }}"

      - name: Sync Template Files
        run: bash tools/synctemplate

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

      - name: Prepare artifact
        shell: bash
        run: |
          mkdir -p dist
          BINARY_NAME="zx-${{ matrix.target.name }}${{ matrix.target.ext }}"
          if [ "${{ matrix.target.os }}" = "windows" ]; then
            # For Windows, create a zip file
            cd zig-out/bin/release
            zip ../../../dist/zx-${{ matrix.target.name }}.zip "$BINARY_NAME"
            cd ../../..
          else
            # For Unix-like systems, create a tar.gz file
            cd zig-out/bin/release
            tar -czf ../../../dist/zx-${{ matrix.target.name }}.tar.gz "$BINARY_NAME"
            cd ../../..
          fi

      - name: Upload artifact
        uses: actions/upload-artifact@v4
        with:
          name: zx-${{ matrix.target.name }}
          path: dist/zx-${{ matrix.target.name }}.*
          retention-days: 30

  create-release:
    name: Create Release Assets
    runs-on: ubuntu-latest
    needs: build-release
    if: github.event_name == 'release' || (github.event_name == 'workflow_dispatch' && inputs.target == 'gh')
    permissions:
      contents: write

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

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

      - name: Flatten artifact structure
        shell: bash
        run: |
          cd dist
          # Move files from subdirectories to dist root
          for dir in */; do
            if [ -d "$dir" ]; then
              mv "$dir"* . 2>/dev/null || true
              rmdir "$dir" 2>/dev/null || true
            fi
          done

      - name: Upload to release
        uses: softprops/action-gh-release@v1
        with:
          files: dist/*
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

  smoke-test:
    name: Smoke Test (${{ matrix.os }})
    runs-on: ${{ matrix.os }}
    needs: [resolve-version, build-release]
    if: github.event_name == 'release' || (github.event_name == 'workflow_dispatch' && inputs.target == 'npm')
    strategy:
      matrix:
        os: [ubuntu-latest, windows-latest, macos-latest]
    steps:
      - uses: actions/checkout@v4

      - uses: actions/setup-node@v4
        with:
          node-version: 24

      - uses: oven-sh/setup-bun@v2

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

      - name: Place binaries in zig-out/bin/release
        shell: bash
        run: |
          mkdir -p zig-out/bin/release
          for dir in dist/*/; do
            for file in "$dir"*; do
              case "$file" in
                *.tar.gz) tar -xzf "$file" -C zig-out/bin/release ;;
                *.zip)    unzip -o "$file" -d zig-out/bin/release ;;
              esac
            done
          done

      - name: Prepare @ziex packages
        shell: bash
        run: |
          cd pkg/@ziex
          bash prepare.sh ${{ needs.resolve-version.outputs.version }}

      - name: Start Verdaccio
        shell: bash
        env:
          CI: true
        run: |
          npm install -g verdaccio
          verdaccio --config pkg/@ziex/verdaccio.yaml --listen 4873 &
          # Wait until the registry responds (up to 30s)
          for i in $(seq 1 60); do
            if curl -sf http://localhost:4873 > /dev/null 2>&1; then
              echo "Verdaccio is up."
              break
            fi
            sleep 0.5
          done

      - name: Run smoke tests
        shell: bash
        run: bash pkg/check.sh ${{ needs.resolve-version.outputs.version }}

  publish-npm:
    name: Publish to NPM
    runs-on: ubuntu-latest
    needs: [resolve-version, build-release, smoke-test]
    if: github.event_name == 'release' || (github.event_name == 'workflow_dispatch' && inputs.target == 'npm')
    permissions:
      contents: read
      id-token: write
    steps:
      - uses: actions/checkout@v4

      - uses: actions/setup-node@v4
        with:
          node-version: 24
          registry-url: https://registry.npmjs.org

      - uses: oven-sh/setup-bun@v2

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

      - name: Place binaries in zig-out/bin/release
        shell: bash
        run: |
          mkdir -p zig-out/bin/release
          for dir in dist/*/; do
            for file in "$dir"*; do
              case "$file" in
                *.tar.gz) tar -xzf "$file" -C zig-out/bin/release ;;
                *.zip)    unzip -o "$file" -d zig-out/bin/release ;;
              esac
            done
          done

      - name: Prepare @ziex packages
        run: |
          cd pkg/@ziex
          bash prepare.sh ${{ needs.resolve-version.outputs.version }}

      - name: Publish @ziex/cli* to NPM
        env:
          NODE_AUTH_TOKEN: ""
        run: |
          cd pkg/@ziex
          npm publish --workspaces --access public --provenance --tag ${{ needs.resolve-version.outputs.dist-tag }}

      - name: Build and publish ziex to NPM
        env:
          NODE_AUTH_TOKEN: ""
        run: |
          cd pkg/ziex
          bun install
          bun run build
          cd dist
          npm publish --access public --provenance --tag ${{ needs.resolve-version.outputs.dist-tag }}

  sync-templates:
    name: Sync & Release Templates
    runs-on: ubuntu-latest
    needs: create-release
    if: github.event_name == 'release'
    permissions:
      contents: write
    steps:
      - uses: actions/checkout@v4
        with:
          fetch-depth: 0

      - name: Setup Zig
        uses: mlugg/setup-zig@v2
        with:
          version: 0.15.2

      - name: Setup Git
        run: |
          git config --global user.name "github-actions[bot]"
          git config --global user.email "github-actions[bot]@users.noreply.github.com"

      - name: Sync and release all templates
        env:
          GITHUB_TOKEN: ${{ secrets.SYNC_TOKEN || secrets.GITHUB_TOKEN }}
        run: |
          OWNER=$(echo $GITHUB_REPOSITORY | cut -d'/' -f1)
          TAG="${{ github.event.release.tag_name }}"

          # Update _base build.zig.zon to point to the release tag
          cd templates/_base
          zig fetch --save "git+https://github.com/ziex-dev/ziex#${TAG}"
          cd ../..

          ALL_TEMPLATES=$(ls -d templates/*/ | grep -v "_base" | xargs -n 1 basename)

          for TEMPLATE in $ALL_TEMPLATES; do
            REPO_NAME="template-${TEMPLATE}"
            TEMP_DIR=$(mktemp -d)

            echo "Syncing and releasing ${TEMPLATE} to ${REPO_NAME} at ${TAG}"

            # Populate template with base files
            bash ./tools/template.sh populate "${TEMPLATE}"

            # Copy populated template to temp dir
            cp -a "templates/${TEMPLATE}/." "$TEMP_DIR/"
            [ ! -f "$TEMP_DIR/LICENSE" ] && [ -f LICENSE ] && cp LICENSE "$TEMP_DIR/"

            cd "$TEMP_DIR"

            # Initialize and push to main
            git init -b main
            git add .
            git commit -m "release: ${TAG} from ziex-dev/ziex"

            REMOTE_URL="https://x-access-token:${GITHUB_TOKEN}@github.com/$OWNER/$REPO_NAME.git"
            git remote add origin "$REMOTE_URL"
            git push -u origin main --force

            # Create the release tag
            git tag "$TAG"
            git push origin "$TAG" --force

            # Create a GitHub release for the template repo
            gh release create "$TAG" \
              --repo "$OWNER/$REPO_NAME" \
              --title "$TAG" \
              --notes "Release ${TAG} — synced from [ziex-dev/ziex@${TAG}](https://github.com/ziex-dev/ziex/releases/tag/${TAG})" \
              || echo "Release $TAG may already exist for $REPO_NAME, skipping..."

            cd -
            rm -rf "$TEMP_DIR"
          done

