name: PR Check

on:
  pull_request:
    branches: ["main"]
  push:
    branches: ["main"]

env:
  CARGO_TERM_COLOR: always
  RUST_BACKTRACE: 1

jobs:
  lint:
    name: Lint
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Install Rust toolchain
        uses: dtolnay/rust-toolchain@stable
        with:
          components: rustfmt, clippy

      - name: Cache cargo registry
        uses: actions/cache@v3
        with:
          path: ~/.cargo/registry
          key: ${{ runner.os }}-cargo-registry-${{ hashFiles('**/Cargo.lock') }}

      - name: Cache cargo index
        uses: actions/cache@v3
        with:
          path: ~/.cargo/git
          key: ${{ runner.os }}-cargo-index-${{ hashFiles('**/Cargo.lock') }}

      - name: Cache cargo build
        uses: actions/cache@v3
        with:
          path: target
          key: ${{ runner.os }}-cargo-build-target-${{ hashFiles('**/Cargo.lock') }}

      - name: Check formatting
        run: cargo fmt --all -- --check

      - name: Run clippy
        run: cargo clippy --all-targets --all-features -- -D warnings

  test:
    name: Test
    runs-on: ubuntu-latest
    needs: lint
    steps:
      - uses: actions/checkout@v4

      - name: Install Rust toolchain
        uses: dtolnay/rust-toolchain@stable

      - name: Cache cargo registry
        uses: actions/cache@v3
        with:
          path: ~/.cargo/registry
          key: ${{ runner.os }}-cargo-registry-${{ hashFiles('**/Cargo.lock') }}

      - name: Cache cargo index
        uses: actions/cache@v3
        with:
          path: ~/.cargo/git
          key: ${{ runner.os }}-cargo-index-${{ hashFiles('**/Cargo.lock') }}

      - name: Cache cargo build
        uses: actions/cache@v3
        with:
          path: target
          key: ${{ runner.os }}-cargo-build-target-${{ hashFiles('**/Cargo.lock') }}

      - name: Build
        run: cargo build --verbose

      - name: Run tests
        run: cargo test --verbose -- --test-threads=1
        env:
          RUST_BACKTRACE: 1

  coverage:
    name: Code Coverage
    runs-on: ubuntu-latest
    needs: test
    permissions:
      pull-requests: write
      contents: read
    steps:
      - uses: actions/checkout@v4

      - name: Install Rust toolchain
        uses: dtolnay/rust-toolchain@stable

      - name: Cache cargo registry
        uses: actions/cache@v3
        with:
          path: ~/.cargo/registry
          key: ${{ runner.os }}-cargo-registry-${{ hashFiles('**/Cargo.lock') }}

      - name: Cache cargo index
        uses: actions/cache@v3
        with:
          path: ~/.cargo/git
          key: ${{ runner.os }}-cargo-index-${{ hashFiles('**/Cargo.lock') }}

      - name: Cache tarpaulin
        uses: actions/cache@v3
        id: cache-tarpaulin
        with:
          path: ~/.cargo/bin/cargo-tarpaulin
          key: ${{ runner.os }}-tarpaulin-0.34.1

      - name: Install cargo-tarpaulin
        if: steps.cache-tarpaulin.outputs.cache-hit != 'true'
        run: cargo install cargo-tarpaulin --version 0.34.1

      - name: Generate coverage
        run: |
          cargo tarpaulin --verbose --all-features --workspace --timeout 300 --out Xml --out Html --engine llvm -- --test-threads=1

      - name: Upload coverage to Codecov
        uses: codecov/codecov-action@v5
        with:
          token: ${{ secrets.CODECOV_TOKEN }}
          files: ./cobertura.xml
          fail_ci_if_error: false
          verbose: true

      - name: Extract coverage percentage
        id: coverage
        run: |
          COVERAGE=$(grep -oP 'line-rate="\K[0-9.]+' cobertura.xml | head -1 | awk '{printf "%.2f", $1 * 100}')
          echo "coverage=${COVERAGE}" >> $GITHUB_OUTPUT
          echo "Coverage: ${COVERAGE}%"

      - name: Comment PR with coverage
        if: github.event_name == 'pull_request'
        uses: actions/github-script@v7
        with:
          github-token: ${{ secrets.GITHUB_TOKEN }}
          script: |
            const coverage = '${{ steps.coverage.outputs.coverage }}';
            const body = `## 📊 Coverage Report
            
            **Overall Coverage:** \`${coverage}%\`
            
            Full coverage report available in [Codecov](https://app.codecov.io/gh/${{ github.repository }}/pull/${{ github.event.pull_request.number }})
            
            <details>
            <summary>Coverage Details</summary>
            
            The coverage report has been generated and uploaded to Codecov.
            - **Target:** 70%
            - **Patch Target:** 80%
            
            </details>
            `;
            
            const { data: comments } = await github.rest.issues.listComments({
              owner: context.repo.owner,
              repo: context.repo.repo,
              issue_number: context.issue.number,
            });
            
            const botComment = comments.find(comment => 
              comment.user.type === 'Bot' && 
              comment.body.includes('📊 Coverage Report')
            );
            
            if (botComment) {
              await github.rest.issues.updateComment({
                owner: context.repo.owner,
                repo: context.repo.repo,
                comment_id: botComment.id,
                body: body
              });
            } else {
              await github.rest.issues.createComment({
                owner: context.repo.owner,
                repo: context.repo.repo,
                issue_number: context.issue.number,
                body: body
              });
            }

      - name: Archive coverage report
        uses: actions/upload-artifact@v4
        with:
          name: coverage-report
          path: |
            cobertura.xml
            tarpaulin-report.html

  security-audit:
    name: Security Audit
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Install Rust toolchain
        uses: dtolnay/rust-toolchain@stable

      - name: Run cargo audit
        uses: rustsec/audit-check@v1
        with:
          token: ${{ secrets.GITHUB_TOKEN }}

  check-success:
    name: Check All Jobs Passed
    runs-on: ubuntu-latest
    needs: [lint, test, coverage, security-audit]
    if: always()
    steps:
      - name: Check if all jobs succeeded
        run: |
          if [ "${{ needs.lint.result }}" != "success" ] || \
             [ "${{ needs.test.result }}" != "success" ] || \
             [ "${{ needs.coverage.result }}" != "success" ] || \
             [ "${{ needs.security-audit.result }}" != "success" ]; then
            echo "One or more jobs failed"
            exit 1
          fi
          echo "All jobs passed successfully!"
