name: Comprehensive Profiling Benchmarks

on:
  # Manual trigger for on-demand profiling runs
  # Allows developers to run full production mode or quick development mode with optional filters

  workflow_dispatch:
    inputs:
      mode:
        description: "Profiling mode"
        required: false
        default: "development"
        type: choice
        options:
          - "development"
          - "production"
      benchmark_filter:
        description: 'Benchmark filter (optional - e.g., "triangulation_scaling" or "memory_profiling")'
        required: false
        default: ""
        type: string

  # Monthly scheduled run for trend monitoring
  # Uses production mode for comprehensive long-term performance tracking

  schedule:
    # First Sunday of each month at 2 AM UTC (runs only when DOM is 1-7 and Sunday)
    - cron: "0 2 1-7 * 0"

  # Trigger on release tags for baseline profiling data
  # Uses development mode to keep runtime reasonable (~1-2 hours) while still capturing performance snapshots
  # Full production profiling can be triggered manually if needed for specific releases

  push:
    tags:
      - "v*.*.*"

permissions:
  contents: read

env:
  CARGO_TERM_COLOR: always
  RUST_BACKTRACE: 1
  RUST_TOOLCHAIN: 1.92.0

jobs:
  comprehensive-profiling:
    name: Comprehensive Performance Profiling
    runs-on: ubuntu-latest
    timeout-minutes: 360 # 6 hours timeout for comprehensive profiling

    steps:
      - name: Checkout code
        uses: actions/checkout@v6.0.2

      - name: Install Rust toolchain
        uses: actions-rust-lang/setup-rust-toolchain@2b1f5e9b395427c92ee4e3331786ca3c37afe2d7 # v1.16.0
        with:
          toolchain: ${{ env.RUST_TOOLCHAIN }}
          cache: false
          rustflags: ""

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

      - name: Install system dependencies
        run: |
          sudo apt-get update
          sudo apt-get install -y build-essential

      - name: Set profiling mode
        run: |
          INPUT_MODE="${{ github.event.inputs.mode || 'development' }}"
          # Profiling mode determination:
          # 1. Tag pushes (github.event_name == "push"): Always use development mode
          #    - Avoids CI timeout issues on release tag creation
          #    - Provides quick performance snapshot (~1-2 hours runtime)
          #    - Full profiling can be triggered manually if needed
          # 2. Manual dispatch with mode="development": Use development mode
          # 3. Scheduled runs and manual dispatch with mode="production": Use production mode
          #    - Full scale testing (10³-10⁶ points)
          #    - May take 4-6 hours
          if [[ "$INPUT_MODE" == "development" ]] || [[ "${{ github.event_name }}" == "push" ]]; then
            echo "PROFILING_DEV_MODE=1" >> "$GITHUB_ENV"
            echo "Running in development mode (reduced scale)"
            # Smaller, faster dev runs
            {
              echo "BENCH_SAMPLE_SIZE=30"
              echo "BENCH_WARMUP_SECS=2"
              echo "BENCH_MEASUREMENT_TIME=3"
              # Keep 4D reduced set (no BENCH_LARGE_SCALE)
            } >> "$GITHUB_ENV"
          else
            echo "Running in production mode (full scale)"
            # Heavier production runs
            {
              echo "BENCH_SAMPLE_SIZE=100"
              echo "BENCH_WARMUP_SECS=5"
              echo "BENCH_MEASUREMENT_TIME=10"
              echo "BENCH_LARGE_SCALE=1"  # enable 4D @ 10K
            } >> "$GITHUB_ENV"
          fi

      - name: Build profiling suite
        run: |
          # Build with the same perf profile used by `cargo bench --profile perf`
          # below so the bench step reuses these artifacts instead of rebuilding.
          cargo build --profile perf --bench profiling_suite

      - name: Run comprehensive profiling benchmarks
        run: |
          # Create results directory
          mkdir -p profiling-results

          # Set benchmark filter if provided. Keep the Criterion separator and
          # filter as separate argv tokens for cargo bench.
          BENCH_FILTER_VALUE="${{ github.event.inputs.benchmark_filter || '' }}"
          BENCH_FILTER_ARGS=()
          if [[ -n "$BENCH_FILTER_VALUE" ]]; then
            BENCH_FILTER_ARGS=("--" "$BENCH_FILTER_VALUE")
          fi

          # Run profiling (timing-focused)
          echo "Starting comprehensive profiling benchmarks..."

          # Determine if memory profiling is requested via filter
          # Match memory_profiling group specifically (not just any "memory" substring)
          MEMORY_FEATURES=""
          if [[ "$BENCH_FILTER_VALUE" =~ (^|[[:space:]])memory_profiling($|[[:space:]]) ]]; then
            MEMORY_FEATURES="--features count-allocations"
            echo "MEMORY_BENCHMARKS_RUN=true" >> "$GITHUB_ENV"
          fi

          # shellcheck disable=SC2086
          cargo bench --profile perf --bench profiling_suite $MEMORY_FEATURES "${BENCH_FILTER_ARGS[@]}" \
            2>&1 | tee profiling-results/profiling_output.log

      # Run dedicated memory profiling if not already executed above
      # Skip if: MEMORY_BENCHMARKS_RUN=true (memory benchmarks already ran with filter)
      # This prevents redundant memory profiling runs
      - name: Run profiling with memory allocation tracking (if not already done)
        if: env.MEMORY_BENCHMARKS_RUN != 'true'
        run: |
          echo "Running memory-specific profiling with allocation tracking..."
          cargo bench --profile perf --bench profiling_suite --features count-allocations -- memory_profiling \
            2>&1 | tee profiling-results/memory_profiling_detailed.log

      - name: Generate profiling summary
        run: |
          # Create a summary of the profiling run
          cat > profiling-results/profiling_summary.md << EOF
          # Comprehensive Profiling Results

          **Date**: $(date -u +"%Y-%m-%d %H:%M:%S UTC")
          **Commit**: ${{ github.sha }}
          **Branch**: ${{ github.ref_name }}
          **Trigger**: ${{ github.event_name }}
          **Mode**: ${{ env.PROFILING_DEV_MODE == '1' && 'Development' || 'Production' }}
          **Runner**: ${{ runner.os }}

          ## Configuration

          - **Profiling Mode**: ${{ env.PROFILING_DEV_MODE == '1' &&
            'Development (reduced scale for faster iteration)' ||
            'Production (full 10³-10⁶ point scale)' }}
          - **Memory Tracking**: ${{ contains(github.event.inputs.benchmark_filter || '', 'memory') &&
            'Enabled for memory benchmarks (count-allocations feature)' ||
            'Enabled only for memory-specific runs' }}
          - **Benchmark Filter**: ${{ (github.event.inputs.benchmark_filter || '') != '' &&
            github.event.inputs.benchmark_filter || 'All benchmarks' }}

          ## Benchmark Categories Run

          - **Triangulation Scaling**: Large-scale triangulation performance (2D-5D)
          - **Memory Profiling**: Memory allocation tracking and analysis
          - **Query Latency**: Circumsphere containment query performance
          - **Algorithmic Bottlenecks**: Boundary facets and convex hull operations

          ## Point Distributions Tested

          - **Random**: Uniform random distribution
          - **Grid**: Regular grid pattern (best-case scenario)
          - **Poisson Disk**: Spatially uniform distribution (realistic scenario)

          ## Files Generated

          - \`profiling_output.log\`: Complete benchmark output
          - \`memory_profiling_detailed.log\`: Detailed memory allocation analysis
          - \`criterion/\`: HTML reports and detailed timing data

          EOF

          # Publish summary to job summary UI for quick visibility
          cat profiling-results/profiling_summary.md >> "$GITHUB_STEP_SUMMARY"

      - name: Save profiling baseline for tagged release
        if: startsWith(github.ref, 'refs/tags/')
        run: |
          # For tagged releases, save as new baseline
          echo "Tagged release detected - this will serve as new profiling baseline"
          cp -r target/criterion profiling-results/criterion-baseline-${{ github.ref_name }}

      - name: Upload profiling results
        uses: actions/upload-artifact@v7
        with:
          name: profiling-results-${{ github.run_number }}
          path: profiling-results/
          retention-days: 30

      - name: Upload profiling baseline (for tagged releases)
        if: startsWith(github.ref, 'refs/tags/')
        uses: actions/upload-artifact@v7
        with:
          name: profiling-baseline-${{ github.ref_name }}
          path: |
            profiling-results/
            target/criterion/
          retention-days: 90 # Keep baselines longer

      - name: Log completion (if triggered manually)
        if: github.event_name == 'workflow_dispatch'
        run: |
          echo "Manual profiling run completed for commit ${{ github.sha }}"
          echo "Check the Actions tab for detailed results and artifacts"

  memory-stress-test:
    name: Memory Stress Testing
    runs-on: ubuntu-latest
    timeout-minutes: 90
    env:
      # CI-friendly configuration to ensure completion within time limits
      PROFILING_DEV_MODE: "1"
      BENCH_SAMPLE_SIZE: "10"
      BENCH_WARMUP_SECS: "1"
      BENCH_MEASUREMENT_TIME: "5"
      BENCH_PERCENTILE: "95"

    steps:
      - name: Checkout code
        uses: actions/checkout@v6.0.2

      - name: Install Rust toolchain
        uses: actions-rust-lang/setup-rust-toolchain@2b1f5e9b395427c92ee4e3331786ca3c37afe2d7 # v1.16.0
        with:
          toolchain: ${{ env.RUST_TOOLCHAIN }}
          cache: false
          rustflags: ""

      - name: Cache Cargo dependencies
        uses: actions/cache@v5
        with:
          path: |
            ~/.cargo/bin/
            ~/.cargo/registry/index/
            ~/.cargo/registry/cache/
            ~/.cargo/git/db/
            target/
          key: ${{ runner.os }}-cargo-memory-${{ hashFiles('**/Cargo.lock') }}

      - name: Run allocation API tests
        run: |
          echo "Running allocation API tests..."
          cargo test --test allocation_api --features count-allocations --verbose

      - name: Run memory scaling benchmarks
        env:
          PROFILING_DEV_MODE: "1"
          BENCH_SAMPLE_SIZE: "10"
          BENCH_WARMUP_SECS: "1"
          BENCH_MEASUREMENT_TIME: "5"
          BENCH_PERCENTILE: "95"
        run: |
          echo "Running memory profiling analysis with CI-friendly configuration..."
          echo "PROFILING_DEV_MODE=$PROFILING_DEV_MODE"
          echo "BENCH_SAMPLE_SIZE=$BENCH_SAMPLE_SIZE"
          echo "BENCH_WARMUP_SECS=$BENCH_WARMUP_SECS"
          echo "BENCH_MEASUREMENT_TIME=$BENCH_MEASUREMENT_TIME"
          cargo bench --profile perf --bench profiling_suite --features count-allocations -- memory_profiling
      - name: Upload memory test results
        uses: actions/upload-artifact@v7
        with:
          name: memory-stress-results-${{ github.run_number }}
          path: |
            target/criterion/
          retention-days: 14
