name: CI

on:
  push:
    branches:
        - main
        - dev
        - "feat/**"
        - "fix/**"
  pull_request:
    branches: [ "main", "dev" ]

env:
  CARGO_TERM_COLOR: always
  RUSTFLAGS: "-Dwarnings"
  DATABASE_URL: "postgres://postgres:postgres@localhost/oxicloud_test"

jobs:

  # Detect which parts of the codebase changed
  changes:
    runs-on: ubuntu-latest
    outputs:
      frontend: ${{ steps.filter.outputs.frontend }}
      backend: ${{ steps.filter.outputs.backend }}
    steps:
      - uses: actions/checkout@v4
      - uses: dorny/paths-filter@v3
        id: filter
        with:
          filters: |
            frontend:
              - 'static/**'
              - 'biome.json'
              - '.grit'
              - '.stylelintrc.json'
              - 'jsconfig.json'
            backend:
              - 'src/**'
              - 'Cargo.toml'
              - 'Cargo.lock'
              - 'tests/**'

  frontend-check:
    name: Frontend — CSS and JS checks (format, lint, css-rules, types)
    needs: changes
    if: needs.changes.outputs.frontend == 'true'
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Install Biome
        uses: biomejs/setup-biome@v2

      - name: Run Biome check
        run: biome ci static/

      - name: Setup Node
        uses: actions/setup-node@v4
        with:
          node-version: 25

      # because we are not using package.json
      - name: Install Stylelint, TypeScript and plugins
        run: |
            npm install --global \
                stylelint@17 \
                postcss@8 \
                stylelint-value-no-unknown-custom-properties@6 \
                typescript

      - name: Run Stylelint
        run: npx stylelint "static/css/**/*.{css,scss}"

      - name: Run TypeScript check
        run: tsc -p jsconfig.json --noEmit

  rust-fmt:
    name: Rustfmt
    needs: changes
    if: needs.changes.outputs.backend == 'true'
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: dtolnay/rust-toolchain@stable
        with:
          components: rustfmt
      - run: cargo fmt --all --check

  rust-clippy:
    name: Clippy
    needs: changes
    if: needs.changes.outputs.backend == 'true'
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: dtolnay/rust-toolchain@stable
        with:
          components: clippy
      - uses: Swatinem/rust-cache@v2
      - run: cargo clippy --all-targets --all-features -- -D warnings

  rust-test:
    name: Server Unit and Functionnal Tests
    needs: changes
    if: needs.changes.outputs.backend == 'true'
    runs-on: ubuntu-latest
    services:
      postgres:
        image: postgres:16-alpine
        env:
          POSTGRES_USER: postgres
          POSTGRES_PASSWORD: postgres
          POSTGRES_DB: oxicloud_test
        ports:
          - 5432:5432
        options: >-
          --health-cmd "pg_isready -U postgres"
          --health-interval 10s
          --health-timeout 5s
          --health-retries 5
    steps:
      - uses: actions/checkout@v4
      - uses: dtolnay/rust-toolchain@stable
      - uses: Swatinem/rust-cache@v2

      - name: Initialize test database
        run: psql -h localhost -U postgres -d oxicloud_test -f migrations/20260307000000_initial_schema.sql
        env:
          PGPASSWORD: postgres

      - name: Run tests
        run: cargo test --all-features --workspace
        env:
          DATABASE_URL: "postgres://postgres:postgres@localhost/oxicloud_test"

  rust-audit:
    name: Security Audit
    needs: changes
    if: needs.changes.outputs.backend == 'true'
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: rustsec/audit-check@v2.0.0
        with:
          token: ${{ secrets.GITHUB_TOKEN }}

  build:
    name: Build
    runs-on: ubuntu-latest
    if: github.event_name == 'pull_request'
    steps:
      - uses: actions/checkout@v4
      - uses: dtolnay/rust-toolchain@stable
      - uses: Swatinem/rust-cache@v2
      - run: cargo build --release
        # build.rs runs the deconflict pass and js_bundle_validate() — any
        # duplicate declaration or parse error in the JS bundle fails here.
      - name: Validate JS bundle (node --check)
        # belt-and-suspenders: node --check parses the bundle without executing it.
        # Catches SyntaxErrors that OXC's parse check inside build.rs would also
        # catch, but gives a human-readable error line in the CI log.
        run: node --check static-dist/js/app.*.js
      - uses: actions/upload-artifact@v4
        with:
          name: oxicloud-release
          path: target/release/oxicloud
          retention-days: 1

  api-test:
    name: API & Webdav tests
    needs: build
    if: github.event_name == 'pull_request'
    timeout-minutes: 30
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - uses: actions/download-artifact@v4
        with:
          name: oxicloud-release
          path: target/release/

      - name: Set execute bit on pre-built binary
        run: chmod +x target/release/oxicloud

      - name: Install Hurl and b3sum
        env:
          HURL_MAJOR: "8"
        run: |
          HURL_VERSION=$(curl -fsSL -H "Authorization: Bearer ${{ github.token }}" \
            https://api.github.com/repos/Orange-OpenSource/hurl/releases \
            | jq -r "map(select(.tag_name | startswith(\"${HURL_MAJOR}.\"))) | first | .tag_name")
          curl -fLO "https://github.com/Orange-OpenSource/hurl/releases/download/${HURL_VERSION}/hurl_${HURL_VERSION}_amd64.deb"
          sudo apt-get install -y "./hurl_${HURL_VERSION}_amd64.deb" b3sum

      - name: Run Hurl API tests
        run: bash tests/api/run.sh
        env:
          BUILD_TARGET: release

      - name: Run Webdav tests
        run: bash tests/webdav/run.sh
        env:
          BUILD_TARGET: release

      - uses: actions/upload-artifact@v4
        if: ${{ !cancelled() }}
        with:
          name: hurl-report
          path: tests/api/storage/
          retention-days: 7

  front-test:
    name: Frontend end-to-end tests (via Playwright)
    # ensure that api tests are ok before
    needs: api-test
    if: github.event_name == 'pull_request'
    timeout-minutes: 60
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - uses: actions/download-artifact@v4
        with:
          name: oxicloud-release
          path: target/release/

      - name: Set execute bit on pre-built binary
        run: chmod +x target/release/oxicloud

      - uses: actions/setup-node@v4
        with:
          node-version: lts/*

      - name: Install Node dependencies
        working-directory: tests/e2e
        run: npm ci

      - name: Install Playwright browsers
        working-directory: tests/e2e
        run: npx playwright install --with-deps

      - name: Run Playwright tests (spawns DB via pretest hook)
        working-directory: tests/e2e
        run: npm test
        env:
          BUILD_TARGET: release

      - uses: actions/upload-artifact@v4
        if: ${{ !cancelled() }}
        with:
          name: playwright-report
          path: tests/e2e/playwright-report/
          retention-days: 30
