# =============================================================================
# REUSABLE WORKFLOW: Docker Build and Security Scanning
# PURPOSE: Build Docker images and scan for vulnerabilities with Trivy
# USAGE: Called by PR and main workflows for container validation
# OUTPUTS: Security findings uploaded to GitHub Security tab, Docker image artifact
# =============================================================================

name: Reusable Docker

on:
  workflow_call:
    inputs:
      platforms:
        description: 'Docker platforms to build (e.g., linux/amd64,linux/arm64)'
        type: string
        default: 'linux/amd64' # Single platform for PRs, multi for main
      push-image:
        description: 'Whether to push image to registry (always false for this workflow)'
        type: boolean
        default: false
      save-artifact:
        description: 'Whether to save Docker image as artifact for later use'
        type: boolean
        default: false
      artifact-name:
        description: 'Name for the Docker image artifact'
        type: string
        default: 'docker-image'
      version:
        description: 'Version tag for the Docker image'
        type: string
        default: ''
      image-name:
        description: 'Docker image name (without registry)'
        type: string
        default: 'sonarqube-mcp-server'
      tag_sha:
        description: 'SHA of the version tag for consistent naming'
        type: string
        default: ''
      build_artifact:
        description: 'Name of the pre-built TypeScript artifact to use'
        type: string
        default: ''
    outputs:
      image-digest:
        description: 'Docker image digest'
        value: ${{ jobs.docker.outputs.digest }}
      artifact-name:
        description: 'Name of the saved artifact'
        value: ${{ jobs.docker.outputs.artifact-name }}

# SECURITY: Required permissions for Docker operations
# Note: packages: write is only needed if pushing to GitHub Container Registry
# Calling workflows can omit it if not pushing images
permissions:
  contents: read # Read source code
  security-events: write # Upload Trivy scan results
  packages: write # Push Docker images to GitHub Container Registry

