# =============================================================================
# WORKFLOW: Main Branch Release Pipeline
# PURPOSE: Automate version management, releases, and security scanning on main
# TRIGGERS: Push to main branch (merges, direct commits)
# OUTPUTS: GitHub release with artifacts, NPM package, Docker image
# =============================================================================

name: Main

on:
  push:
    branches: [main]

# Prevent concurrent runs on the same ref to avoid race conditions during releases
# cancel-in-progress: false ensures releases complete even if new commits arrive
concurrency:
  group: ${{ github.workflow }}-${{ github.ref }}
  cancel-in-progress: false

# SECURITY: Required permissions for release automation
# contents: write - Create releases and tags
# id-token: write - Generate SLSA attestations for supply chain security
# attestations: write - Attach attestations to artifacts
# security-events: write - Upload security scan results
# actions: read - Access workflow runs and artifacts
# packages: write - Push Docker images to GitHub Container Registry
permissions:
  contents: write
  id-token: write
  attestations: write
  security-events: write
  actions: read
  packages: write

jobs:
  # =============================================================================
  # VALIDATION PHASE
  # Runs all quality checks in parallel to ensure code meets standards
  # =============================================================================

  validate:
    # Reusable workflow handles: audit, typecheck, lint, format, tests
    # FAILS IF: Any check fails, tests don't meet 80% coverage threshold
    uses: ./.github/workflows/reusable-validate.yml
    secrets:
      SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}

  # =============================================================================
  # SECURITY SCANNING PHASE
  # Parallel security scans to identify vulnerabilities before release
  # =============================================================================

  # Scans TypeScript/JavaScript for common security issues (XSS, SQL injection, etc.)
  security:
    uses: ./.github/workflows/reusable-security.yml

  # =============================================================================
  # UNIFIED BUILD PHASE
  # Single build job that creates artifacts to be reused throughout the workflow
  # =============================================================================

  build:
    runs-on: ubuntu-latest
    outputs:
      artifact-name: dist-${{ github.sha }}
      changed: ${{ steps.version.outputs.changed }}
      version: ${{ steps.version.outputs.version }}
      tag_sha: ${{ steps.tag.outputs.sha }}
    steps:
      - name: Checkout code
        uses: actions/checkout@v4
        with:
          fetch-depth: 0
          token: ${{ secrets.RELEASE_TOKEN }}

      - name: Install pnpm
        uses: pnpm/action-setup@v4
        with:
          version: 10.17.0
          run_install: false
          standalone: true

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

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

      - name: Version packages
        id: version
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
        run: |
          # Custom script validates changesets and determines version
          # FAILS IF: feat/fix commits exist without changesets
          # Outputs: changed=true/false, version=X.Y.Z
          node .github/scripts/version-and-release.js

      - name: Commit version changes
        if: steps.version.outputs.changed == 'true'
        run: |
          # Configure git with GitHub Actions bot identity
          git config --local user.email "${{ github.actor_id }}+${{ github.actor }}@users.noreply.github.com"
          git config --local user.name "${{ github.actor }}"

          # Stage version-related changes
          git add package.json CHANGELOG.md .changeset

          # Commit with [skip actions] to prevent workflow recursion
          git commit -m "chore(release): v${{ steps.version.outputs.version }} [skip actions]"

          # Push changes to origin
          git push origin main

          echo "✅ Version changes committed and pushed"

      - name: Create and push tag
        # Create tag BEFORE building artifacts so they're associated with the tag
        id: tag
        if: steps.version.outputs.changed == 'true'
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
        run: |
          VERSION="${{ steps.version.outputs.version }}"

          # Configure git
          git config --local user.email "${{ github.actor_id }}+${{ github.actor }}@users.noreply.github.com"
          git config --local user.name "${{ github.actor }}"

          # Create annotated tag
          git tag -a "v${VERSION}" -m "Release v${VERSION}"

          # Push tag to origin
          git push origin "v${VERSION}"

          # Get the tag SHA for artifact naming
          TAG_SHA=$(git rev-list -n 1 "v${VERSION}")
          echo "sha=${TAG_SHA}" >> $GITHUB_OUTPUT
          echo "📌 Tag SHA for artifacts: ${TAG_SHA}"

      - name: Build TypeScript
        if: steps.version.outputs.changed == 'true'
        run: |
          pnpm build
          echo "✅ Built TypeScript once for entire workflow"

      - name: Generate artifact manifest
        if: steps.version.outputs.changed == 'true'
        run: |
          # Create a manifest of what's been built
          cat > build-manifest.json <<EOF
          {
              "build_sha": "${{ github.sha }}",
              "build_time": "$(date -u +"%Y-%m-%dT%H:%M:%SZ")",
              "node_version": "$(node --version)",
              "pnpm_version": "$(pnpm --version)",
              "typescript_version": "$(pnpm list typescript --json | jq -r '.dependencies.typescript.version')",
              "files": $(find dist -type f -name "*.js" | jq -R . | jq -s .)
          }
          EOF
          echo "📋 Generated build manifest with $(find dist -type f -name "*.js" | wc -l) JavaScript files"

      - name: Upload build artifact
        if: steps.version.outputs.changed == 'true'
        uses: actions/upload-artifact@v4
        with:
          name: dist-${{ github.sha }}
          path: |
            dist/
            package.json
            pnpm-lock.yaml
            build-manifest.json
          retention-days: 1 # Only needed for this workflow run

  # =============================================================================
  # PREPARE RELEASE ASSETS PHASE
  # Centralized job for preparing all release artifacts (Docker, binaries, etc.)
  # =============================================================================

  docker:
    name: Build Docker Image
    needs: [validate, security, build]
    if: vars.ENABLE_DOCKER_RELEASE == 'true' && needs.build.outputs.changed == 'true'
    uses: ./.github/workflows/reusable-docker.yml
    with:
      platforms: 'linux/amd64,linux/arm64'
      save-artifact: true
      artifact-name: 'docker-image-${{ needs.build.outputs.version }}'
      image-name: 'sonarqube-mcp-server'
      version: ${{ needs.build.outputs.version }}
      tag_sha: ${{ github.sha }}
      build_artifact: ${{ needs.build.outputs.artifact-name }}

  npm:
    name: Prepare NPM Package
    needs: [validate, security, build]
    if: vars.ENABLE_NPM_RELEASE == 'true' && needs.build.outputs.changed == 'true'
    runs-on: ubuntu-latest
    outputs:
      built: ${{ steps.pack.outputs.built }}
      artifact_name: ${{ steps.pack.outputs.artifact_name }}
      tarball_name: ${{ steps.pack.outputs.tarball_name }}
    steps:
      - name: Checkout code
        uses: actions/checkout@v4
        with:
          fetch-depth: 0

      - name: Download build artifact
        uses: actions/download-artifact@v4
        with:
          name: ${{ needs.build.outputs.artifact-name }}

      - name: Install pnpm
        uses: pnpm/action-setup@v4
        with:
          version: 10.17.0
          run_install: false
          standalone: true

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

      - name: Install dependencies
        # Install all dependencies for packaging (dev and prod)
        run: pnpm install --frozen-lockfile

      - name: Create NPM package
        id: pack
        run: |
          # Create the NPM package tarball
          # Use tail -1 to get just the filename, as npm pack may output additional text
          NPM_PACKAGE=$(npm pack 2>/dev/null | tail -1)
          echo "📦 Created NPM package: $NPM_PACKAGE"

          # Generate metadata using github.sha for consistent naming with publish workflow
          ARTIFACT_NAME="npm-package-${{ needs.build.outputs.version }}-${{ github.sha }}"
          {
            echo "artifact_name=$ARTIFACT_NAME"
            echo "tarball_name=$NPM_PACKAGE"
            echo "built=true"
          } >> $GITHUB_OUTPUT

          # Create manifest of included files for verification
          npm pack --dry-run --json 2>/dev/null | jq -r '.[0].files[].path' > npm-package-manifest.txt
          echo "📋 Package contains $(wc -l < npm-package-manifest.txt) files"

      - name: Upload NPM package artifact
        uses: actions/upload-artifact@v4
        with:
          name: npm-package-${{ needs.build.outputs.version }}-${{ github.sha }}
          path: |
            *.tgz
            npm-package-manifest.txt
          retention-days: 7

      - name: Generate attestations for NPM package
        uses: actions/attest-build-provenance@v2
        with:
          subject-path: '*.tgz'

  # =============================================================================
  # GITHUB RELEASE CREATION PHASE
  # Creates GitHub release as the final step after version is committed
  # =============================================================================

  create-release:
    name: Create GitHub Release
    needs: [build, docker, npm]
    # Run if build succeeded AND docker/npm either succeeded or were skipped
    if: |
      needs.build.outputs.changed == 'true' &&
      !cancelled() &&
      (needs.docker.result == 'success' || needs.docker.result == 'skipped') &&
      (needs.npm.result == 'success' || needs.npm.result == 'skipped')
    runs-on: ubuntu-latest
    outputs:
      released: ${{ steps.release.outputs.released }}
      version: ${{ needs.build.outputs.version }}
    steps:
      - name: Checkout code
        uses: actions/checkout@v4
        with:
          # Checkout the newly created tag
          ref: v${{ needs.build.outputs.version }}

      - name: Download build artifact
        uses: actions/download-artifact@v4
        with:
          name: ${{ needs.build.outputs.artifact-name }}

      - name: Install pnpm
        uses: pnpm/action-setup@v4
        with:
          version: 10.17.0
          run_install: false
          standalone: true

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

      - name: Install dependencies
        # Only production dependencies needed for SBOM generation
        # Skip scripts to avoid running husky (dev dependency)
        run: pnpm install --prod --frozen-lockfile --ignore-scripts

      - name: Generate SBOM
        run: pnpm sbom

      - name: Create release artifacts
        run: |
          VERSION="${{ needs.build.outputs.version }}"
          TAG_SHA="${{ needs.build.outputs.tag_sha }}"
          tar -czf dist-${VERSION}-${TAG_SHA:0:7}.tar.gz dist/
          zip -r dist-${VERSION}-${TAG_SHA:0:7}.zip dist/

      - name: Extract release notes
        run: |
          VERSION="${{ needs.build.outputs.version }}"
          awk -v version="## $VERSION" '
            $0 ~ version { flag=1; next }
            /^## [0-9]/ && flag { exit }
            flag { print }
          ' CHANGELOG.md > release-notes.md

          if [ ! -s release-notes.md ]; then
            echo "Release v$VERSION" > release-notes.md
          fi

      # =============================================================================
      # SUPPLY CHAIN SECURITY
      # Generate attestations BEFORE creating release to avoid race condition
      # This ensures the Main workflow is complete before triggering Publish workflow
      # =============================================================================

      - name: Generate attestations
        # Generate SLSA provenance attestations for supply chain security
        # Requires id-token: write permission
        uses: actions/attest-build-provenance@v2
        with:
          subject-path: |
            dist/**/*.js
            sbom.cdx.json
            dist-*-*.tar.gz
            dist-*-*.zip

      - name: Create GitHub Release
        uses: softprops/action-gh-release@v2
        with:
          tag_name: v${{ needs.build.outputs.version }}
          name: v${{ needs.build.outputs.version }}
          body_path: release-notes.md
          draft: false
          prerelease: false
          make_latest: true
          files: |
            sbom.cdx.json
            dist-${{ needs.build.outputs.version }}-*.tar.gz
            dist-${{ needs.build.outputs.version }}-*.zip
        env:
          GITHUB_TOKEN: ${{ secrets.RELEASE_TOKEN }}

      - name: Set release output
        id: release
        run: |
          echo "released=true" >> $GITHUB_OUTPUT
          echo "✅ Released version ${{ needs.build.outputs.version }}"
