# Copyright (c) Abstract Machines
# SPDX-License-Identifier: Apache-2.0

name: CI Pipeline

on:
  push:
    branches:
      - main
    paths-ignore:
      - '**.md'
      - 'docs/**'
      - 'LICENSE'
      - 'MAINTAINERS'
      - 'CODEOWNERS'
  pull_request:
    branches:
      - main

concurrency:
  group: ${{ github.workflow }}-${{ github.ref }}
  cancel-in-progress: true

jobs:
  check-certs:
    name: Check Certs
    runs-on: ubuntu-latest
    steps:
      - name: Checkout Code
        uses: actions/checkout@v6

      - name: Fetch Certs
        run: |
          make fetch_certs
          if [[ -n $(git status --porcelain docker/addons/certs) ]]; then
            echo "Certs docker file is not up to date. Please update it"
            git diff docker/addons/certs
            exit 1
          else
            exit 0
          fi

  lint-proto:
    name: Lint Proto
    runs-on: ubuntu-latest
    steps:
      - name: Checkout Code
        uses: actions/checkout@v6

      - name: Get Go version from go.mod
        id: go-version
        run: echo "version=$(grep '^go ' go.mod | awk '{print $2}')" >> $GITHUB_OUTPUT

      - name: Setup Go
        uses: actions/setup-go@v6
        with:
          go-version: ${{ steps.go-version.outputs.version }}
          cache-dependency-path: "go.sum"

      - name: Install protolint
        run: |
          go install github.com/yoheimuta/protolint/cmd/protolint@latest

      - name: Lint Protobuf Files
        run: |
          protolint .

  lint-and-build:
    needs: [check-certs, lint-proto]
    uses: ./.github/workflows/lint-and-build.yaml



  detect-changes:
    name: Detect Changes
    runs-on: ubuntu-latest
    outputs:
      modules: ${{ steps.set-matrix.outputs.modules }}
      workflow_changed: ${{ steps.changes.outputs.workflow }}
    steps:
      - name: Checkout
        uses: actions/checkout@v6
        with:
          fetch-depth: 0

      - name: Check for changes in specific paths
        uses: dorny/paths-filter@v3
        id: changes
        with:
          filters: |
            workflow:
              - ".github/workflows/tests.yaml"

            auth:
              - "auth/**"
              - "cmd/auth/**"
              - "auth.proto"
              - "auth.pb.go"
              - "auth_grpc.pb.go"
              - "pkg/ulid/**"
              - "pkg/uuid/**"

            channels:
              - "channels/**"
              - "cmd/channels/**"
              - "auth.pb.go"
              - "auth_grpc.pb.go"
              - "auth/**"
              - "pkg/sdk/**"
              - "clients/api/grpc/**"
              - "groups/api/grpc/**"
              - "domains/api/grpc/**"
              - "internal/grpc/**"

            cli:
              - "cli/**"
              - "cmd/cli/**"
              - "pkg/sdk/**"

            clients:
              - "clients/**"
              - "cmd/clients/**"
              - "auth.pb.go"
              - "auth_grpc.pb.go"
              - "auth/**"
              - "pkg/ulid/**"
              - "pkg/uuid/**"
              - "pkg/events/**"
              - "groups/api/grpc/**"
              - "channels/api/grpc/**"
              - "domains/api/grpc/**"
              - "internal/grpc/**"

            coap:
              - "coap/**"
              - "cmd/coap/**"
              - "auth.pb.go"
              - "auth_grpc.pb.go"
              - "clients/**"
              - "pkg/messaging/**"

            domains:
              - "domains/**"
              - "cmd/domains/**"
              - "auth.pb.go"
              - "auth_grpc.pb.go"
              - "auth/**"
              - "internal/grpc/**"

            groups:
              - "groups/**"
              - "cmd/groups/**"
              - "auth.pb.go"
              - "auth_grpc.pb.go"
              - "auth/**"
              - "pkg/ulid/**"
              - "pkg/uuid/**"
              - "clients/api/grpc/**"
              - "channels/api/grpc/**"
              - "domains/api/grpc/**"
              - "internal/grpc/**"

            http:
              - "http/**"
              - "cmd/http/**"
              - "auth.pb.go"
              - "auth_grpc.pb.go"
              - "clients/**"
              - "pkg/messaging/**"
              - "logger/**"

            internal:
              - "internal/**"

            journal:
              - "journal/**"
              - "cmd/journal/**"
              - "auth.pb.go"
              - "auth_grpc.pb.go"
              - "auth/**"
              - "pkg/events/**"

            logger:
              - "logger/**"

            mqtt:
              - "mqtt/**"
              - "cmd/mqtt/**"
              - "auth.pb.go"
              - "auth_grpc.pb.go"
              - "clients/**"
              - "pkg/messaging/**"
              - "logger/**"
              - "pkg/events/**"

            pkg-errors:
              - "pkg/errors/**"

            pkg-events:
              - "pkg/events/**"
              - "pkg/messaging/**"

            pkg-grpcclient:
              - "pkg/grpcclient/**"

            pkg-messaging:
              - "pkg/messaging/**"

            pkg-sdk:
              - "pkg/sdk/**"
              - "pkg/errors/**"
              - "pkg/groups/**"
              - "auth/**"
              - "http/**"
              - "internal/*"
              - "clients/**"
              - "users/**"
              - "channels/**"
              - "domains/**"
              - "groups/**"
              - "journal/**"
              - "api/http/**"

            pkg-transformers:
              - "pkg/transformers/**"

            pkg-ulid:
              - "pkg/ulid/**"

            pkg-uuid:
              - "pkg/uuid/**"

            users:
              - "users/**"
              - "cmd/users/**"
              - "auth.pb.go"
              - "auth_grpc.pb.go"
              - "auth/**"
              - "pkg/ulid/**"
              - "pkg/uuid/**"
              - "pkg/events/**"

            notifications:
              - "notifications/**"
              - "cmd/notifications/**"
              - "auth.pb.go"
              - "auth_grpc.pb.go"
              - "consumers/notifier.go"
              - "pkg/events/**"

            api:
              - "api/**"

            consumers:
              - "consumers/**"

            readers:
              - "readers/**"

      - name: Set matrix for changed modules
        id: set-matrix
        run: |
          modules=()

          if [[ "${{ steps.changes.outputs.workflow }}" == "true" || "${{ steps.changes.outputs.pkg-errors }}" == "true" ]]; then
            # If workflow or pkg/errors changed, test everything
            modules=("auth" "channels" "cli" "clients" "coap" "domains" "groups" "http" "internal" "journal" "logger" "mqtt" "pkg-errors" "pkg-events" "pkg-grpcclient" "pkg-messaging" "pkg-sdk" "pkg-transformers" "pkg-ulid" "pkg-uuid" "users" "notifications" "api" "consumers" "readers")
          else
            # Add only changed modules
            [[ "${{ steps.changes.outputs.auth }}" == "true" ]] && modules+=("auth")
            [[ "${{ steps.changes.outputs.channels }}" == "true" ]] && modules+=("channels")
            [[ "${{ steps.changes.outputs.cli }}" == "true" ]] && modules+=("cli")
            [[ "${{ steps.changes.outputs.clients }}" == "true" ]] && modules+=("clients")
            [[ "${{ steps.changes.outputs.coap }}" == "true" ]] && modules+=("coap")
            [[ "${{ steps.changes.outputs.domains }}" == "true" ]] && modules+=("domains")
            [[ "${{ steps.changes.outputs.groups }}" == "true" ]] && modules+=("groups")
            [[ "${{ steps.changes.outputs.http }}" == "true" ]] && modules+=("http")
            [[ "${{ steps.changes.outputs.internal }}" == "true" ]] && modules+=("internal")
            [[ "${{ steps.changes.outputs.journal }}" == "true" ]] && modules+=("journal")
            [[ "${{ steps.changes.outputs.logger }}" == "true" ]] && modules+=("logger")
            [[ "${{ steps.changes.outputs.mqtt }}" == "true" ]] && modules+=("mqtt")
            [[ "${{ steps.changes.outputs.pkg-errors }}" == "true" ]] && modules+=("pkg-errors")
            [[ "${{ steps.changes.outputs.pkg-events }}" == "true" ]] && modules+=("pkg-events")
            [[ "${{ steps.changes.outputs.pkg-grpcclient }}" == "true" ]] && modules+=("pkg-grpcclient")
            [[ "${{ steps.changes.outputs.pkg-messaging }}" == "true" ]] && modules+=("pkg-messaging")
            [[ "${{ steps.changes.outputs.pkg-sdk }}" == "true" ]] && modules+=("pkg-sdk")
            [[ "${{ steps.changes.outputs.pkg-transformers }}" == "true" ]] && modules+=("pkg-transformers")
            [[ "${{ steps.changes.outputs.pkg-ulid }}" == "true" ]] && modules+=("pkg-ulid")
            [[ "${{ steps.changes.outputs.pkg-uuid }}" == "true" ]] && modules+=("pkg-uuid")
            [[ "${{ steps.changes.outputs.users }}" == "true" ]] && modules+=("users")
            [[ "${{ steps.changes.outputs.notifications }}" == "true" ]] && modules+=("notifications")
            [[ "${{ steps.changes.outputs.api }}" == "true" ]] && modules+=("api")
            [[ "${{ steps.changes.outputs.consumers }}" == "true" ]] && modules+=("consumers")
            [[ "${{ steps.changes.outputs.readers }}" == "true" ]] && modules+=("readers")
          fi

          # Convert to JSON array
          json_modules=$(printf '%s\n' "${modules[@]}" | jq -R . | jq -s -c .)
          echo "modules=$json_modules" >> $GITHUB_OUTPUT
          echo "Testing modules: $json_modules"

  run-tests:
    name: Test ${{ matrix.module }}
    runs-on: ubuntu-latest
    needs: [lint-and-build, detect-changes]
    if: needs.detect-changes.outputs.modules != '[]'
    strategy:
      fail-fast: true
      max-parallel: 20
      matrix:
        module: ${{ fromJSON(needs.detect-changes.outputs.modules) }}

    steps:
      - name: Checkout
        uses: actions/checkout@v6

      - name: Get Go version from go.mod
        id: go-version
        run: echo "version=$(grep '^go ' go.mod | awk '{print $2}')" >> $GITHUB_OUTPUT

      - name: Setup Go
        uses: actions/setup-go@v6
        with:
          go-version: ${{ steps.go-version.outputs.version }}
          cache-dependency-path: "go.sum"

      - name: Verify dependencies
        run: |
          go mod download
          go mod verify

      - name: Run ${{ matrix.module }} tests
        run: |
          # Map module names to directories
          case "${{ matrix.module }}" in
            pkg-errors) dir="pkg/errors" ;;
            pkg-events) dir="pkg/events" ;;
            pkg-grpcclient) dir="pkg/grpcclient" ;;
            pkg-messaging) dir="pkg/messaging" ;;
            pkg-sdk) dir="pkg/sdk" ;;
            pkg-transformers) dir="pkg/transformers" ;;
            pkg-ulid) dir="pkg/ulid" ;;
            pkg-uuid) dir="pkg/uuid" ;;
            *) dir="${{ matrix.module }}" ;;
          esac

          go test -mod=readonly --race -v -count=1 -failfast -coverprofile=coverage-${{ matrix.module }}.out ./$dir/...

      - name: Upload coverage
        uses: actions/upload-artifact@v7
        with:
          name: coverage-${{ matrix.module }}
          path: coverage-${{ matrix.module }}.out
          retention-days: 1

  upload-coverage:
    name: Upload Coverage
    runs-on: ubuntu-latest
    needs: run-tests
    if: always() && needs.run-tests.result != 'cancelled'
    steps:
      - name: Checkout
        uses: actions/checkout@v6

      - name: Download all coverage artifacts
        uses: actions/download-artifact@v8
        with:
          pattern: coverage-*
          path: coverage
          merge-multiple: true

      - name: Upload coverage to Codecov
        uses: codecov/codecov-action@v5
        with:
          token: ${{ secrets.CODECOV }}
          directory: ./coverage
          files: ./coverage/*.out
          fail_ci_if_error: false
          verbose: true
