name: Monthly Metrics Snapshot

on:
  schedule:
    - cron: '0 14 1 * *' # Monthly on the 1st at 14:00 UTC
  workflow_dispatch:

permissions:
  contents: read
  issues: write

jobs:
  snapshot:
    name: Update metrics issue
    runs-on: ubuntu-latest
    steps:
      - name: Update monthly metrics issue
        uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
        with:
          script: |
            const owner = context.repo.owner;
            const repo = context.repo.repo;
            const title = "Monthly Metrics Snapshot";
            const label = "metrics-snapshot";
            const monthKey = new Date().toISOString().slice(0, 7);

            function parseLastPage(linkHeader) {
              if (!linkHeader) return null;
              const match = linkHeader.match(/&page=(\d+)>; rel="last"/);
              return match ? Number(match[1]) : null;
            }

            function escapeRegex(value) {
              return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
            }

            function fmt(value) {
              if (value === null || value === undefined) return "n/a";
              return Number(value).toLocaleString("en-US");
            }

            async function getNpmDownloads(range, pkg) {
              try {
                const res = await fetch(`https://api.npmjs.org/downloads/point/${range}/${pkg}`);
                if (!res.ok) return null;
                const data = await res.json();
                return data.downloads ?? null;
              } catch {
                return null;
              }
            }

            async function getContributorsCount() {
              try {
                const resp = await github.rest.repos.listContributors({
                  owner,
                  repo,
                  per_page: 1,
                  anon: "false"
                });
                return parseLastPage(resp.headers.link) ?? resp.data.length;
              } catch {
                return null;
              }
            }

            async function getReleasesCount() {
              try {
                const resp = await github.rest.repos.listReleases({
                  owner,
                  repo,
                  per_page: 1
                });
                return parseLastPage(resp.headers.link) ?? resp.data.length;
              } catch {
                return null;
              }
            }

            async function getTraffic(metric) {
              try {
                const route = metric === "clones"
                  ? "GET /repos/{owner}/{repo}/traffic/clones"
                  : "GET /repos/{owner}/{repo}/traffic/views";
                const resp = await github.request(route, { owner, repo });
                return resp.data?.count ?? null;
              } catch {
                return null;
              }
            }

            const [
              mainWeek,
              shieldWeek,
              mainMonth,
              shieldMonth,
              repoData,
              contributors,
              releases,
              views14d,
              clones14d
            ] = await Promise.all([
              getNpmDownloads("last-week", "ecc-universal"),
              getNpmDownloads("last-week", "ecc-agentshield"),
              getNpmDownloads("last-month", "ecc-universal"),
              getNpmDownloads("last-month", "ecc-agentshield"),
              github.rest.repos.get({ owner, repo }),
              getContributorsCount(),
              getReleasesCount(),
              getTraffic("views"),
              getTraffic("clones")
            ]);

            const stars = repoData.data.stargazers_count;
            const forks = repoData.data.forks_count;

            const tableHeader = [
              "| Month (UTC) | ecc-universal (week) | ecc-agentshield (week) | ecc-universal (30d) | ecc-agentshield (30d) | Stars | Forks | Contributors | GitHub App installs (manual) | Views (14d) | Clones (14d) | Releases |",
              "|---|---:|---:|---:|---:|---:|---:|---:|---:|---:|---:|---:|"
            ].join("\n");

            const row = `| ${monthKey} | ${fmt(mainWeek)} | ${fmt(shieldWeek)} | ${fmt(mainMonth)} | ${fmt(shieldMonth)} | ${fmt(stars)} | ${fmt(forks)} | ${fmt(contributors)} | n/a | ${fmt(views14d)} | ${fmt(clones14d)} | ${fmt(releases)} |`;

            const intro = [
              "# Monthly Metrics Snapshot",
              "",
              "Automated monthly snapshot for sponsor/partner reporting.",
              "",
              "- `GitHub App installs (manual)` is intentionally manual until a stable public API path is available.",
              "- Traffic metrics are 14-day rolling windows from the GitHub traffic API and can show `n/a` if unavailable.",
              "",
              tableHeader
            ].join("\n");

            try {
              await github.rest.issues.getLabel({ owner, repo, name: label });
            } catch (error) {
              if (error.status === 404) {
                await github.rest.issues.createLabel({
                  owner,
                  repo,
                  name: label,
                  color: "0e8a16",
                  description: "Automated monthly project metrics snapshots"
                });
              } else {
                throw error;
              }
            }

            const issuesResp = await github.rest.issues.listForRepo({
              owner,
              repo,
              state: "open",
              labels: label,
              per_page: 100
            });

            let issue = issuesResp.data.find((item) => item.title === title);

            if (!issue) {
              const created = await github.rest.issues.create({
                owner,
                repo,
                title,
                labels: [label],
                body: `${intro}\n${row}\n`
              });
              console.log(`Created issue #${created.data.number}`);
              return;
            }

            const currentBody = issue.body || "";
            const rowPattern = new RegExp(`^\\| ${escapeRegex(monthKey)} \\|.*$`, "m");

            let body;
            if (rowPattern.test(currentBody)) {
              body = currentBody.replace(rowPattern, row);
              console.log(`Refreshed issue #${issue.number} snapshot row for ${monthKey}`);
            } else {
              body = currentBody.includes("| Month (UTC) |")
                ? `${currentBody.trimEnd()}\n${row}\n`
                : `${intro}\n${row}\n`;
            }

            await github.rest.issues.update({
              owner,
              repo,
              issue_number: issue.number,
              body
            });
            console.log(`Updated issue #${issue.number}`);
