name: compliance-close

on:
  schedule:
    # Run every 30 minutes to check for expired compliance windows
    - cron: "*/30 * * * *"
  workflow_dispatch:

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

jobs:
  close-non-compliant:
    runs-on: ubuntu-latest
    steps:
      - name: Close non-compliant issues and PRs after 2 hours
        uses: actions/github-script@f28e40c7f34bde8b3046d885e986cb6290c5673b # v7.1.0
        with:
          script: |
            const { data: items } = await github.rest.issues.listForRepo({
              owner: context.repo.owner,
              repo: context.repo.repo,
              labels: 'needs:compliance',
              state: 'open',
              per_page: 100,
            });

            if (items.length === 0) {
              core.info('No open issues/PRs with needs:compliance label');
              return;
            }

            const now = Date.now();
            const twoHours = 2 * 60 * 60 * 1000;
            const orgMemberAssociations = new Set(['OWNER', 'MEMBER']);
            const agentLogin = 'opencode-agent[bot]';
            const { data: file } = await github.rest.repos.getContent({
              owner: context.repo.owner,
              repo: context.repo.repo,
              path: '.github/TEAM_MEMBERS',
              ref: 'dev',
            });
            const teamMembers = new Set(
              Buffer.from(file.content, 'base64')
                .toString()
                .split('\n')
                .map((line) => line.trim().toLowerCase())
                .filter(Boolean)
            );

            function isExempt(item) {
              const login = item.user?.login?.toLowerCase();
              return (
                login === agentLogin ||
                orgMemberAssociations.has(item.author_association) ||
                (login && teamMembers.has(login))
              );
            }

            for (const item of items) {
              const isPR = !!item.pull_request;
              const kind = isPR ? 'PR' : 'issue';
              const login = item.user?.login;

              if (isExempt(item)) {
                core.info(`Skipping ${kind} #${item.number}; author ${login || 'unknown'} is exempt`);
                try {
                  await github.rest.issues.removeLabel({
                    owner: context.repo.owner,
                    repo: context.repo.repo,
                    issue_number: item.number,
                    name: 'needs:compliance',
                  });
                } catch (e) {}
                continue;
              }

              const { data: comments } = await github.rest.issues.listComments({
                owner: context.repo.owner,
                repo: context.repo.repo,
                issue_number: item.number,
              });

              const complianceComment = comments.find(c => c.body.includes('<!-- issue-compliance -->'));
              if (!complianceComment) continue;

              const commentAge = now - new Date(complianceComment.created_at).getTime();
              if (commentAge < twoHours) {
                core.info(`${kind} #${item.number} still within 2-hour window (${Math.round(commentAge / 60000)}m elapsed)`);
                continue;
              }

              const closeMessage = isPR
                ? 'This pull request has been automatically closed because it was not updated to meet our [contributing guidelines](../blob/dev/CONTRIBUTING.md) within the 2-hour window.\n\nFeel free to open a new pull request that follows our guidelines.'
                : 'This issue has been automatically closed because it was not updated to meet our [contributing guidelines](../blob/dev/CONTRIBUTING.md) within the 2-hour window.\n\nFeel free to open a new issue that follows our issue templates.';

              await github.rest.issues.createComment({
                owner: context.repo.owner,
                repo: context.repo.repo,
                issue_number: item.number,
                body: closeMessage,
              });

              try {
                await github.rest.issues.removeLabel({
                  owner: context.repo.owner,
                  repo: context.repo.repo,
                  issue_number: item.number,
                  name: 'needs:compliance',
                });
              } catch (e) {}

              if (isPR) {
                await github.rest.pulls.update({
                  owner: context.repo.owner,
                  repo: context.repo.repo,
                  pull_number: item.number,
                  state: 'closed',
                });
              } else {
                await github.rest.issues.update({
                  owner: context.repo.owner,
                  repo: context.repo.repo,
                  issue_number: item.number,
                  state: 'closed',
                  state_reason: 'not_planned',
                });
              }

              core.info(`Closed non-compliant ${kind} #${item.number} after 2-hour window`);
            }
