name: Contributor Reputation Check

on:
  pull_request_target:
    types: [opened, synchronize, reopened, edited, ready_for_review]
  issues:
    types: [opened, reopened, edited]

permissions:
  contents: read
  issues: write
  pull-requests: write

jobs:
  check:
    runs-on: ubuntu-latest
    if: >-
      github.actor != 'dependabot[bot]' &&
      github.actor != 'github-actions[bot]' &&
      github.actor != 'copilot-swe-agent[bot]'
    steps:
      - name: Checkout code
        uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1
        with:
          fetch-depth: 0

      - name: Setup Python
        uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0
        with:
          python-version: "3.12"

      - name: Fetch AGT check scripts
        env:
          AGT_REF: v3.4.0
        run: |
          mkdir -p /tmp/agt
          curl -fsSL "https://raw.githubusercontent.com/microsoft/agent-governance-toolkit/${AGT_REF}/scripts/contributor_check.py" \
            -o /tmp/agt/contributor_check.py
          curl -fsSL "https://raw.githubusercontent.com/microsoft/agent-governance-toolkit/${AGT_REF}/scripts/credential_audit.py" \
            -o /tmp/agt/credential_audit.py

      - name: Determine author
        id: author
        run: |
          if [ "${{ github.event_name }}" = "pull_request_target" ]; then
            echo "username=${{ github.event.pull_request.user.login }}" >> "$GITHUB_OUTPUT"
            echo "number=${{ github.event.pull_request.number }}" >> "$GITHUB_OUTPUT"
            echo "type=pr" >> "$GITHUB_OUTPUT"
          else
            echo "username=${{ github.event.issue.user.login }}" >> "$GITHUB_OUTPUT"
            echo "number=${{ github.event.issue.number }}" >> "$GITHUB_OUTPUT"
            echo "type=issue" >> "$GITHUB_OUTPUT"
          fi

      - name: Run profile check
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
        run: |
          set +e
          python3 /tmp/agt/contributor_check.py \
            --username "${{ steps.author.outputs.username }}" \
            --repo "${{ github.repository }}" \
            --json > /tmp/profile.json 2>/tmp/profile.log
          status=$?
          set -e
          if [ "$status" -ne 0 ] && [ ! -s /tmp/profile.json ]; then
            echo "::warning::Profile check failed"
            if [ -s /tmp/profile.log ]; then
              sed -n '1,120p' /tmp/profile.log
            fi
          fi

      - name: Run credential audit
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
        run: |
          set +e
          python3 /tmp/agt/credential_audit.py \
            --username "${{ steps.author.outputs.username }}" \
            --repo "${{ github.repository }}" \
            --json > /tmp/cred.json 2>/tmp/cred.log
          status=$?
          set -e
          if [ "$status" -ne 0 ] && [ ! -s /tmp/cred.json ]; then
            echo "::warning::Credential audit failed"
            if [ -s /tmp/cred.log ]; then
              sed -n '1,120p' /tmp/cred.log
            fi
          fi

      - name: Dump check outputs
        if: always()
        run: |
          dump_json() {
            label="$1"
            file="$2"
            log_file="$3"

            echo "::group::${label} JSON"
            if [ -s "$file" ]; then
              if jq . "$file"; then
                :
              else
                cat "$file"
              fi
            else
              echo "<missing>"
            fi
            echo "::endgroup::"

            if [ -s "$log_file" ]; then
              echo "::group::${label} stderr"
              sed -n '1,120p' "$log_file"
              echo "::endgroup::"
            fi
          }

          dump_json "Profile check" /tmp/profile.json /tmp/profile.log
          dump_json "Credential audit" /tmp/cred.json /tmp/cred.log

      - name: Resolve check risks
        id: results
        run: |
          extract_risk() {
            file="$1"
            fallback="$2"

            if [ ! -s "$file" ]; then
              echo "$fallback"
              return
            fi

            risk=$(
              jq -r '
                [
                  .risk,
                  .overall_risk,
                  .overallRisk,
                  .result.risk,
                  .result.overall_risk,
                  .result.overallRisk
                ]
                | map(select(. != null and . != ""))
                | .[0] // empty
              ' "$file" 2>/dev/null \
                | tr "[:lower:]" "[:upper:]" \
                | tr -d "\r"
            )

            case "$risk" in
              HIGH|MEDIUM|LOW|NONE|UNKNOWN) echo "$risk" ;;
              "") echo "$fallback" ;;
              *) echo "$fallback" ;;
            esac
          }

          profile_risk=$(extract_risk /tmp/profile.json UNKNOWN)
          credential_risk=$(extract_risk /tmp/cred.json UNKNOWN)

          echo "profile=$profile_risk" >> "$GITHUB_OUTPUT"
          echo "credential=$credential_risk" >> "$GITHUB_OUTPUT"

      - name: Compute overall risk
        id: overall
        run: |
          risk_to_num() {
            case "$1" in
              HIGH) echo 3 ;;
              MEDIUM) echo 2 ;;
              LOW|NONE) echo 1 ;;
              UNKNOWN|"") echo 0 ;;
              *) echo 0 ;;
            esac
          }
          p=$(risk_to_num "${{ steps.results.outputs.profile }}")
          c=$(risk_to_num "${{ steps.results.outputs.credential }}")
          max=$p; [ "$c" -gt "$max" ] && max=$c
          case "$max" in
            3) r="HIGH" ;;
            2) r="MEDIUM" ;;
            1) r="LOW" ;;
            *) r="UNKNOWN" ;;
          esac
          echo "risk=$r" >> "$GITHUB_OUTPUT"

      - name: Sync risk comment
        env:
          GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
        run: |
          number="${{ steps.author.outputs.number }}"
          risk="${{ steps.overall.outputs.risk }}"
          profile="${{ steps.results.outputs.profile }}"
          cred="${{ steps.results.outputs.credential }}"
          marker="<!-- agt-contributor-check -->"
          comment_ids=$(
            gh api "repos/${{ github.repository }}/issues/$number/comments" --paginate \
              | jq -r --arg marker "$marker" '.[] | select((.user.login // "") == "github-actions[bot]" and ((.body // "") | contains($marker))) | .id'
          )
          comment_id=$(printf "%s\n" "$comment_ids" | sed -n '1p')

          if [ "$risk" != "MEDIUM" ] && [ "$risk" != "HIGH" ]; then
            if [ -n "$comment_id" ]; then
              # Keep one canonical comment thread by removing all matching comments
              # when risk drops below MEDIUM.
              while IFS= read -r id; do
                [ -z "$id" ] && continue
                gh api --method DELETE "repos/${{ github.repository }}/issues/comments/$id" \
                  || echo "Comment $id could not be deleted; continuing because the comment may have already been removed or changed."
              done <<< "$comment_ids"
            fi
            exit 0
          fi

          if [ "$risk" = "HIGH" ]; then icon="🔴"; else icon="🟡"; fi

          body=$(cat <<EOF
          $marker
          $icon **Contributor Reputation Check: $risk risk**

          | Check | Risk |
          |-------|------|
          | Profile | $profile |
          | Credential audit | $cred |

          Maintainers: please review this contributor before merging.
          See the [workflow run](${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}) for full details.
          *Automated check powered by [AGT](https://github.com/microsoft/agent-governance-toolkit).*
          EOF
          )

          if [ -n "$comment_id" ]; then
            gh api --method PATCH "repos/${{ github.repository }}/issues/comments/$comment_id" -f body="$body"
            # Clean up any stale duplicates after updating the canonical comment.
            printf "%s\n" "$comment_ids" | sed '1d' | while IFS= read -r id; do
              [ -z "$id" ] && continue
              gh api --method DELETE "repos/${{ github.repository }}/issues/comments/$id" >/dev/null 2>&1 || true
            done
          else
            gh api --method POST "repos/${{ github.repository }}/issues/$number/comments" -f body="$body"
          fi

      - name: Sync risk label
        env:
          GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
        run: |
          number="${{ steps.author.outputs.number }}"
          risk="${{ steps.overall.outputs.risk }}"

          for label in needs-review:MEDIUM needs-review:HIGH; do
            if [ "$label" != "needs-review:$risk" ]; then
              gh api --method DELETE "repos/${{ github.repository }}/issues/$number/labels/$label" >/dev/null 2>&1 || true
            fi
          done

          if [ "$risk" != "MEDIUM" ] && [ "$risk" != "HIGH" ]; then
            exit 0
          fi

          gh label create "needs-review:$risk" \
            --description "Contributor reputation check flagged $risk risk" \
            --color "FFA500" --force 2>/dev/null || true

          gh api --method POST "repos/${{ github.repository }}/issues/$number/labels" \
            -f labels[]="needs-review:$risk" >/dev/null

      - name: Job summary
        if: always()
        run: |
          risk="${{ steps.overall.outputs.risk }}"
          case "$risk" in HIGH) icon="🔴" ;; MEDIUM) icon="🟡" ;; LOW) icon="✅" ;; *) icon="❓" ;; esac
          {
            echo "## $icon Contributor Check: \`${{ steps.author.outputs.username }}\`"
            echo "| Check | Risk |"
            echo "|-------|------|"
            echo "| Profile | ${{ steps.results.outputs.profile }} |"
            echo "| Credential | ${{ steps.results.outputs.credential }} |"
            echo "| **Overall** | **$risk** |"
          } >> "$GITHUB_STEP_SUMMARY"
