name: CI

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

env:
  CARGO_TERM_COLOR: always

jobs:
  build:
    name: Build
    runs-on: ubuntu-latest

    services:
      postgres:
        image: postgres:15-alpine
        env:
          POSTGRES_USER: postgres
          POSTGRES_PASSWORD: postgres
          POSTGRES_DB: fraiseql_test
        options: >-
          --health-cmd pg_isready
          --health-interval 10s
          --health-timeout 5s
          --health-retries 5
        ports:
          - 5432:5432

    steps:
      - uses: actions/checkout@v4

      - name: Install Rust
        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-git-${{ 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 --lib

      - name: Run clippy
        run: cargo clippy -- -D warnings

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

      - name: Security audit
        run: cargo audit --deny warnings

  build-postgres-17:
    name: Build (PostgreSQL 17 with Chunked Rows)
    runs-on: ubuntu-latest

    services:
      postgres:
        image: postgres:17-alpine
        env:
          POSTGRES_USER: postgres
          POSTGRES_PASSWORD: postgres
          POSTGRES_DB: fraiseql_test
        options: >-
          --health-cmd pg_isready
          --health-interval 10s
          --health-timeout 5s
          --health-retries 5
        ports:
          - 5432:5432

    steps:
      - uses: actions/checkout@v4

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

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

      - name: Build
        run: cargo build --verbose

      - name: Run tests with PostgreSQL 17
        run: cargo test --verbose --lib

  coverage:
    name: Code Coverage
    runs-on: ubuntu-latest

    steps:
      - uses: actions/checkout@v4

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

      - name: Install tarpaulin
        run: cargo install cargo-tarpaulin

      - name: Generate coverage
        run: cargo tarpaulin --out Xml --exclude-files tests/* --timeout 120

      - name: Upload coverage to Codecov
        uses: codecov/codecov-action@v3
        with:
          files: ./cobertura.xml
          fail_ci_if_error: false

  msrv:
    name: MSRV (Rust 1.75)
    runs-on: ubuntu-latest

    steps:
      - uses: actions/checkout@v4

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

      - name: Build with MSRV
        run: cargo build --verbose

      - name: Test with MSRV
        run: cargo test --lib --verbose

  integration-tests:
    name: Integration Tests (PostgreSQL 15)
    runs-on: ubuntu-latest

    services:
      postgres:
        image: postgres:15-alpine
        env:
          POSTGRES_USER: postgres
          POSTGRES_PASSWORD: postgres
          POSTGRES_DB: fraiseql_test
        options: >-
          --health-cmd pg_isready
          --health-interval 10s
          --health-timeout 5s
          --health-retries 5
        ports:
          - 5432:5432

    steps:
      - uses: actions/checkout@v4

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

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

      - name: Configure PostgreSQL SCRAM Authentication
        run: |
          until pg_isready -h localhost -p 5432; do sleep 1; done
          PGPASSWORD=postgres psql -h localhost -U postgres -c \
            "ALTER USER postgres WITH PASSWORD 'postgres';"
        env:
          PGPASSWORD: postgres

      - name: Run integration tests
        run: cargo test --test integration -- --ignored --nocapture
        env:
          POSTGRES_HOST: localhost
          POSTGRES_USER: postgres
          POSTGRES_PASSWORD: postgres
          POSTGRES_DB: fraiseql_test

      - name: Run streaming integration tests
        run: cargo test --test streaming_integration -- --ignored --nocapture
        env:
          POSTGRES_HOST: localhost
          POSTGRES_USER: postgres
          POSTGRES_PASSWORD: postgres
          POSTGRES_DB: fraiseql_test

      - name: Run full integration tests
        run: cargo test --test integration_full -- --ignored --nocapture
        env:
          POSTGRES_HOST: localhost
          POSTGRES_USER: postgres
          POSTGRES_PASSWORD: postgres
          POSTGRES_DB: fraiseql_test

      - name: Run operator tests
        run: cargo test --test integration_operators -- --ignored --nocapture
        env:
          POSTGRES_HOST: localhost
          POSTGRES_USER: postgres
          POSTGRES_PASSWORD: postgres
          POSTGRES_DB: fraiseql_test

      - name: Run pause/resume tests
        run: cargo test --test integration_pause_resume -- --ignored --nocapture
        env:
          POSTGRES_HOST: localhost
          POSTGRES_USER: postgres
          POSTGRES_PASSWORD: postgres
          POSTGRES_DB: fraiseql_test

      - name: Run Rust predicate tests
        run: cargo test --test rust_predicate_integration -- --ignored --nocapture
        env:
          POSTGRES_HOST: localhost
          POSTGRES_USER: postgres
          POSTGRES_PASSWORD: postgres
          POSTGRES_DB: fraiseql_test

      - name: Run type-safe deserialization tests
        run: cargo test --test typed_streaming -- --ignored --nocapture
        env:
          POSTGRES_HOST: localhost
          POSTGRES_USER: postgres
          POSTGRES_PASSWORD: postgres
          POSTGRES_DB: fraiseql_test

      - name: Run config integration tests
        run: cargo test --test config_integration -- --ignored --nocapture
        env:
          POSTGRES_HOST: localhost
          POSTGRES_USER: postgres
          POSTGRES_PASSWORD: postgres
          POSTGRES_DB: fraiseql_test

      - name: Run client integration tests
        run: cargo test --test client_integration -- --ignored --nocapture
        env:
          POSTGRES_HOST: localhost
          POSTGRES_USER: postgres
          POSTGRES_PASSWORD: postgres
          POSTGRES_DB: fraiseql_test

      - name: Run SCRAM integration tests
        run: cargo test --test scram_integration -- --ignored --nocapture
        env:
          SCRAM_TEST_DB_URL: "postgres://postgres:postgres@localhost:5432/fraiseql_test"
          POSTGRES_HOST: localhost
          POSTGRES_USER: postgres
          POSTGRES_PASSWORD: postgres

      - name: Run TLS integration tests
        run: cargo test --test tls_integration -- --ignored --nocapture
        env:
          TLS_TEST_DB_URL: "postgres://postgres:postgres@localhost:5432/fraiseql_test"
          TLS_TEST_INSECURE: "true"

  integration-tests-postgres-16:
    name: Integration Tests (PostgreSQL 16)
    runs-on: ubuntu-latest

    services:
      postgres:
        image: postgres:16-alpine
        env:
          POSTGRES_USER: postgres
          POSTGRES_PASSWORD: postgres
          POSTGRES_DB: fraiseql_test
        options: >-
          --health-cmd pg_isready
          --health-interval 10s
          --health-timeout 5s
          --health-retries 5
        ports:
          - 5432:5432

    steps:
      - uses: actions/checkout@v4

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

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

      - name: Configure PostgreSQL SCRAM Authentication
        run: |
          until pg_isready -h localhost -p 5432; do sleep 1; done
          PGPASSWORD=postgres psql -h localhost -U postgres -c \
            "ALTER USER postgres WITH PASSWORD 'postgres';"
        env:
          PGPASSWORD: postgres

      - name: Run integration tests against PostgreSQL 16
        run: cargo test --test integration -- --ignored --nocapture
        env:
          POSTGRES_HOST: localhost
          POSTGRES_USER: postgres
          POSTGRES_PASSWORD: postgres
          POSTGRES_DB: fraiseql_test

      - name: Run streaming integration tests against PostgreSQL 16
        run: cargo test --test streaming_integration -- --ignored --nocapture
        env:
          POSTGRES_HOST: localhost
          POSTGRES_USER: postgres
          POSTGRES_PASSWORD: postgres
          POSTGRES_DB: fraiseql_test

      - name: Run full integration tests against PostgreSQL 16
        run: cargo test --test integration_full -- --ignored --nocapture
        env:
          POSTGRES_HOST: localhost
          POSTGRES_USER: postgres
          POSTGRES_PASSWORD: postgres
          POSTGRES_DB: fraiseql_test

      - name: Run operator tests against PostgreSQL 16
        run: cargo test --test integration_operators -- --ignored --nocapture
        env:
          POSTGRES_HOST: localhost
          POSTGRES_USER: postgres
          POSTGRES_PASSWORD: postgres
          POSTGRES_DB: fraiseql_test

      - name: Run pause/resume tests against PostgreSQL 16
        run: cargo test --test integration_pause_resume -- --ignored --nocapture
        env:
          POSTGRES_HOST: localhost
          POSTGRES_USER: postgres
          POSTGRES_PASSWORD: postgres
          POSTGRES_DB: fraiseql_test

      - name: Run Rust predicate tests against PostgreSQL 16
        run: cargo test --test rust_predicate_integration -- --ignored --nocapture
        env:
          POSTGRES_HOST: localhost
          POSTGRES_USER: postgres
          POSTGRES_PASSWORD: postgres
          POSTGRES_DB: fraiseql_test

      - name: Run type-safe deserialization tests against PostgreSQL 16
        run: cargo test --test typed_streaming -- --ignored --nocapture
        env:
          POSTGRES_HOST: localhost
          POSTGRES_USER: postgres
          POSTGRES_PASSWORD: postgres
          POSTGRES_DB: fraiseql_test

      - name: Run SCRAM integration tests against PostgreSQL 16
        run: cargo test --test scram_integration -- --ignored --nocapture
        env:
          SCRAM_TEST_DB_URL: "postgres://postgres:postgres@localhost:5432/fraiseql_test"
          POSTGRES_HOST: localhost
          POSTGRES_USER: postgres
          POSTGRES_PASSWORD: postgres

      - name: Run TLS integration tests against PostgreSQL 16
        run: cargo test --test tls_integration -- --ignored --nocapture
        env:
          TLS_TEST_DB_URL: "postgres://postgres:postgres@localhost:5432/fraiseql_test"
          TLS_TEST_INSECURE: "true"

  integration-tests-postgres-17:
    name: Integration Tests (PostgreSQL 17 with Chunked Rows)
    runs-on: ubuntu-latest

    services:
      postgres:
        image: postgres:17-alpine
        env:
          POSTGRES_USER: postgres
          POSTGRES_PASSWORD: postgres
          POSTGRES_DB: fraiseql_test
        options: >-
          --health-cmd pg_isready
          --health-interval 10s
          --health-timeout 5s
          --health-retries 5
        ports:
          - 5432:5432

    steps:
      - uses: actions/checkout@v4

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

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

      - name: Configure PostgreSQL SCRAM Authentication
        run: |
          until pg_isready -h localhost -p 5432; do sleep 1; done
          PGPASSWORD=postgres psql -h localhost -U postgres -c \
            "ALTER USER postgres WITH PASSWORD 'postgres';"
        env:
          PGPASSWORD: postgres

      - name: Run integration tests against PostgreSQL 17
        run: cargo test --test integration -- --ignored --nocapture
        env:
          POSTGRES_HOST: localhost
          POSTGRES_USER: postgres
          POSTGRES_PASSWORD: postgres
          POSTGRES_DB: fraiseql_test

      - name: Run streaming integration tests against PostgreSQL 17
        run: cargo test --test streaming_integration -- --ignored --nocapture
        env:
          POSTGRES_HOST: localhost
          POSTGRES_USER: postgres
          POSTGRES_PASSWORD: postgres
          POSTGRES_DB: fraiseql_test

      - name: Run full integration tests against PostgreSQL 17
        run: cargo test --test integration_full -- --ignored --nocapture
        env:
          POSTGRES_HOST: localhost
          POSTGRES_USER: postgres
          POSTGRES_PASSWORD: postgres
          POSTGRES_DB: fraiseql_test

      - name: Run operator tests against PostgreSQL 17
        run: cargo test --test integration_operators -- --ignored --nocapture
        env:
          POSTGRES_HOST: localhost
          POSTGRES_USER: postgres
          POSTGRES_PASSWORD: postgres
          POSTGRES_DB: fraiseql_test

      - name: Run pause/resume tests against PostgreSQL 17
        run: cargo test --test integration_pause_resume -- --ignored --nocapture
        env:
          POSTGRES_HOST: localhost
          POSTGRES_USER: postgres
          POSTGRES_PASSWORD: postgres
          POSTGRES_DB: fraiseql_test

      - name: Run Rust predicate tests against PostgreSQL 17
        run: cargo test --test rust_predicate_integration -- --ignored --nocapture
        env:
          POSTGRES_HOST: localhost
          POSTGRES_USER: postgres
          POSTGRES_PASSWORD: postgres
          POSTGRES_DB: fraiseql_test

      - name: Run type-safe deserialization tests against PostgreSQL 17
        run: cargo test --test typed_streaming -- --ignored --nocapture
        env:
          POSTGRES_HOST: localhost
          POSTGRES_USER: postgres
          POSTGRES_PASSWORD: postgres
          POSTGRES_DB: fraiseql_test

      - name: Run SCRAM integration tests against PostgreSQL 17
        run: cargo test --test scram_integration -- --ignored --nocapture
        env:
          SCRAM_TEST_DB_URL: "postgres://postgres:postgres@localhost:5432/fraiseql_test"
          POSTGRES_HOST: localhost
          POSTGRES_USER: postgres
          POSTGRES_PASSWORD: postgres

      - name: Run TLS integration tests against PostgreSQL 17
        run: cargo test --test tls_integration -- --ignored --nocapture
        env:
          TLS_TEST_DB_URL: "postgres://postgres:postgres@localhost:5432/fraiseql_test"
          TLS_TEST_INSECURE: "true"

  build-postgres-18:
    name: Build (PostgreSQL 18)
    runs-on: ubuntu-latest

    services:
      postgres:
        image: postgres:18-alpine
        env:
          POSTGRES_USER: postgres
          POSTGRES_PASSWORD: postgres
          POSTGRES_DB: fraiseql_test
        options: >-
          --health-cmd pg_isready
          --health-interval 10s
          --health-timeout 5s
          --health-retries 5
        ports:
          - 5432:5432

    steps:
      - uses: actions/checkout@v4

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

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

      - name: Build
        run: cargo build --verbose

      - name: Run tests with PostgreSQL 18
        run: cargo test --verbose --lib

  integration-tests-postgres-18:
    name: Integration Tests (PostgreSQL 18)
    runs-on: ubuntu-latest

    services:
      postgres:
        image: postgres:18-alpine
        env:
          POSTGRES_USER: postgres
          POSTGRES_PASSWORD: postgres
          POSTGRES_DB: fraiseql_test
        options: >-
          --health-cmd pg_isready
          --health-interval 10s
          --health-timeout 5s
          --health-retries 5
        ports:
          - 5432:5432

    steps:
      - uses: actions/checkout@v4

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

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

      - name: Configure PostgreSQL SCRAM Authentication
        run: |
          until pg_isready -h localhost -p 5432; do sleep 1; done
          PGPASSWORD=postgres psql -h localhost -U postgres -c \
            "ALTER USER postgres WITH PASSWORD 'postgres';"
        env:
          PGPASSWORD: postgres

      - name: Run integration tests against PostgreSQL 18
        run: cargo test --test integration -- --ignored --nocapture
        env:
          POSTGRES_HOST: localhost
          POSTGRES_USER: postgres
          POSTGRES_PASSWORD: postgres
          POSTGRES_DB: fraiseql_test

      - name: Run streaming integration tests against PostgreSQL 18
        run: cargo test --test streaming_integration -- --ignored --nocapture
        env:
          POSTGRES_HOST: localhost
          POSTGRES_USER: postgres
          POSTGRES_PASSWORD: postgres
          POSTGRES_DB: fraiseql_test

      - name: Run full integration tests against PostgreSQL 18
        run: cargo test --test integration_full -- --ignored --nocapture
        env:
          POSTGRES_HOST: localhost
          POSTGRES_USER: postgres
          POSTGRES_PASSWORD: postgres
          POSTGRES_DB: fraiseql_test

      - name: Run operator tests against PostgreSQL 18
        run: cargo test --test integration_operators -- --ignored --nocapture
        env:
          POSTGRES_HOST: localhost
          POSTGRES_USER: postgres
          POSTGRES_PASSWORD: postgres
          POSTGRES_DB: fraiseql_test

      - name: Run pause/resume tests against PostgreSQL 18
        run: cargo test --test integration_pause_resume -- --ignored --nocapture
        env:
          POSTGRES_HOST: localhost
          POSTGRES_USER: postgres
          POSTGRES_PASSWORD: postgres
          POSTGRES_DB: fraiseql_test

      - name: Run Rust predicate tests against PostgreSQL 18
        run: cargo test --test rust_predicate_integration -- --ignored --nocapture
        env:
          POSTGRES_HOST: localhost
          POSTGRES_USER: postgres
          POSTGRES_PASSWORD: postgres
          POSTGRES_DB: fraiseql_test

      - name: Run type-safe deserialization tests against PostgreSQL 18
        run: cargo test --test typed_streaming -- --ignored --nocapture
        env:
          POSTGRES_HOST: localhost
          POSTGRES_USER: postgres
          POSTGRES_PASSWORD: postgres
          POSTGRES_DB: fraiseql_test

      - name: Run SCRAM integration tests against PostgreSQL 18
        run: cargo test --test scram_integration -- --ignored --nocapture
        env:
          SCRAM_TEST_DB_URL: "postgres://postgres:postgres@localhost:5432/fraiseql_test"
          POSTGRES_HOST: localhost
          POSTGRES_USER: postgres
          POSTGRES_PASSWORD: postgres

      - name: Run TLS integration tests against PostgreSQL 18
        run: cargo test --test tls_integration -- --ignored --nocapture
        env:
          TLS_TEST_DB_URL: "postgres://postgres:postgres@localhost:5432/fraiseql_test"
          TLS_TEST_INSECURE: "true"

  documentation:
    name: Documentation
    runs-on: ubuntu-latest

    steps:
      - uses: actions/checkout@v4

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

      - name: Build documentation
        run: cargo doc --no-deps --document-private-items

      - name: Check documentation
        env:
          RUSTDOCFLAGS: -D warnings
        run: cargo doc --no-deps