jobs:
  docker:
    runs-on: ubuntu-latest
    outputs:
      digest: ${{ steps.build.outputs.digest }}
      artifact-name: ${{ inputs.artifact-name }}
    steps:
      - name: Checkout code
        uses: actions/checkout@v4

      - name: Download build artifact
        # Download pre-built TypeScript if artifact name provided
        if: inputs.build_artifact != ''
        uses: actions/download-artifact@v4
        with:
          name: ${{ inputs.build_artifact }}

      # =============================================================================
      # DOCKER SETUP
      # Configure build environment for single or multi-platform builds
      # =============================================================================

      - name: Set up QEMU
        # Required for multi-platform builds (arm64)
        if: contains(inputs.platforms, 'arm64')
        uses: docker/setup-qemu-action@v3

      - name: Set up Docker Buildx
        # Advanced Docker builder with cache support
        uses: docker/setup-buildx-action@v3

      - name: Login to GitHub Container Registry
        # Login to GHCR for multi-platform builds that need to be pushed to registry
        # Single-platform builds for PRs don't need registry push
        if: inputs.save-artifact && contains(inputs.platforms, ',')
        uses: docker/login-action@v3
        with:
          registry: ghcr.io
          username: ${{ github.actor }}
          password: ${{ secrets.GITHUB_TOKEN }}

      # =============================================================================
      # DOCKER BUILD
      # Build image with layer caching for efficiency
      # =============================================================================

      - name: Generate Docker metadata
        id: meta
        uses: docker/metadata-action@v5
        with:
          # Use GHCR for multi-platform artifact builds, local name otherwise
          images: |
            ${{ (inputs.save-artifact && contains(inputs.platforms, ',')) && format('ghcr.io/{0}/{1}', github.repository_owner, inputs.image-name) || inputs.image-name }}
          tags: |
            type=raw,value=${{ inputs.version }},enable=${{ inputs.version != '' }}
            type=raw,value=latest,enable=${{ inputs.version != '' }}
            type=ref,event=pr
            type=sha,format=short

      - name: Determine build configuration
        # Set clear variables for build mode to improve readability
        id: build-config
        run: |
          # Determine if we're building for multiple platforms
          IS_MULTI_PLATFORM="false"
          if echo "${{ inputs.platforms }}" | grep -q ','; then
            IS_MULTI_PLATFORM="true"
          fi

          # For multi-platform builds with save-artifact, push to GHCR
          # For single-platform builds or PR builds, load locally or save to tar
          SAVE_ARTIFACT="${{ inputs.save-artifact }}"
          SHOULD_PUSH="false"
          CAN_LOAD="false"
          OUTPUT_TYPE=""

          if [ "$SAVE_ARTIFACT" = "true" ] && [ "$IS_MULTI_PLATFORM" = "true" ]; then
            # Multi-platform artifact build: push to GHCR
            SHOULD_PUSH="true"
            CAN_LOAD="false"
          elif [ "$SAVE_ARTIFACT" != "true" ] && [ "$IS_MULTI_PLATFORM" = "false" ]; then
            # Single-platform PR build: load locally
            CAN_LOAD="true"
          else
            # Single-platform artifact build: save to tar
            CAN_LOAD="false"
            SHA_TO_USE="${{ inputs.tag_sha || github.sha }}"
            OUTPUT_TYPE="type=docker,dest=${{ inputs.artifact-name }}-${SHA_TO_USE}.tar"
          fi

          {
            echo "is_multi_platform=$IS_MULTI_PLATFORM"
            echo "should_push=$SHOULD_PUSH"
            echo "can_load=$CAN_LOAD"
            echo "output_type=$OUTPUT_TYPE"
          } >> $GITHUB_OUTPUT

          echo "📋 Build configuration:"
          echo "  Multi-platform: $IS_MULTI_PLATFORM"
          echo "  Save artifact: $SAVE_ARTIFACT"
          echo "  Should push: $SHOULD_PUSH"
          echo "  Can load: $CAN_LOAD"
          echo "  Output type: $OUTPUT_TYPE"

      - name: Build Docker image
        id: build
        uses: docker/build-push-action@v6
        with:
          context: .
          platforms: ${{ inputs.platforms }}
          push: ${{ steps.build-config.outputs.should_push == 'true' }}
          load: ${{ steps.build-config.outputs.can_load == 'true' }}
          tags: ${{ steps.meta.outputs.tags }}
          labels: ${{ steps.meta.outputs.labels }}
          cache-from: type=gha # Use GitHub Actions cache
          cache-to: type=gha,mode=max # Maximum cache retention
          build-args: |
            VERSION=${{ inputs.version || github.sha }}
          outputs: ${{ steps.build-config.outputs.output_type }}

      # =============================================================================
      # SECURITY SCANNING
      # Trivy vulnerability scanning with configurable severity
      # =============================================================================

      - name: Determine Trivy scan configuration
        # Set clear variables for scan inputs to improve readability
        id: scan-config
        run: |
          # Determine scanning mode based on build configuration
          CAN_LOAD="${{ steps.build-config.outputs.can_load }}"
          if [ "$CAN_LOAD" = "true" ]; then
            # For loaded single-platform images, scan by image reference
            FIRST_TAG=$(echo "${{ steps.meta.outputs.tags }}" | head -n1)
            {
              echo "scan_input="
              echo "scan_image_ref=$FIRST_TAG"
            } >> $GITHUB_OUTPUT
            echo "Using image reference for scanning: $FIRST_TAG"
          else
            # For multi-platform or artifact builds, scan the tar file
            SHA_TO_USE="${{ inputs.tag_sha || github.sha }}"
            {
              echo "scan_input=${{ inputs.artifact-name }}-${SHA_TO_USE}.tar"
              echo "scan_image_ref="
            } >> $GITHUB_OUTPUT
            echo "Using tar file for scanning: ${{ inputs.artifact-name }}-${SHA_TO_USE}.tar"
          fi

      - name: Run Trivy vulnerability scanner
        # SECURITY: Scan image for vulnerabilities before any distribution
        # NOTE: Multi-platform OCI exports cannot be scanned from tar files
        # Scans for vulnerabilities, secrets, misconfigurations, and licenses
        # License findings are informational only (see LICENSES.md)
        if: steps.build-config.outputs.can_load == 'true' || !contains(inputs.platforms, ',')
        uses: aquasecurity/trivy-action@0.28.0
        with:
          input: ${{ steps.scan-config.outputs.scan_input }}
          image-ref: ${{ steps.scan-config.outputs.scan_image_ref }}
          exit-code: '1'
          format: 'sarif'
          hide-progress: false
          output: 'trivy-results.sarif'
          severity: 'HIGH,CRITICAL'
          scanners: 'vuln,secret,misconfig'
          trivyignores: '.trivyignore'
          version: 'latest'
        env:
          TRIVY_DEBUG: 'true'

      - name: Check Trivy results for vulnerabilities
        # Fail build if non-license security issues are found
        # License findings are informational and don't fail the build
        if: steps.build-config.outputs.can_load == 'true' || !contains(inputs.platforms, ',')
        run: |
          if [ -f trivy-results.sarif ]; then
            # Check for vulnerabilities, secrets, or misconfigurations (not licenses)
            SECURITY_ISSUES=$(jq -r '.runs[0].results[] | select(.ruleId | startswith("CVE-") or startswith("SECRET-") or startswith("CONFIG-")) | .level' trivy-results.sarif 2>/dev/null | wc -l || echo "0")
            if [ "$SECURITY_ISSUES" -gt 0 ]; then
              echo "::error::Found $SECURITY_ISSUES security issue(s) in container image"
              echo "Review the scan results in the Security tab after SARIF upload"
              exit 1
            fi
            echo "No security vulnerabilities found (license findings are informational)"
          fi

      - name: Upload Trivy results to GitHub Security
        # Always upload results, even if scan fails
        # Results viewable at: Security > Code scanning alerts
        if: always() && (steps.build-config.outputs.can_load == 'true' || !contains(inputs.platforms, ','))
        uses: github/codeql-action/upload-sarif@v3
        with:
          sarif_file: 'trivy-results.sarif'
          category: 'container-scan-${{ github.event_name }}'

      - name: Upload Trivy SARIF as artifact
        # Upload SARIF file as artifact for debugging and inspection
        if: always() && (steps.build-config.outputs.can_load == 'true' || !contains(inputs.platforms, ','))
        uses: actions/upload-artifact@v4
        with:
          name: trivy-${{ github.sha }}
          path: trivy-results.sarif
          retention-days: 7

      # =============================================================================
      # ARTIFACT STORAGE
      # Save Docker image tar files for single-platform builds
      # Multi-platform builds are pushed to GHCR instead
      # =============================================================================

      - name: Compress Docker image artifact
        # Compress the tar file to reduce storage costs
        # Only for single-platform builds (multi-platform builds pushed to GHCR)
        if: inputs.save-artifact && !contains(inputs.platforms, ',')
        run: |
          SHA_TO_USE="${{ inputs.tag_sha || github.sha }}"
          echo "Compressing Docker image artifact..."
          gzip -9 ${{ inputs.artifact-name }}-${SHA_TO_USE}.tar
          ls -lh ${{ inputs.artifact-name }}-${SHA_TO_USE}.tar.gz

      - name: Upload Docker image artifact
        # Store single-platform image tar for deterministic publishing
        # Multi-platform images are stored in GHCR registry
        if: inputs.save-artifact && !contains(inputs.platforms, ',')
        uses: actions/upload-artifact@v4
        with:
          name: ${{ inputs.artifact-name }}-${{ inputs.tag_sha || github.sha }}
          path: ${{ inputs.artifact-name }}-${{ inputs.tag_sha || github.sha }}.tar.gz
          retention-days: 7 # Keep for a week (enough for release cycle)
          compression-level: 0 # Already compressed with gzip

      # =============================================================================
      # SUPPLY CHAIN SECURITY
      # Generate attestations for build provenance (main builds only)
      # =============================================================================

      - name: Generate attestations for GHCR images
        # Creates cryptographic proof of build provenance for multi-platform images
        # Multi-platform images are stored in GHCR registry
        if: inputs.save-artifact && contains(inputs.platforms, ',') && inputs.version != '' && env.ACTIONS_ID_TOKEN_REQUEST_URL != ''
        uses: actions/attest-build-provenance@v2
        with:
          subject-name: ghcr.io/${{ github.repository_owner }}/${{ inputs.image-name }}
          subject-digest: ${{ steps.build.outputs.digest }}
          push-to-registry: true

      - name: Generate attestations for tar artifacts
        # Creates cryptographic proof of build provenance for single-platform tar files
        if: inputs.save-artifact && !contains(inputs.platforms, ',') && inputs.version != '' && env.ACTIONS_ID_TOKEN_REQUEST_URL != ''
        uses: actions/attest-build-provenance@v2
        with:
          subject-path: ${{ inputs.artifact-name }}-${{ inputs.tag_sha || github.sha }}.tar.gz
