name: CI

on:
  push:
    branches: [ main, master ]
    tags:
      - "v*"
  pull_request:
    branches: [ main, master ]
  workflow_dispatch:

env:
  CARGO_TERM_COLOR: always

jobs:
  # ─── Quality gate: fmt + clippy + tests ───────────────────────────────────
  test:
    name: Test (fmt, clippy, test)
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v6

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

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

      - name: Build
        run: cargo build --verbose

      - name: Run tests
        run: cargo test --verbose

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

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

      - name: Security audit
        run: |
          cargo install cargo-audit --quiet
          cargo audit

  # ─── Cross-compile Linux binaries (only on version tags) ──────────────────
  build-linux:
    name: Build Linux (${{ matrix.target }})
    runs-on: ubuntu-latest
    needs: [test]
    if: startsWith(github.ref, 'refs/tags/v')
    strategy:
      fail-fast: false
      matrix:
        target:
          - x86_64-unknown-linux-gnu
          - x86_64-unknown-linux-musl
          - aarch64-unknown-linux-gnu
          - aarch64-unknown-linux-musl

    steps:
      - uses: actions/checkout@v6

      - name: Install Rust (stable)
        uses: dtolnay/rust-toolchain@stable
        with:
          targets: ${{ matrix.target }}

      - name: Install cross
        uses: taiki-e/install-action@v2
        with:
          tool: cross

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

      - name: Build release (cross)
        run: cross build --release --target ${{ matrix.target }}

      - name: Package binary
        shell: bash
        run: |
          set -euxo pipefail
          BIN_NAME="oxo-call"
          TARGET_DIR="target/${{ matrix.target }}/release"
          mkdir -p dist
          tar -C "${TARGET_DIR}" -czvf \
            "dist/${BIN_NAME}-${{ github.ref_name }}-${{ matrix.target }}.tar.gz" \
            "${BIN_NAME}"

      - name: Upload artifact
        uses: actions/upload-artifact@v7
        with:
          name: oxo-call-${{ matrix.target }}
          path: dist/*.tar.gz

  # ─── Build macOS binaries (only on version tags) ──────────────────────────
  build-macos:
    name: Build macOS (${{ matrix.target }})
    permissions:
      contents: read
    runs-on: ${{ matrix.runner }}
    needs: [test]
    if: startsWith(github.ref, 'refs/tags/v')
    strategy:
      fail-fast: false
      matrix:
        include:
          - target: x86_64-apple-darwin
            runner: macos-latest
          - target: aarch64-apple-darwin
            runner: macos-14

    steps:
      - uses: actions/checkout@v6

      - name: Install Rust (stable)
        uses: dtolnay/rust-toolchain@stable
        with:
          targets: ${{ matrix.target }}

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

      - name: Build release
        run: cargo build --release --target ${{ matrix.target }}

      - name: Package binary
        shell: bash
        run: |
          set -euxo pipefail
          BIN_NAME="oxo-call"
          TARGET_DIR="target/${{ matrix.target }}/release"
          mkdir -p dist
          tar -C "${TARGET_DIR}" -czvf \
            "dist/${BIN_NAME}-${{ github.ref_name }}-${{ matrix.target }}.tar.gz" \
            "${BIN_NAME}"

      - name: Upload artifact
        uses: actions/upload-artifact@v7
        with:
          name: oxo-call-${{ matrix.target }}
          path: dist/*.tar.gz

  # ─── Build Windows binaries (only on version tags) ────────────────────────
  build-windows:
    name: Build Windows (${{ matrix.target }})
    permissions:
      contents: read
    runs-on: windows-latest
    needs: [test]
    if: startsWith(github.ref, 'refs/tags/v')
    strategy:
      fail-fast: false
      matrix:
        target:
          - x86_64-pc-windows-msvc
          - aarch64-pc-windows-msvc

    steps:
      - uses: actions/checkout@v6

      - name: Install Rust (stable)
        uses: dtolnay/rust-toolchain@stable
        with:
          targets: ${{ matrix.target }}

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

      - name: Build release
        run: cargo build --release --target ${{ matrix.target }}

      - name: Package binary
        shell: pwsh
        run: |
          $BIN_NAME = "oxo-call"
          $TARGET_DIR = "target/${{ matrix.target }}/release"
          New-Item -ItemType Directory -Force -Path dist | Out-Null
          Compress-Archive -Path "${TARGET_DIR}/${BIN_NAME}.exe" `
            -DestinationPath "dist/${BIN_NAME}-${{ github.ref_name }}-${{ matrix.target }}.zip"

      - name: Upload artifact
        uses: actions/upload-artifact@v7
        with:
          name: oxo-call-${{ matrix.target }}
          path: dist/*.zip

  # ─── Build WebAssembly binary (only on version tags) ─────────────────────────
  build-wasm:
    name: Build WebAssembly (wasm32-wasip1)
    runs-on: ubuntu-latest
    needs: [test]
    if: startsWith(github.ref, 'refs/tags/v')

    steps:
      - uses: actions/checkout@v6

      - name: Install Rust (stable) with wasm32-wasip1
        uses: dtolnay/rust-toolchain@stable
        with:
          targets: wasm32-wasip1

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

      - name: Build release (wasm32-wasip1)
        run: cargo build --release --target wasm32-wasip1

      - name: Package binary
        shell: bash
        run: |
          set -euxo pipefail
          BIN_NAME="oxo-call"
          TARGET_DIR="target/wasm32-wasip1/release"
          mkdir -p dist
          tar -C "${TARGET_DIR}" -czvf \
            "dist/${BIN_NAME}-${{ github.ref_name }}-wasm32-wasip1.tar.gz" \
            "${BIN_NAME}.wasm"

      - name: Upload artifact
        uses: actions/upload-artifact@v7
        with:
          name: oxo-call-wasm32-wasip1
          path: dist/*.tar.gz

  # ─── GitHub Release (attach all binary artifacts) ─────────────────────────
  release:
    permissions:
      contents: write
    name: GitHub Release
    needs: [build-linux, build-macos, build-windows, build-wasm]
    runs-on: ubuntu-latest
    if: startsWith(github.ref, 'refs/tags/v')

    steps:
      - uses: actions/checkout@v6

      - name: Download all artifacts
        uses: actions/download-artifact@v8
        with:
          path: artifacts

      - name: Generate SHA256 checksums
        shell: bash
        run: |
          mkdir -p artifacts/checksums
          cd artifacts
          find . -type f \( -name '*.tar.gz' -o -name '*.zip' \) -print0 \
            | while IFS= read -r -d '' f; do
                sha256sum "$f" | awk -v name="$(basename "$f")" '{print $1 "  " name}'
              done \
            | sort -k2 > checksums/SHA256SUMS.txt
          echo "=== SHA256SUMS ==="
          cat checksums/SHA256SUMS.txt

      - name: Create GitHub Release
        uses: softprops/action-gh-release@v2
        with:
          files: |
            artifacts/**/*.tar.gz
            artifacts/**/*.zip
            artifacts/checksums/SHA256SUMS.txt
          draft: false
          prerelease: false
          generate_release_notes: true
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

  # ─── Publish to crates.io (only on version tags) ──────────────────────────
  publish-crate:
    name: Publish to crates.io
    needs: [test]
    runs-on: ubuntu-latest
    if: startsWith(github.ref, 'refs/tags/v')

    steps:
      - uses: actions/checkout@v6

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

      - name: Verify tag matches Cargo.toml version
        shell: bash
        run: |
          set -euxo pipefail
          TAG="${GITHUB_REF_NAME#v}"
          CARGO_VER="$(cargo metadata --no-deps --format-version=1 \
            | python3 -c "import sys,json; print(json.load(sys.stdin)['packages'][0]['version'])")"
          if [ "$TAG" != "$CARGO_VER" ]; then
            echo "Tag version ($TAG) does not match Cargo.toml version ($CARGO_VER)"
            exit 1
          fi

      - name: Update lock file
        run: cargo update --workspace

      - name: Publish
        env:
          CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }}
        run: cargo publish --locked

  # ─── Deploy GitHub Pages (landing page + mdBook documentation) ──────────────
  deploy-pages:
    name: Deploy GitHub Pages
    runs-on: ubuntu-latest
    needs: [test]
    if: github.ref == 'refs/heads/main' || github.ref == 'refs/heads/master'
    permissions:
      pages: write
      id-token: write
    environment:
      name: github-pages
      url: ${{ steps.deploy.outputs.page_url }}

    steps:
      - uses: actions/checkout@v6

      - name: Install mdBook
        run: cargo install mdbook --quiet

      - name: Build documentation
        run: |
          cd docs/guide
          mdbook build

      - name: Prepare Pages artifact
        run: |
          # Copy mdBook output into docs/ for unified deployment
          cp -r docs/guide/book docs/documentation

      - name: Setup Pages
        uses: actions/configure-pages@v5

      - name: Upload Pages artifact
        uses: actions/upload-pages-artifact@v4
        with:
          path: docs

      - name: Deploy to GitHub Pages
        id: deploy
        uses: actions/deploy-pages@v4
