name: Pull Request Check
on:
  pull_request:
  push:
  workflow_dispatch:
permissions:
  contents: read
  pull-requests: read # Use with `only-new-issues` option.
jobs:
  build:
    name: build
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
      - uses: actions/setup-go@v5
        with:
          go-version: "1.21"
      - name: Build
        run: go build -v ./...
  discover-go-modules:
    name: discover go modules
    runs-on: ubuntu-latest
    outputs:
      matrix: ${{ steps.set-matrix.outputs.matrix }}
    steps:
      - uses: actions/checkout@v3
      - id: set-matrix
        shell: bash
        run: |
          set -euo pipefail
          mapfile -t modules < <(find . -name go.mod \
            -not -path "./.resource/*" \
            -not -path "./docs/*" \
            -not -path "./examples/*" \
            -not -path "./test/*" | sort | while IFS= read -r mod; do
              if head -n 5 "${mod}" | grep -q "DO NOT USE!"; then
                continue
              fi
              echo "${mod}"
            done)
          if [ "${#modules[@]}" -eq 0 ]; then
            echo "matrix={\"module\":[]}" >> "$GITHUB_OUTPUT"
            exit 0
          fi
          matrix=$(printf '%s\n' "${modules[@]}" | sed 's|^\./||' | jq -R -s -c 'split("\n") | map(select(length>0))')
          echo "matrix={\"module\":${matrix}}" >> "$GITHUB_OUTPUT"
  test-go-modules:
    name: go test (${{ matrix.module }})
    runs-on: ubuntu-latest
    needs: discover-go-modules
    strategy:
      fail-fast: false
      matrix: ${{ fromJson(needs.discover-go-modules.outputs.matrix) }}
    steps:
      - uses: actions/checkout@v3
      - uses: actions/setup-go@v5
        with:
          go-version: "1.21"
      - name: Prepare module metadata
        id: module-info
        shell: bash
        run: |
          set -euo pipefail
          module='${{ matrix.module }}'
          if [ -z "${module}" ]; then
            echo "empty module path" >&2
            exit 1
          fi
          rel="${module#./}"
          if [ "${rel}" = "go.mod" ]; then
            readable="root"
          else
            readable="${rel%/go.mod}"
          fi
          safe="${readable//\//_}"
          safe="${safe//./_}"
          if [ -z "${safe}" ]; then
            safe="root"
          fi
          echo "safe_name=${safe}" >> "$GITHUB_OUTPUT"
      - name: Test module
        shell: bash
        run: bash .github/scripts/run-go-tests.sh --module "${{ matrix.module }}"
      - name: Prepare coverage artifact
        shell: bash
        run: mv coverage.out "coverage-${{ steps.module-info.outputs.safe_name }}.out"
      - name: Upload coverage artifact
        uses: actions/upload-artifact@v4
        with:
          name: coverage-${{ steps.module-info.outputs.safe_name }}
          path: coverage-${{ steps.module-info.outputs.safe_name }}.out
          if-no-files-found: error
  e2e:
    name: e2e test
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
      - uses: actions/setup-go@v5
        with:
          go-version: "1.21"
      - name: Run e2e tests
        shell: bash
        run: |
          set -euo pipefail
          cd test
          go test ./...
  coverage:
    name: coverage
    runs-on: ubuntu-latest
    needs: test-go-modules
    steps:
      - uses: actions/checkout@v3
      - name: Download coverage artifacts
        uses: actions/download-artifact@v4
        with:
          pattern: coverage-*
          merge-multiple: true
          path: coverage-artifacts
      - name: Combine coverage reports
        shell: bash
        run: |
          set -euo pipefail
          mapfile -t files < <(find coverage-artifacts -maxdepth 1 -type f -name 'coverage-*.out' | sort)
          if [ "${#files[@]}" -eq 0 ]; then
            echo "no coverage files found" >&2
            exit 1
          fi
          cp "${files[0]}" coverage.out
          for file in "${files[@]:1}"; do
            tail -n +2 "${file}" >> coverage.out
          done
      - name: Upload merged coverage artifact
        uses: actions/upload-artifact@v4
        with:
          name: coverage-merged
          path: coverage.out
      - name: Upload coverage reports to Codecov
        uses: codecov/codecov-action@v5
        with:
          files: coverage.out
          flags: unittests
          token: ${{ secrets.CODECOV_TOKEN }}
  golangci:
    name: lint
    runs-on: ubuntu-latest
    steps:
      - uses: actions/setup-go@v5
        with:
          go-version: "1.21"
      - uses: actions/checkout@v3
      - name: golangci-lint
        uses: golangci/golangci-lint-action@v3
        with:
          version: latest
          args: --timeout=10m
      - name: gofmt
        run: |
          echo "::group::Checking for interface{} usage and gofmt formatting"
          files=$(gofmt -r 'interface{} -> any' -l .)
          if [ -n "$files" ]; then
            echo "::error::Found files that need formatting or contain interface{} type usage."
            echo "This check detects both:"
            echo "  - Files using 'interface{}' (should use 'any' in Go 1.18+)"
            echo "  - Files not properly formatted with gofmt"
            echo ""
            echo "Files that need attention:"
            echo "$files"
            echo ""
            echo "You can fix this automatically by running:"
            echo "  gofmt -w -r 'interface{} -> any' ."
            echo "  # This will format files and replace interface{} with any"
            exit 1
          else
            echo "✓ All files are properly formatted and use modern 'any' type"
          fi
          echo "::endgroup::"
      - name: goimports
        run: |
          echo "::group::Checking import formatting with goimports"
          go install golang.org/x/tools/cmd/goimports@latest
          files=$(goimports -l .)
          if [ -n "$files" ]; then
            echo "::error::Found files with incorrect import formatting."
            echo "goimports checks:"
            echo "  - Import statement formatting"
            echo "  - Import grouping (stdlib, external, internal)"
            echo "  - Import sorting within groups"
            echo ""
            echo "Files that need attention:"
            echo "$files"
            echo ""
            echo "You can fix this automatically by running:"
            echo "  go install golang.org/x/tools/cmd/goimports@latest"
            echo "  goimports -w ."
            exit 1
          else
            echo "✓ All files have properly formatted imports"
          fi
          echo "::endgroup::"
      - name: license
        run: |
          echo "::group::Checking license headers in Go files"
          missing_files=""
          while IFS= read -r file; do
            if [ -z "$file" ]; then continue; fi

            # Check for Tencent license header (like artifacts.go format)
            if head -n 10 "$file" | grep -q "Tencent is pleased to support the open source community" && \
               head -n 10 "$file" | grep -q "trpc-agent-go is licensed under the Apache License" && \
               head -n 10 "$file" | grep -q "Apache License Version 2.0."; then
              continue
            fi

            missing_files="${missing_files}${file}\n"
          done < <(git ls-files '*.go' | grep -Ev '(^vendor/|^third_party/|^out/|^build/|stub/)')

          if [ -n "$missing_files" ]; then
            echo "::error::Found Go files missing proper license headers:"
            echo -e "$missing_files"
            echo ""
            echo "Please ensure all Go files have the standard Tencent license header."
            exit 1
          else
            echo "✓ All Go files have proper license headers"
          fi
          echo "::endgroup::"
  check-go-mod-tidy:
    name: check go mod tidy
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
      - uses: actions/setup-go@v5
        with:
          go-version: "1.21"
      - name: Check go.mod and go.sum are tidy
        shell: bash
        run: bash .github/scripts/check-go-mod-tidy.sh
  check-examples:
    name: check examples
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
      - uses: actions/setup-go@v5
        with:
          go-version: "1.21"
      - name: Check examples go.mod, go.sum and build
        run: bash .github/scripts/check-examples.sh
  check-lfs-excluded:
    name: check git lfs excluded
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
      - name: Validate Git LFS files are excluded from published modules
        run: bash .github/scripts/check-lfs-excluded.sh
  check-current-module-sums:
    name: check current module sums
    runs-on: ubuntu-latest
    needs: check-lfs-excluded
    steps:
      - uses: actions/checkout@v3
        with:
          lfs: true
      - uses: actions/setup-go@v5
        with:
          go-version: "1.21"
      - name: Compare module zip sums (VCS vs working tree)
        run: bash .github/scripts/check-current-module-sums.sh
  check-go-sumdb:
    name: check sum.golang.org checksums
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
        with:
          fetch-depth: 0
          fetch-tags: true
      - uses: actions/setup-go@v5
        with:
          go-version: "1.21"
      - name: Validate module checksums via sumdb
        run: bash .github/scripts/check-go-sumdb.sh
  typos:
    name: typos
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
      - name: typos
        uses: crate-ci/typos@master
  go-apidiff:
    if: github.event_name == 'pull_request'
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
        with:
          fetch-depth: 0
      - uses: actions/setup-go@v5
        with:
          go-version: "1.21"
      # Use merge-base to only detect breaking changes introduced by this PR,
      # not changes from main that were merged after the PR branch was created.
      - name: Get merge base
        id: merge-base
        run: |
          MERGE_BASE=$(git merge-base HEAD origin/${{ github.base_ref }})
          echo "sha=$MERGE_BASE" >> "$GITHUB_OUTPUT"
      - uses: joelanford/go-apidiff@main
        with:
          base-ref: ${{ steps.merge-base.outputs.sha }}
  docs-build:
    name: docs-build
    runs-on: ubuntu-latest
    defaults:
      run:
        working-directory: docs
    steps:
      - uses: actions/checkout@v3
      - uses: actions/setup-python@v5
        with:
          python-version: "3.11"
      - name: Install MkDocs dependencies
        run: pip install -r mkdocs/assets/requirements.txt
      - name: Build documentation
        run: mkdocs build --strict
  check-go-mod-version:
    name: check go.mod version
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
        with:
          fetch-depth: 0
          fetch-tags: true
      - uses: actions/setup-go@v5
        with:
          go-version: "1.21"
      - name: Validate go.mod version strings
        shell: bash
        run: bash .github/scripts/check-go-mod-version.sh
