name: Mac CI

on:
  workflow_call:

permissions:
  contents: read
  pull-requests: write
  issues: write

# Single job for efficient execution on shared runner
jobs:
  build-lint-test:
    name: Build, Lint, and Test macOS
    runs-on: [self-hosted, macOS, ARM64]
    timeout-minutes: 40
    env:
      GITHUB_REPO_NAME: ${{ github.repository }}
    
    steps:
    - name: Clean workspace
      run: |
        # Clean workspace for self-hosted runner
        # Clean workspace but preserve .git directory
        find . -maxdepth 1 -name '.*' -not -name '.git' -not -name '.' -not -name '..' -exec rm -rf {} + || true
        rm -rf * || true
        
    - name: Checkout code
      uses: actions/checkout@v4
    
    - name: Verify Xcode
      run: |
        xcodebuild -version
        swift --version
    
    # Node.js/pnpm not needed - web artifacts are downloaded
    
    - name: Cache Homebrew packages
      uses: actions/cache@v4
      continue-on-error: true
      with:
        path: |
          ~/Library/Caches/Homebrew
          /opt/homebrew/Cellar/swiftlint
          /opt/homebrew/Cellar/swiftformat
          /opt/homebrew/Cellar/xcbeautify
        key: ${{ runner.os }}-brew-${{ hashFiles('.github/workflows/mac.yml') }}
        restore-keys: |
          ${{ runner.os }}-brew-
    
    - name: Cache Swift packages
      uses: actions/cache@v4
      continue-on-error: true
      with:
        path: |
          ~/.swiftpm
        key: ${{ runner.os }}-spm-${{ hashFiles('mac/Package.resolved') }}
        restore-keys: |
          ${{ runner.os }}-spm-
    
    - name: Cache Xcode derived data
      uses: actions/cache@v4
      continue-on-error: true
      with:
        path: |
          ~/Library/Developer/Xcode/DerivedData/**/Build/Products
          ~/Library/Developer/Xcode/DerivedData/**/Build/Intermediates.noindex
          ~/Library/Developer/Xcode/DerivedData/**/SourcePackages
        key: ${{ runner.os }}-xcode-build-${{ hashFiles('mac/**/*.swift', 'mac/**/*.h', 'mac/**/*.m') }}
        restore-keys: |
          ${{ runner.os }}-xcode-build-
    
    - name: Install all tools
      shell: bash
      run: |
        # Skip Homebrew update for faster CI
        export HOMEBREW_NO_AUTO_UPDATE=1
        
        # Retry logic for brew commands to handle concurrent access
        MAX_ATTEMPTS=5
        WAIT_TIME=5
        
        for attempt in $(seq 1 $MAX_ATTEMPTS); do
          echo "Attempting Homebrew operations (attempt $attempt/$MAX_ATTEMPTS)"
          
          # Check if another brew process is running
          if pgrep -x "brew" > /dev/null; then
            echo "Another brew process detected, waiting ${WAIT_TIME}s..."
            sleep $WAIT_TIME
            WAIT_TIME=$((WAIT_TIME * 2))  # Exponential backoff
            continue
          fi
          
          # Install tools without updating Homebrew
          # brew install automatically upgrades if already installed
          if brew install swiftlint swiftformat xcbeautify; then
            echo "Successfully installed/upgraded all tools"
            break
          else
            if [ $attempt -eq $MAX_ATTEMPTS ]; then
              echo "Failed to install tools after $MAX_ATTEMPTS attempts"
              exit 1
            fi
            echo "Command failed, waiting ${WAIT_TIME}s before retry..."
            sleep $WAIT_TIME
            WAIT_TIME=$((WAIT_TIME * 2))  # Exponential backoff
          fi
        done
        
        # Show versions
        echo "SwiftLint: $(swiftlint --version || echo 'not found')"
        echo "SwiftFormat: $(swiftformat --version || echo 'not found')"
        echo "xcbeautify: $(xcbeautify --version || echo 'not found')"
        echo "jq: $(which jq || echo 'not found')"
    
    # No web artifact caching - Mac build will handle web build directly
    
    - name: Resolve Dependencies (once)
      env:
        CI: "true"  # Ensure CI environment variable is set
      run: |
        echo "Resolving Swift package dependencies..."
        # Workspace is at root level
        xcodebuild -resolvePackageDependencies -workspace VibeTunnel.xcworkspace -scheme VibeTunnel-Mac || echo "Dependency resolution completed"
        
        # Debug: List available schemes
        echo "=== Available schemes ==="
        xcodebuild -list -workspace VibeTunnel.xcworkspace | grep -A 20 "Schemes:" || true
    
    # BUILD PHASE
    - name: Build Debug
      timeout-minutes: 10
      id: build
      env:
        CI: "true"  # Ensure CI environment variable is set for build scripts
      run: |
        # Always use Debug for now to match test expectations
        BUILD_CONFIG="Debug"
        
        set -o pipefail && xcodebuild build \
          -workspace VibeTunnel.xcworkspace \
          -scheme VibeTunnel-Mac \
          -configuration $BUILD_CONFIG \
          -destination "platform=macOS" \
          -showBuildTimingSummary \
          CODE_SIGN_IDENTITY="" \
          CODE_SIGNING_REQUIRED=NO \
          CODE_SIGNING_ALLOWED=NO \
          CODE_SIGN_ENTITLEMENTS="" \
          ENABLE_HARDENED_RUNTIME=NO \
          PROVISIONING_PROFILE_SPECIFIER="" \
          DEVELOPMENT_TEAM="" \
          COMPILER_INDEX_STORE_ENABLE=NO || {
          echo "::error::Build failed"
          exit 1
        }
    
    # LINT PHASE (after build to avoid conflicts)
    - name: Run SwiftFormat (check mode)
      id: swiftformat
      continue-on-error: true
      run: |
        cd mac
        swiftformat . --lint 2>&1 | tee ../swiftformat-output.txt
        echo "result=${PIPESTATUS[0]}" >> $GITHUB_OUTPUT
    
    - name: Run SwiftLint
      id: swiftlint
      continue-on-error: true
      run: |
        cd mac
        swiftlint 2>&1 | tee ../swiftlint-output.txt
        echo "result=${PIPESTATUS[0]}" >> $GITHUB_OUTPUT
    
    # TEST PHASE
    - name: Run tests with coverage
      id: test-coverage
      timeout-minutes: 20  # Increased from 15 for CI stability
      env:
        RUN_SLOW_TESTS: "false"  # Skip slow tests in CI by default
        RUN_FLAKY_TESTS: "false"  # Skip flaky tests in CI by default
      run: |
        
        # Use xcodebuild test for workspace testing
        # Only enable coverage on main branch
        if [[ "${{ github.event_name }}" == "push" && "${{ github.ref }}" == "refs/heads/main" ]]; then
          ENABLE_COVERAGE="YES"
        else
          ENABLE_COVERAGE="NO"
        fi
        
        # Always use Debug for tests
        TEST_CONFIG="Debug"
        
        set -o pipefail && \
        xcodebuild test \
          -workspace VibeTunnel.xcworkspace \
          -scheme VibeTunnel-Mac \
          -configuration $TEST_CONFIG \
          -destination "platform=macOS" \
          -enableCodeCoverage $ENABLE_COVERAGE \
          -resultBundlePath TestResults.xcresult \
          CODE_SIGN_IDENTITY="" \
          CODE_SIGNING_REQUIRED=NO \
          CODE_SIGNING_ALLOWED=NO \
          COMPILER_INDEX_STORE_ENABLE=NO || {
          echo "::error::Tests failed"
          echo "result=1" >> $GITHUB_OUTPUT
          # Try to get more detailed error information
          echo "=== Attempting to get test failure details ==="
          xcrun xcresulttool get --path TestResults.xcresult --format json 2>/dev/null | jq '.issues._values[]? | select(.severity == "error")' 2>/dev/null || true
          exit 1
        }
        echo "result=0" >> $GITHUB_OUTPUT
    
    
    # COVERAGE EXTRACTION
    - name: Debug coverage files
      if: always()
      run: |
        echo "=== Checking TestResults.xcresult ==="
        if [ -d TestResults.xcresult ]; then
          echo "TestResults.xcresult exists"
          ls -la TestResults.xcresult
          
          echo "\n=== Checking for coverage data in xcresult ==="
          xcrun xcresulttool get --path TestResults.xcresult --format json 2>/dev/null | jq '.actions._values[].actionResult.coverage' 2>/dev/null | head -20 || echo "No coverage data found in xcresult"
          
          echo "\n=== Attempting direct coverage view ==="
          xcrun xccov view --report TestResults.xcresult 2>&1 | head -20 || echo "Direct coverage view failed"
        else
          echo "TestResults.xcresult not found"
          ls -la
        fi
    
    - name: Extract coverage summary
      if: always()
      id: coverage
      run: |
        if [ -d TestResults.xcresult ]; then
          # Try multiple extraction methods
          echo "=== Method 1: Standard extraction ==="
          COVERAGE_PCT=$(xcrun xccov view --report TestResults.xcresult --json 2>/dev/null | jq -r '.lineCoverage // 0' | awk '{printf "%.1f", $1 * 100}') || COVERAGE_PCT="0"
          
          if [ "$COVERAGE_PCT" = "0" ] || [ -z "$COVERAGE_PCT" ]; then
            echo "Method 1 failed, trying alternative methods"
            
            echo "\n=== Method 2: Without --json flag ==="
            COVERAGE_LINE=$(xcrun xccov view --report TestResults.xcresult 2>&1 | grep -E "^[0-9]+\.[0-9]+%" | head -1) || true
            if [ -n "$COVERAGE_LINE" ]; then
              COVERAGE_PCT=$(echo "$COVERAGE_LINE" | sed 's/%.*//g')
              echo "Extracted coverage from text output: $COVERAGE_PCT%"
            fi
          fi
          
          if [ "$COVERAGE_PCT" = "0" ] || [ -z "$COVERAGE_PCT" ]; then
            echo "\n=== Method 3: Check if tests ran ==="
            if xcrun xcresulttool get --path TestResults.xcresult --format json 2>/dev/null | grep -q '"testsCount"' && \
               xcrun xcresulttool get --path TestResults.xcresult --format json 2>/dev/null | jq -e '.metrics.testsCount > 0' >/dev/null 2>&1; then
              echo "Tests ran but coverage extraction failed"
              # Set to 0.1% to indicate tests ran but coverage couldn't be extracted
              COVERAGE_PCT="0.1"
            else
              echo "No tests were found or run"
              COVERAGE_PCT="0"
            fi
          fi
          
          # Create minimal summary JSON
          echo "{\"coverage\": \"$COVERAGE_PCT\"}" > coverage-summary.json
          
          echo "Final Coverage: ${COVERAGE_PCT}%"
          
          # Any coverage above 0% is acceptable for now
          if (( $(echo "$COVERAGE_PCT > 0" | bc -l) )); then
            echo "coverage_result=success" >> $GITHUB_OUTPUT
          else
            echo "coverage_result=failure" >> $GITHUB_OUTPUT
          fi
        else
          echo '{"error": "No test results bundle found"}' > coverage-summary.json
          echo "coverage_result=failure" >> $GITHUB_OUTPUT
        fi
    
    # ARTIFACT UPLOADS
    - name: List build products
      if: always()
      run: |
        echo "Searching for build products..."
        find ~/Library/Developer/Xcode/DerivedData -name "VibeTunnel.app" -type d 2>/dev/null || echo "No build products found"
    
    # Skip build artifact upload for PR builds to save time
    - name: Upload build artifacts
      if: github.event_name == 'push' && github.ref == 'refs/heads/main'
      uses: actions/upload-artifact@v4
      with:
        name: mac-build-artifacts
        path: |
          ~/Library/Developer/Xcode/DerivedData/*/Build/Products/Debug/VibeTunnel.app
          ~/Library/Developer/Xcode/DerivedData/*/Build/Products/Release/VibeTunnel.app
        retention-days: 3
    
    - name: Upload coverage artifacts
      if: always()
      uses: actions/upload-artifact@v4
      with:
        name: mac-coverage
        path: |
          coverage-summary.json
          TestResults.xcresult
    
    # LINT REPORTING
    - name: Read SwiftFormat Output
      if: always()
      id: swiftformat-output
      run: |
        if [ -f swiftformat-output.txt ]; then
          echo 'content<<EOF' >> $GITHUB_OUTPUT
          cat swiftformat-output.txt >> $GITHUB_OUTPUT
          echo 'EOF' >> $GITHUB_OUTPUT
        else
          echo "content=No output" >> $GITHUB_OUTPUT
        fi
    
    - name: Read SwiftLint Output
      if: always()
      id: swiftlint-output
      run: |
        if [ -f swiftlint-output.txt ]; then
          echo 'content<<EOF' >> $GITHUB_OUTPUT
          cat swiftlint-output.txt >> $GITHUB_OUTPUT
          echo 'EOF' >> $GITHUB_OUTPUT
        else
          echo "content=No output" >> $GITHUB_OUTPUT
        fi
    
    - name: Report SwiftFormat Results
      if: always()
      uses: ./.github/actions/lint-reporter
      with:
        title: 'Mac Formatting (SwiftFormat)'
        lint-result: ${{ steps.swiftformat.outputs.result == '0' && 'success' || 'failure' }}
        lint-output: ${{ steps.swiftformat-output.outputs.content }}
        github-token: ${{ secrets.GITHUB_TOKEN }}
    
    - name: Report SwiftLint Results
      if: always()
      uses: ./.github/actions/lint-reporter
      with:
        title: 'Mac Linting (SwiftLint)'
        lint-result: ${{ steps.swiftlint.outputs.result == '0' && 'success' || 'failure' }}
        lint-output: ${{ steps.swiftlint-output.outputs.content }}
        github-token: ${{ secrets.GITHUB_TOKEN }}

  report-coverage:
    name: Report Coverage Results
    runs-on: blacksmith-8vcpu-ubuntu-2404-arm
    needs: [build-lint-test]
    # Only run coverage reporting on main branch where we actually collect coverage
    if: always() && github.event_name == 'push' && github.ref == 'refs/heads/main'

    steps:
    - name: Clean workspace
      run: |
        # Clean workspace for self-hosted runner
        # Clean workspace but preserve .git directory
        find . -maxdepth 1 -name '.*' -not -name '.git' -not -name '.' -not -name '..' -exec rm -rf {} + || true
        rm -rf * || true
        
    - name: Checkout code
      uses: actions/checkout@v4

    - name: Download coverage artifacts
      uses: actions/download-artifact@v4
      with:
        name: mac-coverage
        path: mac-coverage-artifacts

    - name: Read coverage summary
      id: coverage
      run: |
        if [ -f mac-coverage-artifacts/coverage-summary.json ]; then
          # Read the coverage summary
          COVERAGE_JSON=$(cat mac-coverage-artifacts/coverage-summary.json)
          echo "summary<<EOF" >> $GITHUB_OUTPUT
          echo "$COVERAGE_JSON" >> $GITHUB_OUTPUT
          echo "EOF" >> $GITHUB_OUTPUT
          
          # Extract coverage percentage
          COVERAGE_PCT=$(echo "$COVERAGE_JSON" | jq -r '.coverage // 0')
          
          # Any coverage above 0% is acceptable for now
          if (( $(echo "$COVERAGE_PCT > 0" | bc -l) )); then
            echo "result=success" >> $GITHUB_OUTPUT
          else
            echo "result=failure" >> $GITHUB_OUTPUT
          fi
          
          # Format output - just show the percentage
          echo "output=• Coverage: ${COVERAGE_PCT}%" >> $GITHUB_OUTPUT
        else
          echo "summary={\"error\": \"No coverage data found\"}" >> $GITHUB_OUTPUT
          echo "result=failure" >> $GITHUB_OUTPUT
          echo "output=Coverage data not found" >> $GITHUB_OUTPUT
        fi

    - name: Report Coverage Results
      uses: ./.github/actions/lint-reporter
      with:
        title: 'macOS Test Coverage'
        lint-result: ${{ steps.coverage.outputs.result }}
        lint-output: ${{ steps.coverage.outputs.output }}
        github-token: ${{ secrets.GITHUB_TOKEN }}