name: Unit Tests

on:
  push:
    branches: ["*"]
  pull_request:
    branches: ["*"]

jobs:
  # Build frontend once and share across all jobs
  build-frontend:
    name: Build Frontend
    runs-on: ubuntu-latest
    steps:
      - name: Checkout code
        uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5  # v4.3.1

      - name: Setup Node.js
        uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020  # v4.4.0
        with:
          node-version: '20'
          cache: 'npm'
          cache-dependency-path: frontend/package-lock.json

      - name: Install frontend dependencies
        run: cd frontend && npm ci

      - name: Build frontend
        run: cd frontend && npm run build

      - name: Upload frontend artifact
        uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02  # v4.6.2
        with:
          name: frontend-dist
          path: frontend/dist/
          retention-days: 1

  verify-oas:
    name: Verify OpenAPI Artifacts
    runs-on: ubuntu-latest
    steps:
      - name: Checkout code
        uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5  # v4.3.1

      - name: Set up Go
        uses: actions/setup-go@40f1582b2485089dde7abd97c1529aa768e1baff  # v5.6.0
        with:
          go-version: "1.25"

      - name: Install swag
        run: go install github.com/swaggo/swag/v2/cmd/swag@v2.0.0-rc4

      - name: Regenerate and verify OpenAPI spec
        run: scripts/verify-oas.sh

  test:
    name: Unit Tests
    needs: [build-frontend, verify-oas]
    runs-on: ${{ matrix.os }}

    env:
      GO111MODULE: "on"

    strategy:
      matrix:
        # On PRs: test only ubuntu-latest. On main/push: test all OSes.
        # Go is pinned to "1.25" because go.mod requires go 1.25.5 — older
        # toolchains can't build the module, and auto-downloading 1.25.x under
        # setup-go's module cache collides ("tar: ... File exists").
        os: ${{ github.event_name == 'pull_request' && fromJSON('["ubuntu-latest"]') || fromJSON('["ubuntu-latest", "macos-latest", "windows-latest"]') }}
        go-version: ["1.25"]

    steps:
      - name: Checkout code
        uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5  # v4.3.1

      - name: Set up Go
        uses: actions/setup-go@40f1582b2485089dde7abd97c1529aa768e1baff  # v5.6.0
        with:
          go-version: ${{ matrix.go-version }}
          cache: true

      - name: Download dependencies
        run: go mod download

      - name: Verify dependencies
        run: go mod verify

      - name: Download frontend artifact
        uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093  # v4.3.0
        with:
          name: frontend-dist
          path: frontend/dist

      - name: Copy frontend dist for embedding
        if: matrix.os != 'windows-latest'
        run: |
          rm -rf web/frontend
          mkdir -p web/frontend
          cp -r frontend/dist web/frontend/

      - name: Copy frontend dist for embedding (Windows)
        if: matrix.os == 'windows-latest'
        shell: pwsh
        run: |
          if (Test-Path web/frontend) { Remove-Item -Recurse -Force web/frontend }
          New-Item -ItemType Directory -Force -Path web/frontend
          Copy-Item -Recurse frontend/dist web/frontend/

      - name: Run unit tests (Windows)
        if: matrix.os == 'windows-latest'
        shell: pwsh
        run: go test -v -race -timeout 5m '-coverprofile=coverage.out' -covermode=atomic '-skip=E2E|Binary|MCPProtocol|TestInfoEndpoint|TestGracefulShutdownNoPanic|TestSocketInfoEndpoint' ./...

      - name: Run unit tests (Unix)
        if: matrix.os != 'windows-latest'
        run: go test -v -race -coverprofile=coverage.out -skip "E2E|Binary|MCPProtocol|TestInfoEndpoint|TestGracefulShutdownNoPanic|TestSocketInfoEndpoint" ./...

      - name: Generate coverage report (Windows)
        if: matrix.os == 'windows-latest'
        shell: pwsh
        run: go tool cover '-html=coverage.out' -o coverage.html

      - name: Generate coverage report (Unix)
        if: matrix.os != 'windows-latest'
        run: go tool cover -html=coverage.out -o coverage.html

      - name: Upload coverage to Codecov
        if: matrix.os == 'ubuntu-latest' && matrix.go-version == '1.25'
        uses: codecov/codecov-action@e79a6962e0d4c0c17b229090214935d2e33f8354  # v6.0.1
        with:
          file: ./coverage.out
          flags: unittests
          name: codecov-umbrella
          fail_ci_if_error: false

      - name: Upload coverage artifacts
        if: matrix.os == 'ubuntu-latest' && matrix.go-version == '1.25'
        uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02  # v4.6.2
        with:
          name: coverage-report
          path: |
            coverage.out
            coverage.html

  lint:
    name: Lint
    needs: [build-frontend, verify-oas]
    runs-on: ubuntu-latest

    env:
      GO111MODULE: "on"

    steps:
      - name: Checkout code
        uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5  # v4.3.1

      - name: Set up Go
        uses: actions/setup-go@40f1582b2485089dde7abd97c1529aa768e1baff  # v5.6.0
        with:
          go-version: "1.25"

      - name: Download frontend artifact
        uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093  # v4.3.0
        with:
          name: frontend-dist
          path: frontend/dist

      - name: Copy frontend dist for embedding
        run: |
          rm -rf web/frontend
          mkdir -p web/frontend
          cp -r frontend/dist web/frontend/

      - name: golangci-lint
        uses: golangci/golangci-lint-action@9fae48acfc02a90574d7c304a1758ef9895495fa  # v7.0.1
        with:
          version: v2.6.2
          args: --config .github/.golangci.yml --timeout=5m

  build:
    name: Build
    needs: [build-frontend, verify-oas]
    runs-on: ${{ matrix.os }}

    env:
      GO111MODULE: "on"

    strategy:
      matrix:
        # On PRs: build only on ubuntu-latest
        # On main/push: build on all platforms
        os: ${{ github.event_name == 'pull_request' && fromJSON('["ubuntu-latest"]') || fromJSON('["ubuntu-latest", "macos-latest", "windows-latest"]') }}

    steps:
      - name: Checkout code
        uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5  # v4.3.1

      - name: Set up Go
        uses: actions/setup-go@40f1582b2485089dde7abd97c1529aa768e1baff  # v5.6.0
        with:
          go-version: "1.25"
          cache: true

      - name: Download frontend artifact
        uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093  # v4.3.0
        with:
          name: frontend-dist
          path: frontend/dist

      - name: Copy frontend dist for embedding (Windows)
        if: matrix.os == 'windows-latest'
        shell: pwsh
        run: |
          if (Test-Path web/frontend) { Remove-Item -Recurse -Force web/frontend }
          New-Item -ItemType Directory -Force -Path web/frontend
          Copy-Item -Recurse frontend/dist web/frontend/

      - name: Copy frontend dist for embedding (Unix)
        if: matrix.os != 'windows-latest'
        run: |
          rm -rf web/frontend
          mkdir -p web/frontend
          cp -r frontend/dist web/frontend/

      - name: Build (Windows)
        if: matrix.os == 'windows-latest'
        run: go build -v -o mcpproxy.exe ./cmd/mcpproxy

      - name: Build (Unix)
        if: matrix.os != 'windows-latest'
        run: go build -v ./cmd/mcpproxy

      - name: Build for different architectures (Linux only)
        if: matrix.os == 'ubuntu-latest'
        run: |
          CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -o mcpproxy-linux-amd64 ./cmd/mcpproxy
          CGO_ENABLED=0 GOOS=linux GOARCH=arm64 go build -o mcpproxy-linux-arm64 ./cmd/mcpproxy
          CGO_ENABLED=0 GOOS=darwin GOARCH=amd64 go build -o mcpproxy-darwin-amd64 ./cmd/mcpproxy || echo "macOS amd64 cross-compilation may fail (expected)"
          CGO_ENABLED=0 GOOS=darwin GOARCH=arm64 go build -o mcpproxy-darwin-arm64 ./cmd/mcpproxy || echo "macOS arm64 cross-compilation may fail (expected)"
          CGO_ENABLED=0 GOOS=windows GOARCH=amd64 go build -o mcpproxy-windows-amd64.exe ./cmd/mcpproxy

      - name: Upload build artifacts
        if: matrix.os == 'ubuntu-latest'
        uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02  # v4.6.2
        with:
          name: binaries
          path: mcpproxy-*
