name: Firmware CI

on:
  push:
    branches:
      - '**'
    tags:
      # ESP32 firmware release tags — build + version-consistency guard (RuView#505).
      - 'v*-esp32'
    paths:
      - 'firmware/**'
      - '.github/workflows/firmware-ci.yml'
  pull_request:
    paths:
      - 'firmware/**'
      - '.github/workflows/firmware-ci.yml'

jobs:
  version-guard:
    name: Verify version.txt matches release tag
    runs-on: ubuntu-latest
    if: github.ref_type == 'tag'
    steps:
      - uses: actions/checkout@v4
      - name: Check firmware version.txt == tag
        run: |
          # Tag form: vX.Y.Z-esp32  →  expect version.txt to contain X.Y.Z
          TAG="${GITHUB_REF_NAME}"
          EXPECTED="${TAG#v}"
          EXPECTED="${EXPECTED%-esp32}"
          ACTUAL="$(tr -d '[:space:]' < firmware/esp32-csi-node/version.txt)"
          echo "Tag: $TAG  →  expected version.txt: $EXPECTED  |  actual: $ACTUAL"
          if [ "$EXPECTED" != "$ACTUAL" ]; then
            echo "::error::firmware/esp32-csi-node/version.txt is '$ACTUAL' but tag '$TAG' expects '$EXPECTED'."
            echo "::error::Bump version.txt and re-tag so esp_app_get_description()->version is correct (RuView#505)."
            exit 1
          fi
          echo "version.txt matches the release tag."

  build:
    name: Build firmware (${{ matrix.target }} / ${{ matrix.variant }})
    runs-on: ubuntu-latest
    container:
      image: espressif/idf:v5.4
    strategy:
      fail-fast: false
      matrix:
        include:
          - variant: 8mb
            target: esp32s3
            sdkconfig: sdkconfig.defaults
            partition_table_name: partitions_display.csv
            size_limit_kb: 1100
            artifact_app: esp32-csi-node.bin
            artifact_pt: partition-table.bin
          - variant: 4mb
            target: esp32s3
            sdkconfig: sdkconfig.defaults.4mb
            partition_table_name: partitions_4mb.csv
            size_limit_kb: 1100
            artifact_app: esp32-csi-node-4mb.bin
            artifact_pt: partition-table-4mb.bin
          # ADR-110: ESP32-C6 research target (Wi-Fi 6 / 802.15.4 / TWT / LP-core)
          - variant: c6-4mb
            target: esp32c6
            sdkconfig: sdkconfig.defaults
            partition_table_name: partitions_4mb.csv
            size_limit_kb: 1100
            artifact_app: esp32-csi-node-c6.bin
            artifact_pt: partition-table-c6.bin

    steps:
      - uses: actions/checkout@v4

      - name: Build firmware (${{ matrix.variant }})
        working-directory: firmware/esp32-csi-node
        run: |
          . $IDF_PATH/export.sh
          # 4mb variant supplies its own sdkconfig.defaults overlay.
          # c6-4mb variant relies on the auto-applied sdkconfig.defaults.esp32c6
          # overlay (ESP-IDF auto-loads sdkconfig.defaults.$TARGET when present).
          if [ "${{ matrix.variant }}" = "4mb" ]; then
            cp "${{ matrix.sdkconfig }}" sdkconfig.defaults
          fi
          idf.py set-target ${{ matrix.target }}
          idf.py build

      - name: Build and run host-side ADR-110 unit tests
        if: matrix.variant == 'c6-4mb'
        working-directory: firmware/esp32-csi-node/test
        run: |
          make test_adr110
          ./test_adr110

      - name: Verify binary size (< ${{ matrix.size_limit_kb }} KB gate)
        working-directory: firmware/esp32-csi-node
        run: |
          BIN=build/esp32-csi-node.bin
          SIZE=$(stat -c%s "$BIN")
          MAX=$((${{ matrix.size_limit_kb }} * 1024))
          echo "Binary size: $SIZE bytes ($(( SIZE / 1024 )) KB)"
          echo "Size limit:  $MAX bytes (${{ matrix.size_limit_kb }} KB)"
          if [ "$SIZE" -gt "$MAX" ]; then
            echo "::error::Firmware binary exceeds ${{ matrix.size_limit_kb }} KB size gate ($SIZE > $MAX)"
            exit 1
          fi
          echo "Binary size OK: $SIZE <= $MAX"

      - name: Verify flash image integrity
        working-directory: firmware/esp32-csi-node
        run: |
          ERRORS=0
          BIN=build/esp32-csi-node.bin

          if [ ! -s "$BIN" ]; then
            echo "::error::Binary not found or empty"
            exit 1
          fi

          PT=build/partition_table/partition-table.bin
          if [ -f "$PT" ]; then
            MAGIC=$(od -A n -t x1 -N 2 "$PT" | tr -d ' ')
            if [ "$MAGIC" != "aa50" ]; then
              echo "::warning::Partition table magic mismatch: $MAGIC (expected aa50)"
              ERRORS=$((ERRORS + 1))
            fi
          fi

          BL=build/bootloader/bootloader.bin
          if [ ! -s "$BL" ]; then
            echo "::warning::Bootloader binary missing or empty"
            ERRORS=$((ERRORS + 1))
          fi

          NONZERO=$(od -A n -t x1 -N 1024 "$BIN" | tr -d ' f\n' | wc -c)
          if [ "$NONZERO" -lt 100 ]; then
            echo "::error::Binary appears to be mostly padding (non-zero chars: $NONZERO)"
            ERRORS=$((ERRORS + 1))
          fi

          if [ "$ERRORS" -gt 0 ]; then
            echo "::warning::Flash image verification completed with $ERRORS warning(s)"
          else
            echo "Flash image integrity verified"
          fi

      - name: Stage release binaries with variant-specific names
        working-directory: firmware/esp32-csi-node
        run: |
          mkdir -p release-staging
          cp build/esp32-csi-node.bin                      release-staging/${{ matrix.artifact_app }}
          cp build/partition_table/partition-table.bin     release-staging/${{ matrix.artifact_pt }}
          if [ "${{ matrix.variant }}" = "8mb" ]; then
            cp build/bootloader/bootloader.bin             release-staging/bootloader.bin
            cp build/ota_data_initial.bin                  release-staging/ota_data_initial.bin
          fi
          ls -la release-staging/

      - name: Check QEMU ESP32-S3 support status
        run: |
          echo "::notice::ESP32-S3 QEMU support is experimental in ESP-IDF v5.4. "
          echo "Full smoke testing requires QEMU 8.2+ with xtensa-esp32s3 target."
          echo "See: https://github.com/espressif/qemu/wiki"

      - name: Upload firmware artifact (${{ matrix.variant }})
        uses: actions/upload-artifact@v4
        with:
          name: esp32-csi-node-firmware-${{ matrix.variant }}
          path: firmware/esp32-csi-node/release-staging/
          retention-days: 90
