name: CI

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

env:
  CARGO_TERM_COLOR: always
  RUST_BACKTRACE: 1

jobs:
  # ──────────────────────────────────────────────
  # Job 1: Format Check
  # ──────────────────────────────────────────────
  fmt:
    name: Rustfmt
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v7
      - uses: dtolnay/rust-toolchain@stable
        with:
          components: rustfmt
      - run: cargo fmt --all -- --check

  # ──────────────────────────────────────────────
  # Job 2: Clippy Lint (all platforms)
  # ──────────────────────────────────────────────
  clippy:
    name: Clippy (${{ matrix.os }})
    runs-on: ${{ matrix.os }}
    strategy:
      fail-fast: false
      matrix:
        os: [ubuntu-latest, macos-latest, windows-latest]
    steps:
      - uses: actions/checkout@v7
      - uses: dtolnay/rust-toolchain@stable
        with:
          components: clippy
      - uses: Swatinem/rust-cache@v2
        with:
          key: clippy-${{ matrix.os }}
      - run: cargo clippy --all-targets --all-features -- -D warnings

  # ──────────────────────────────────────────────
  # Job 3: Tests (all platforms, multiple Rust versions)
  # ──────────────────────────────────────────────
  test:
    name: Test (${{ matrix.os }}, rust ${{ matrix.rust }})
    runs-on: ${{ matrix.os }}
    strategy:
      fail-fast: false
      matrix:
        os: [ubuntu-latest, macos-latest, windows-latest]
        rust: [stable, beta]
        include:
          - os: ubuntu-latest
            rust: nightly
    steps:
      - uses: actions/checkout@v7
      - uses: dtolnay/rust-toolchain@master
        with:
          toolchain: ${{ matrix.rust }}
      - uses: Swatinem/rust-cache@v2
        with:
          key: test-${{ matrix.os }}-${{ matrix.rust }}
      - name: Run unit tests
        run: cargo test --lib
      - name: Run integration tests
        run: cargo test --tests
      - name: Run doc tests
        run: cargo test --doc

  # ──────────────────────────────────────────────
  # Job 4: Docker Integration Tests
  # ──────────────────────────────────────────────
  docker-tests:
    name: Docker Integration Tests
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v7
      - uses: dtolnay/rust-toolchain@stable
      - uses: Swatinem/rust-cache@v2
        with:
          key: docker-tests
      - name: Pull nginx image
        run: docker pull nginx:alpine
      - name: Run container integration tests
        run: cargo test --test container_integration_test -- --ignored

  # ──────────────────────────────────────────────
  # Job 5: Code Coverage
  # ──────────────────────────────────────────────
  coverage:
    name: Code Coverage
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v7
      - uses: dtolnay/rust-toolchain@stable
      - uses: Swatinem/rust-cache@v2
        with:
          key: coverage
      - name: Install cargo-llvm-cov
        uses: taiki-e/install-action@cargo-llvm-cov
      - name: Generate coverage
        run: cargo llvm-cov --all-features --workspace --lcov --output-path lcov.info
      - name: Upload coverage to Codecov
        uses: codecov/codecov-action@v7
        with:
          files: lcov.info
          fail_ci_if_error: false

  # ──────────────────────────────────────────────
  # Job 6: Security Audit
  # ──────────────────────────────────────────────
  audit:
    name: Security Audit
    runs-on: ubuntu-latest
    # Dependabot-triggered runs get a read-only token by default; audit-check
    # needs checks: write to report its results.
    permissions:
      contents: read
      checks: write
    steps:
      - uses: actions/checkout@v7
      - uses: rustsec/audit-check@v2
        with:
          token: ${{ secrets.GITHUB_TOKEN }}

  # ──────────────────────────────────────────────
  # Job 7: Cross-compilation Check
  # ──────────────────────────────────────────────
  cross-build:
    name: Cross Build (${{ matrix.target }})
    runs-on: ${{ matrix.runner }}
    strategy:
      fail-fast: false
      matrix:
        include:
          - target: x86_64-unknown-linux-gnu
            runner: ubuntu-latest
          - target: i686-unknown-linux-gnu
            runner: ubuntu-latest
          - target: aarch64-unknown-linux-gnu
            runner: ubuntu-latest
          - target: armv7-unknown-linux-gnueabihf
            runner: ubuntu-latest
          - target: arm-unknown-linux-gnueabihf
            runner: ubuntu-latest
          - target: powerpc64le-unknown-linux-gnu
            runner: ubuntu-latest
          - target: s390x-unknown-linux-gnu
            runner: ubuntu-latest
          - target: aarch64-apple-darwin
            runner: macos-latest
          - target: x86_64-apple-darwin
            runner: macos-latest
          - target: x86_64-pc-windows-gnu
            runner: ubuntu-latest
    steps:
      - uses: actions/checkout@v7
      - uses: dtolnay/rust-toolchain@stable
        with:
          targets: ${{ matrix.target }}
      - uses: Swatinem/rust-cache@v2
        with:
          key: cross-${{ matrix.target }}
      - name: Install cross
        run: cargo install cross --git https://github.com/cross-rs/cross
        if: matrix.runner == 'ubuntu-latest'
      - name: Build (cross)
        run: cross build --target ${{ matrix.target }}
        if: matrix.runner == 'ubuntu-latest'
      - name: Build (native)
        run: cargo build --target ${{ matrix.target }}
        if: matrix.runner != 'ubuntu-latest'

  # ──────────────────────────────────────────────
  # Job 8: Documentation
  # ──────────────────────────────────────────────
  docs:
    name: Documentation
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v7
      - uses: dtolnay/rust-toolchain@stable
      - uses: Swatinem/rust-cache@v2
        with:
          key: docs
      - run: cargo doc --no-deps --all-features
        env:
          RUSTDOCFLAGS: -D warnings

  # ──────────────────────────────────────────────
  # Job 9: Aggregate gate — the single required status check.
  # Branch ruleset and Dependabot auto-merge key off this job,
  # so it must fail unless every job above succeeded.
  # ──────────────────────────────────────────────
  ci-ok:
    name: CI OK
    if: always()
    needs: [fmt, clippy, test, docker-tests, coverage, audit, cross-build, docs]
    runs-on: ubuntu-latest
    steps:
      - name: Check that all jobs succeeded
        run: |
          results='${{ join(needs.*.result, ' ') }}'
          echo "Job results: $results"
          for result in $results; do
            if [ "$result" != "success" ]; then
              echo "A required job finished with result: $result"
              exit 1
            fi
          done
