name: PR Gate

on:
  pull_request_target:
    types: [opened]

jobs:
  check-contributor:
    runs-on: ubuntu-latest
    permissions:
      contents: read
      issues: write
      pull-requests: write
    steps:
      - name: Check if contributor is approved
        uses: actions/github-script@v7
        with:
          script: |
            const APPROVED_FILE = '.github/APPROVED_CONTRIBUTORS';
            const VALID_CAPABILITIES = new Set(['issue', 'pr']);
            const prAuthor = context.payload.pull_request.user.login;
            const defaultBranch = context.payload.repository.default_branch;

            if (prAuthor.endsWith('[bot]') || prAuthor === 'dependabot[bot]') {
              console.log(`Skipping bot: ${prAuthor}`);
              return;
            }

            async function getPermission(username) {
              try {
                const { data: permissionLevel } = await github.rest.repos.getCollaboratorPermissionLevel({
                  owner: context.repo.owner,
                  repo: context.repo.repo,
                  username,
                });
                return permissionLevel.permission;
              } catch {
                return null;
              }
            }

            async function getTextFile(path) {
              const { data: fileContent } = await github.rest.repos.getContent({
                owner: context.repo.owner,
                repo: context.repo.repo,
                path,
                ref: defaultBranch,
              });

              if (!('content' in fileContent) || typeof fileContent.content !== 'string') {
                throw new Error(`Expected file content for ${path}`);
              }

              return Buffer.from(fileContent.content, 'base64').toString('utf8');
            }

            function parseApprovedUsers(content) {
              const users = new Map();

              for (const rawLine of content.split('\n')) {
                const line = rawLine.trim();
                if (!line || line.startsWith('#')) continue;

                const parts = line.split(/\s+/);
                if (parts.length !== 2) {
                  console.log(`Skipping malformed line: ${rawLine}`);
                  continue;
                }

                const [username, capability] = parts;
                const normalizedCapability = capability.toLowerCase();
                if (!VALID_CAPABILITIES.has(normalizedCapability)) {
                  console.log(`Skipping line with invalid capability: ${rawLine}`);
                  continue;
                }

                users.set(username.toLowerCase(), normalizedCapability);
              }

              return users;
            }

            async function closePullRequest(message) {
              await github.rest.issues.createComment({
                owner: context.repo.owner,
                repo: context.repo.repo,
                issue_number: context.payload.pull_request.number,
                body: message,
              });

              await github.rest.pulls.update({
                owner: context.repo.owner,
                repo: context.repo.repo,
                pull_number: context.payload.pull_request.number,
                state: 'closed',
              });
            }

            const permission = await getPermission(prAuthor);
            if (['admin', 'maintain', 'write'].includes(permission)) {
              console.log(`${prAuthor} is a collaborator with ${permission} access`);
              return;
            }

            const approvedContent = await getTextFile(APPROVED_FILE);
            const approvedUsers = parseApprovedUsers(approvedContent);
            const capability = approvedUsers.get(prAuthor.toLowerCase());

            if (capability === 'pr') {
              console.log(`${prAuthor} is approved for PRs`);
              return;
            }

            console.log(`${prAuthor} is not approved, closing PR`);

            const message = [
              'This PR was auto-closed. Only contributors approved with `lgtm` can open PRs. Open an issue first.',
              '',
              `Maintainers review auto-closed issues daily. Issues that do not meet the quality bar in [CONTRIBUTING.md](https://github.com/${context.repo.owner}/${context.repo.repo}/blob/${defaultBranch}/CONTRIBUTING.md) will not be reopened or receive a reply.`,
              '',
              'If a maintainer replies `lgtmi`, your future issues will stay open. If a maintainer replies `lgtm`, your future issues and PRs will stay open.',
              '',
              `See [CONTRIBUTING.md](https://github.com/${context.repo.owner}/${context.repo.repo}/blob/${defaultBranch}/CONTRIBUTING.md).`,
            ].join('\n');

            await closePullRequest(message);
