name: Node.js CI

on:
  workflow_call:

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

jobs:
  node-ci:
    name: Node.js CI - Lint, Build, Test, Type Check
    runs-on: blacksmith-8vcpu-ubuntu-2404-arm
    env:
      GITHUB_REPO_NAME: ${{ github.repository }}

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

    - name: Setup Node.js
      uses: actions/setup-node@v4
      with:
        node-version: '24'

    - name: Setup pnpm
      uses: pnpm/action-setup@v4
      with:
        version: 10
        run_install: false

    - name: Install system dependencies
      run: |
        sudo apt-get update
        sudo apt-get install -y libpam0g-dev

    - name: Cache TypeScript build info
      uses: useblacksmith/cache@v5
      continue-on-error: true
      with:
        path: |
          web/dist/tsconfig.server.tsbuildinfo
          web/public/tsconfig.client.tsbuildinfo
          web/public/tsconfig.sw.tsbuildinfo
        key: ${{ runner.os }}-tsbuild-${{ hashFiles('web/src/**/*.ts', 'web/tsconfig*.json') }}
        restore-keys: |
          ${{ runner.os }}-tsbuild-

    - name: Install dependencies
      working-directory: web
      run: |
        pnpm config set network-concurrency 4
        pnpm config set child-concurrency 2
        pnpm install --frozen-lockfile --prefer-offline

    - name: Build node-pty
      working-directory: web
      run: |
        cd node-pty && npm install && npm run build

    # Run all checks - build first, then tests
    - name: Run all checks
      working-directory: web
      run: |
        # Create a temporary directory for outputs
        mkdir -p ci-outputs
        
        # Run format, lint, typecheck, and audit in parallel (these don't depend on build)
        (
          echo "Starting format check..."
          pnpm run format:check > ci-outputs/format-output.txt 2>&1
          echo $? > ci-outputs/format-exit-code.txt
          echo "Format check completed"
        ) &
        
        (
          echo "Starting lint..."
          pnpm run lint:biome > ci-outputs/lint-output.txt 2>&1
          echo $? > ci-outputs/lint-exit-code.txt
          echo "Lint completed"
        ) &
        
        (
          echo "Starting type check..."
          pnpm run typecheck > ci-outputs/typecheck-output.txt 2>&1
          echo $? > ci-outputs/typecheck-exit-code.txt
          echo "Type check completed"
        ) &
        
        (
          echo "Starting security audit..."
          pnpm audit --audit-level=moderate > ci-outputs/audit-output.txt 2>&1 || true
          echo 0 > ci-outputs/audit-exit-code.txt  # Don't fail on audit
          echo "Audit completed"
        ) &
        
        # Wait for parallel checks
        wait
        
        # Run build (must complete before tests)
        echo "Starting build..."
        export ESBUILD_MAX_WORKERS=$(nproc)
        pnpm run build:ci > ci-outputs/build-output.txt 2>&1
        echo $? > ci-outputs/build-exit-code.txt
        echo "Build completed"
        
        # Run tests after build completes
        echo "Starting tests..."
        # Run client and server tests sequentially to avoid conflicts
        pnpm run test:client:coverage > ci-outputs/test-client-output.txt 2>&1
        CLIENT_EXIT=$?
        pnpm run test:server:coverage > ci-outputs/test-server-output.txt 2>&1
        SERVER_EXIT=$?
        # Return non-zero if either test failed
        if [ $CLIENT_EXIT -ne 0 ] || [ $SERVER_EXIT -ne 0 ]; then
          echo 1 > ci-outputs/test-exit-code.txt
        else
          echo 0 > ci-outputs/test-exit-code.txt
        fi
        echo "Tests completed"
        
        echo "All checks completed"

    # Process results
    - name: Process check results
      if: always()
      id: results
      working-directory: web
      run: |
        # Read exit codes
        FORMAT_EXIT=$(cat ci-outputs/format-exit-code.txt || echo 1)
        LINT_EXIT=$(cat ci-outputs/lint-exit-code.txt || echo 1)
        TYPECHECK_EXIT=$(cat ci-outputs/typecheck-exit-code.txt || echo 1)
        BUILD_EXIT=$(cat ci-outputs/build-exit-code.txt || echo 1)
        TEST_EXIT=$(cat ci-outputs/test-exit-code.txt || echo 1)
        
        # Set outputs
        echo "format_result=$FORMAT_EXIT" >> $GITHUB_OUTPUT
        echo "lint_result=$LINT_EXIT" >> $GITHUB_OUTPUT
        echo "typecheck_result=$TYPECHECK_EXIT" >> $GITHUB_OUTPUT
        echo "build_result=$BUILD_EXIT" >> $GITHUB_OUTPUT
        echo "test_result=$TEST_EXIT" >> $GITHUB_OUTPUT
        
        # Read outputs for reporting
        echo 'format_output<<EOF' >> $GITHUB_OUTPUT
        cat ci-outputs/format-output.txt 2>/dev/null || echo "No output" >> $GITHUB_OUTPUT
        echo 'EOF' >> $GITHUB_OUTPUT
        
        echo 'lint_output<<EOF' >> $GITHUB_OUTPUT
        cat ci-outputs/lint-output.txt 2>/dev/null || echo "No output" >> $GITHUB_OUTPUT
        echo 'EOF' >> $GITHUB_OUTPUT
        
        echo 'typecheck_output<<EOF' >> $GITHUB_OUTPUT
        cat ci-outputs/typecheck-output.txt 2>/dev/null || echo "No output" >> $GITHUB_OUTPUT
        echo 'EOF' >> $GITHUB_OUTPUT
        
        echo 'build_output<<EOF' >> $GITHUB_OUTPUT
        tail -n 50 ci-outputs/build-output.txt 2>/dev/null || echo "No output" >> $GITHUB_OUTPUT
        echo 'EOF' >> $GITHUB_OUTPUT
        
        echo 'test_output<<EOF' >> $GITHUB_OUTPUT
        tail -n 100 ci-outputs/test-client-output.txt 2>/dev/null || echo "No client test output" >> $GITHUB_OUTPUT
        echo "---" >> $GITHUB_OUTPUT
        tail -n 100 ci-outputs/test-server-output.txt 2>/dev/null || echo "No server test output" >> $GITHUB_OUTPUT
        echo 'EOF' >> $GITHUB_OUTPUT
        
        echo 'audit_output<<EOF' >> $GITHUB_OUTPUT
        cat ci-outputs/audit-output.txt 2>/dev/null || echo "No output" >> $GITHUB_OUTPUT
        echo 'EOF' >> $GITHUB_OUTPUT
        
        # Determine overall result
        if [ $FORMAT_EXIT -ne 0 ] || [ $LINT_EXIT -ne 0 ] || [ $TYPECHECK_EXIT -ne 0 ] || [ $BUILD_EXIT -ne 0 ] || [ $TEST_EXIT -ne 0 ]; then
          echo "overall_result=failure" >> $GITHUB_OUTPUT
        else
          echo "overall_result=success" >> $GITHUB_OUTPUT
        fi

    # Generate coverage summary
    - name: Generate coverage summaries
      if: always()
      working-directory: web
      run: |
        # Process client coverage
        if [ -f coverage/client/coverage-summary.json ]; then
          node -e "
            const coverage = require('./coverage/client/coverage-summary.json');
            const total = coverage.total;
            const summary = {
              type: 'client',
              lines: { pct: total.lines.pct, covered: total.lines.covered, total: total.lines.total },
              statements: { pct: total.statements.pct, covered: total.statements.covered, total: total.statements.total },
              functions: { pct: total.functions.pct, covered: total.functions.covered, total: total.functions.total },
              branches: { pct: total.branches.pct, covered: total.branches.covered, total: total.branches.total }
            };
            console.log(JSON.stringify(summary, null, 2));
          " > coverage-client-summary.json
        else
          echo '{"error": "No client coverage data found"}' > coverage-client-summary.json
        fi
        
        # Process server coverage
        if [ -f coverage/server/coverage-summary.json ]; then
          node -e "
            const coverage = require('./coverage/server/coverage-summary.json');
            const total = coverage.total;
            const summary = {
              type: 'server',
              lines: { pct: total.lines.pct, covered: total.lines.covered, total: total.lines.total },
              statements: { pct: total.statements.pct, covered: total.statements.covered, total: total.statements.total },
              functions: { pct: total.functions.pct, covered: total.functions.covered, total: total.functions.total },
              branches: { pct: total.branches.pct, covered: total.branches.covered, total: total.branches.total }
            };
            console.log(JSON.stringify(summary, null, 2));
          " > coverage-server-summary.json
        else
          echo '{"error": "No server coverage data found"}' > coverage-server-summary.json
        fi

    # Report results
    - name: Report Format Results
      if: always()
      uses: ./.github/actions/lint-reporter
      with:
        title: 'Node.js Biome Formatting'
        lint-result: ${{ steps.results.outputs.format_result == '0' && 'success' || 'failure' }}
        lint-output: ${{ steps.results.outputs.format_output }}
        github-token: ${{ secrets.GITHUB_TOKEN }}

    - name: Report Lint Results
      if: always()
      uses: ./.github/actions/lint-reporter
      with:
        title: 'Node.js Biome Linting'
        lint-result: ${{ steps.results.outputs.lint_result == '0' && 'success' || 'failure' }}
        lint-output: ${{ steps.results.outputs.lint_output }}
        github-token: ${{ secrets.GITHUB_TOKEN }}

    - name: Report TypeCheck Results
      if: always()
      uses: ./.github/actions/lint-reporter
      with:
        title: 'Node.js TypeScript Type Checking'
        lint-result: ${{ steps.results.outputs.typecheck_result == '0' && 'success' || 'failure' }}
        lint-output: ${{ steps.results.outputs.typecheck_output }}
        github-token: ${{ secrets.GITHUB_TOKEN }}

    - name: Report Build Results
      if: always()
      uses: ./.github/actions/lint-reporter
      with:
        title: 'Node.js Build'
        lint-result: ${{ steps.results.outputs.build_result == '0' && 'success' || 'failure' }}
        lint-output: ${{ steps.results.outputs.build_output }}
        github-token: ${{ secrets.GITHUB_TOKEN }}

    - name: Report Test Results
      if: always()
      uses: ./.github/actions/lint-reporter
      with:
        title: 'Node.js Tests'
        lint-result: ${{ steps.results.outputs.test_result == '0' && 'success' || 'failure' }}
        lint-output: ${{ steps.results.outputs.test_output }}
        github-token: ${{ secrets.GITHUB_TOKEN }}

    - name: Report Audit Results
      if: always()
      uses: ./.github/actions/lint-reporter
      with:
        title: 'Node.js Security Audit'
        lint-result: 'success'
        lint-output: ${{ steps.results.outputs.audit_output }}
        github-token: ${{ secrets.GITHUB_TOKEN }}

    # Format and report coverage
    - name: Format coverage output
      id: format-coverage
      if: always() && github.event_name == 'pull_request'
      working-directory: web
      run: |
        # Format client coverage
        CLIENT_OUTPUT="**Client Coverage:**\n"
        if [ -f coverage-client-summary.json ] && [ "$(jq -r '.error // empty' coverage-client-summary.json)" = "" ]; then
          CLIENT_LINES=$(jq -r '.lines.pct' coverage-client-summary.json)
          CLIENT_FUNCTIONS=$(jq -r '.functions.pct' coverage-client-summary.json)
          CLIENT_BRANCHES=$(jq -r '.branches.pct' coverage-client-summary.json)
          CLIENT_STATEMENTS=$(jq -r '.statements.pct' coverage-client-summary.json)
          
          CLIENT_OUTPUT="${CLIENT_OUTPUT}• Lines: ${CLIENT_LINES}%\n"
          CLIENT_OUTPUT="${CLIENT_OUTPUT}• Functions: ${CLIENT_FUNCTIONS}%\n"
          CLIENT_OUTPUT="${CLIENT_OUTPUT}• Branches: ${CLIENT_BRANCHES}%\n"
          CLIENT_OUTPUT="${CLIENT_OUTPUT}• Statements: ${CLIENT_STATEMENTS}%\n"
        else
          CLIENT_OUTPUT="${CLIENT_OUTPUT}No client coverage data found\n"
        fi
        
        # Format server coverage
        SERVER_OUTPUT="\n**Server Coverage:**\n"
        if [ -f coverage-server-summary.json ] && [ "$(jq -r '.error // empty' coverage-server-summary.json)" = "" ]; then
          SERVER_LINES=$(jq -r '.lines.pct' coverage-server-summary.json)
          SERVER_FUNCTIONS=$(jq -r '.functions.pct' coverage-server-summary.json)
          SERVER_BRANCHES=$(jq -r '.branches.pct' coverage-server-summary.json)
          SERVER_STATEMENTS=$(jq -r '.statements.pct' coverage-server-summary.json)
          
          SERVER_OUTPUT="${SERVER_OUTPUT}• Lines: ${SERVER_LINES}%\n"
          SERVER_OUTPUT="${SERVER_OUTPUT}• Functions: ${SERVER_FUNCTIONS}%\n"
          SERVER_OUTPUT="${SERVER_OUTPUT}• Branches: ${SERVER_BRANCHES}%\n"
          SERVER_OUTPUT="${SERVER_OUTPUT}• Statements: ${SERVER_STATEMENTS}%"
        else
          SERVER_OUTPUT="${SERVER_OUTPUT}No server coverage data found"
        fi
        
        echo "output<<EOF" >> $GITHUB_OUTPUT
        echo -e "${CLIENT_OUTPUT}${SERVER_OUTPUT}" >> $GITHUB_OUTPUT
        echo "EOF" >> $GITHUB_OUTPUT

    - name: Report Coverage Results
      if: always() && github.event_name == 'pull_request'
      uses: ./.github/actions/lint-reporter
      with:
        title: 'Node.js Test Coverage'
        lint-result: 'success'
        lint-output: ${{ steps.format-coverage.outputs.output }}
        github-token: ${{ secrets.GITHUB_TOKEN }}

    # Upload artifacts
    - name: Upload coverage artifacts
      if: always()
      uses: actions/upload-artifact@v4
      with:
        name: node-coverage
        path: |
          web/coverage-client-summary.json
          web/coverage-server-summary.json
          web/coverage/client/lcov.info
          web/coverage/server/lcov.info

    # Check overall result
    - name: Check overall result
      if: always()
      run: |
        if [ "${{ steps.results.outputs.overall_result }}" = "failure" ]; then
          echo "::error::One or more checks failed"
          exit 1
        fi