name: Rust CI

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

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

env:
  CARGO_TERM_COLOR: always
  RUST_BACKTRACE: 1

jobs:
  style:
    name: Check Style
    runs-on: ubuntu-latest
    steps:
      - name: Checkout repository
        uses: actions/checkout@v4

      - name: Install Rust toolchain
        uses: actions-rust-lang/setup-rust-toolchain@v1
        with:
          toolchain: stable
          components: rustfmt
          cache: false

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

  build_and_test:
    name: Build and Test (${{ matrix.os }})
    runs-on: ${{ matrix.os }}
    strategy:
      fail-fast: false
      matrix:
        os: [ubuntu-latest, macos-latest, windows-latest]

    steps:
      - name: Checkout repository
        uses: actions/checkout@v4
        with: { fetch-depth: 0 }

      # --- Smart Change Detection ---
      - name: Get branch name
        id: branch-name
        uses: tj-actions/branch-names@v8

      - name: Get last successful commit
        uses: nrwl/nx-set-shas@v4
        id: last_successful_commit
        with:
          main-branch-name: ${{ steps.branch-name.outputs.current_branch }}
          workflow-id: 'ci.yml'

      - name: Get changed files
        id: changed_files
        uses: tj-actions/changed-files@v45
        with:
          base_sha: ${{ steps.last_successful_commit.outputs.base }}
          files_yaml: |
            code:
              - src/**/*.rs
              - tests/**/*.rs
              - Cargo.toml
              - Cargo.lock
              - .github/workflows/**

      # --- Setup ---
      - name: Install Rust toolchain
        if: steps.changed_files.outputs.code_any_changed == 'true'
        uses: actions-rust-lang/setup-rust-toolchain@v1
        with:
          toolchain: stable
          cache: true
          components: clippy

      - name: Install cargo-hack & cargo-nextest
        if: steps.changed_files.outputs.code_any_changed == 'true'
        uses: taiki-e/install-action@v2
        with:
          tool: cargo-hack, cargo-nextest

      - name: Configure Nextest
        if: steps.changed_files.outputs.code_any_changed == 'true'
        shell: bash
        run: |
          mkdir -p .config
          cat <<EOF > .config/nextest.toml
          [profile.ci]
          failure-output = "immediate"
          fail-fast = false
          
          [profile.ci.junit]
          path = "junit.xml"
          report-name = "nextest-run"
          EOF

      # --- Execution ---
      - name: Linting (cargo clippy)
        if: steps.changed_files.outputs.code_any_changed == 'true'
        # Note: setup-rust-toolchain handles the Problem Matcher for Clippy automatically
        run: cargo hack --each-feature clippy --all-targets -- -D warnings

      - name: Run Tests (nextest)
        if: steps.changed_files.outputs.code_any_changed == 'true'
        # We run with --profile ci to ensure XML generation
        run: cargo nextest run --all-features --profile ci

      - name: Run Doc Tests
        if: steps.changed_files.outputs.code_any_changed == 'true'
        run: cargo test --doc --all-features

      - name: Build (Release)
        if: steps.changed_files.outputs.code_any_changed == 'true'
        run: cargo build --release --all-features --verbose

      # --- Artifact Upload ---
      - name: Upload Test Results
        # Run if code changed, even if previous steps failed (so we see WHY they failed)
        if: steps.changed_files.outputs.code_any_changed == 'true' && !cancelled()
        uses: actions/upload-artifact@v4
        with:
          # Naming convention is critical here for the reporter regex
          name: test-results-${{ matrix.os }}
          path: target/nextest/ci/junit.xml
          if-no-files-found: ignore