name: GitHubActionsBot
on:
  issue_comment:
    types:
      - created

  # We need to be call in context of the main branch to have write permissions
  # "pull_request" target is called in context of a fork
  # "pull_request_target" is called in context of the repository but not necessary latest main
  workflow_run:
    workflows:
      - PullRequestOpened
    types:
      - completed
env:
  SUPPORTED_COMMANDS: |
    <details>
    <summary> Supported commands </summary>

    Please post this commands in separate comments and only one per comment:
    * `@github-actions run-benchmark` - Run benchmark comparing base and merge commits for this PR
    * `@github-actions publish-pr-on-npm` - Build package from this PR and publish it on NPM
    </details>
permissions: {}
jobs:
  hello-message:
    if: github.event_name == 'workflow_run'
    runs-on: ubuntu-latest
    permissions:
      actions: read # to download event.json
      pull-requests: write # to add comment to pull request
    steps:
      - name: Download event.json
        run: gh run download "$WORKFLOW_ID" --repo "$REPO" --name event.json
        env:
          REPO: ${{ github.repository }}
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
          WORKFLOW_ID: ${{github.event.workflow_run.id}}

      - name: Add comment on PR
        uses: actions/github-script@v6
        with:
          script: |
            const fs = require('node:fs');

            const event = JSON.parse(fs.readFileSync('./event.json', 'utf8'));
            await github.rest.issues.createComment({
              ...context.repo,
              issue_number: event.pull_request.number,
              body:
                `Hi @${event.sender.login}, I'm @github-actions bot happy to help you with this PR 👋\n\n` +
                process.env.SUPPORTED_COMMANDS,
            })

  accept-cmd:
    if: |
      github.event_name == 'issue_comment' &&
      github.event.issue.pull_request &&
      startsWith(github.event.comment.body, '@github-actions ')
    runs-on: ubuntu-latest
    permissions:
      pull-requests: write # to add comment to pull request
    outputs:
      cmd: ${{ steps.parse-cmd.outputs.cmd }}
      pull_request_json: ${{ steps.parse-cmd.outputs.pull_request_json }}
    steps:
      - uses: actions/github-script@v6
        with:
          script: |
            await github.rest.reactions.createForIssueComment({
              ...context.repo,
              comment_id: context.payload.comment.id,
              content: 'eyes',
            });

      - id: parse-cmd
        uses: actions/github-script@v6
        with:
          script: |
            const comment = context.payload.comment.body;
            core.setOutput('cmd', comment.replace('@github-actions ', '').trim());

            const { url } = context.payload.issue.pull_request;
            const { data } = await github.request(url);
            core.setOutput('pull_request_json', JSON.stringify(data, null, 2));

  cmd-publish-pr-on-npm:
    needs: [accept-cmd]
    if: needs.accept-cmd.outputs.cmd == 'publish-pr-on-npm'
    permissions:
      contents: read # for actions/checkout
    uses: ./.github/workflows/cmd-publish-pr-on-npm.yml
    with:
      pull_request_json: ${{ needs.accept-cmd.outputs.pull_request_json }}
    secrets:
      npm_canary_pr_publish_token: ${{ secrets.npm_canary_pr_publish_token }}

  cmd-run-benchmark:
    needs: [accept-cmd]
    if: needs.accept-cmd.outputs.cmd == 'run-benchmark'
    permissions:
      contents: read # for actions/checkout
      actions: read # to list workflow runs
    uses: ./.github/workflows/cmd-run-benchmark.yml
    with:
      pull_request_json: ${{ needs.accept-cmd.outputs.pull_request_json }}

  respond-to-cmd:
    needs:
      - accept-cmd
      - cmd-publish-pr-on-npm
      - cmd-run-benchmark
    if: needs.accept-cmd.result != 'skipped' && always()
    runs-on: ubuntu-latest
    permissions:
      pull-requests: write # to add comment to pull request
    steps:
      - uses: actions/download-artifact@v3
        with:
          name: replyMessage

      - if: failure()
        uses: actions/github-script@v6
        with:
          script: |
            const fs = require('node:fs');

            const needs = JSON.parse(process.env.NEEDS);

            let allSkipped = true;
            for (const [ name, job ] of Object.entries(needs)) {
              if (name.startsWith('cmd-')) {
                allSkipped = allSkipped && job.result === 'skipped';
              }
            }

            const replyMessage = allSkipped
              ? 'Unknown command 😕\n\n' + process.env.SUPPORTED_COMMANDS
              : `Something went wrong, [please check log](${process.env.RUN_URL}).`;

            fs.writeFileSync('./replyMessage.txt', replyMessage);
        env:
          NEEDS: ${{ toJSON(needs) }}
          RUN_URL: ${{github.server_url}}/${{github.repository}}/actions/runs/${{github.run_id}}

      - if: always()
        uses: actions/github-script@v6
        with:
          script: |
            const fs = require('node:fs');

            const replyMessage = fs.readFileSync('./replyMessage.txt', 'utf-8');
            const { issue, comment, sender } = context.payload;

            const quoteRequest = comment.body
              .split('\n')
              .map((line) => '> ' + line)
              .join('\n');

            await github.rest.issues.createComment({
              ...context.repo,
              issue_number: issue.number,
              body: quoteRequest + `\n\n@${sender.login} ` + replyMessage,
            });

            // `github.rest` doesn't have this method :( so use graphql instead
            await github.graphql(`
              mutation ($subjectId: ID!) {
                minimizeComment(input: { subjectId: $subjectId, classifier: RESOLVED})
                  { __typename }
              }
            `, { subjectId: comment.node_id });