name: Stale PR Management
on:
  schedule:
    - cron: "0 0 * * *"
  workflow_dispatch:
  pull_request_target:
    types:
      - labeled

jobs:
  stale-prs:
    runs-on: self-hosted
    permissions:
      pull-requests: write
      issues: write
      contents: read
    steps:
      - name: Handle stale PRs
        uses: actions/github-script@v7
        with:
          script: |
            const now = new Date();
            const owner = context.repo.owner;
            const repo = context.repo.repo;

            // --- When stale label is added, comment immediately ---
            if (context.eventName === "pull_request_target" && context.payload.action === "labeled") {
              const label = context.payload.label?.name;
              if (label === "stale") {
                const author = context.payload.pull_request.user.login;
                await github.rest.issues.createComment({
                  owner,
                  repo,
                  issue_number: context.payload.pull_request.number,
                  body: `@${author} This PR has been marked as stale. It will be closed if no new commits are added in 7 days.`
                });
              }
              return;
            }

            // --- Scheduled run: fetch all open PRs ---
            const { data: prs } = await github.rest.pulls.list({
              owner,
              repo,
              state: "open",
              per_page: 100
            });

            for (const pr of prs) {
              const labels = pr.labels.map(l => l.name);
              const hasStale = labels.includes("stale");
              const hasKeepOpen = labels.includes("keep-open");

              // -------------------------------------------------------
              // Auto-label PRs with no activity in the last 14 days,
              // but ONLY if a maintainer has engaged with the PR at least once.
              // -------------------------------------------------------
              if (!hasStale && !hasKeepOpen) {
                const author = pr.user.login;

                // Fetch reviews and issue comments to determine maintainer engagement.
                const { data: reviews } = await github.rest.pulls.listReviews({
                  owner,
                  repo,
                  pull_number: pr.number,
                  per_page: 100
                });

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

                // A "maintainer touch" is any review or comment from a non-bot account
                // that isn't the PR author.
                const hasNonAuthorReview = reviews.some(
                  r => r.user?.type !== "Bot" && r.user?.login !== author
                );
                const hasNonAuthorComment = comments.some(
                  c => c.user?.type !== "Bot" && c.user?.login !== author
                );

                if (!hasNonAuthorReview && !hasNonAuthorComment) {
                  // No one but the author (and bots) has touched this PR.
                  // Don't penalize the contributor with a stale label — skip it.
                  continue;
                }

                // --- Activity check (unchanged logic) ---
                const { data: commits } = await github.rest.pulls.listCommits({
                  owner,
                  repo,
                  pull_number: pr.number
                });
                const lastCommitDate = commits.length > 0
                  ? new Date(commits[commits.length - 1].commit.author.date)
                  : new Date(pr.created_at);

                const humanComments = comments.filter(c => c.user?.type !== "Bot");
                const lastCommentDate = humanComments.length > 0
                  ? new Date(humanComments[humanComments.length - 1].created_at)
                  : null;

                // Most recent activity across commits and comments
                const lastActivityDate = lastCommentDate && lastCommentDate > lastCommitDate
                  ? lastCommentDate
                  : lastCommitDate;

                const daysSinceActivity = (now - lastActivityDate) / (1000 * 60 * 60 * 24);

                if (daysSinceActivity > 14) {
                  await github.rest.issues.addLabels({
                    owner,
                    repo,
                    issue_number: pr.number,
                    labels: ["stale"]
                  });
                  // The pull_request_target labeled event will fire the comment automatically.
                  // Skip further processing for this PR in this run.
                  continue;
                }

                // Not stale, nothing else to do for this PR.
                continue;
              }

              // -------------------------------------------------------
              // EXISTING: Manage already-stale PRs
              // -------------------------------------------------------
              if (!hasStale) continue; // has keep-open but not stale — skip

              // Get timeline events to find when stale label was added
              const { data: events } = await github.rest.issues.listEvents({
                owner,
                repo,
                issue_number: pr.number,
                per_page: 100
              });

              // Find the most recent time the stale label was added
              const staleLabelEvents = events
                .filter(e => e.event === "labeled" && e.label?.name === "stale")
                .sort((a, b) => new Date(b.created_at) - new Date(a.created_at));

              if (staleLabelEvents.length === 0) continue;

              const staleLabelDate = new Date(staleLabelEvents[0].created_at);
              const daysSinceStale = (now - staleLabelDate) / (1000 * 60 * 60 * 24);

              // Check for new commits since stale label was added
              const { data: commits } = await github.rest.pulls.listCommits({
                owner,
                repo,
                pull_number: pr.number
              });

              const lastCommitDate = new Date(commits[commits.length - 1].commit.author.date);
              const author = pr.user.login;

              // If there are new commits after the stale label, remove it
              if (lastCommitDate > staleLabelDate) {
                await github.rest.issues.removeLabel({
                  owner,
                  repo,
                  issue_number: pr.number,
                  name: "stale"
                });
                await github.rest.issues.createComment({
                  owner,
                  repo,
                  issue_number: pr.number,
                  body: `@${author} Recent activity detected. Removing stale label.`
                });
              }
              // If 7 days have passed since stale label, close the PR
              else if (daysSinceStale > 7) {
                await github.rest.pulls.update({
                  owner,
                  repo,
                  pull_number: pr.number,
                  state: "closed"
                });
                await github.rest.issues.createComment({
                  owner,
                  repo,
                  issue_number: pr.number,
                  body: `@${author} Closing stale PR due to inactivity (no commits for 7 days after stale label).`
                });
              }
            }
