name: Create Changelog Pull Request

on:
  push:
    branches: ["main"]
  workflow_dispatch:

jobs:
  update-changelog-pull-request:
    if: github.repository == 'community-scripts/ProxmoxVE'
    runs-on: ubuntu-latest
    env:
      CONFIG_PATH: .github/changelog-pr-config.json
      BRANCH_NAME: github-action-update-changelog
      AUTOMATED_PR_LABEL: "automated pr"
    permissions:
      contents: write
      pull-requests: write
    steps:
      - name: Generate a token
        id: generate-token
        uses: actions/create-github-app-token@v1
        with:
          app-id: ${{ vars.APP_ID }}
          private-key: ${{ secrets.APP_PRIVATE_KEY }}

      - name: Generate a token for PR approval and merge
        id: generate-token-merge
        uses: actions/create-github-app-token@v1
        with:
          app-id: ${{ secrets.APP_ID_APPROVE_AND_MERGE }}
          private-key: ${{ secrets.APP_KEY_APPROVE_AND_MERGE }}

      - name: Checkout code
        uses: actions/checkout@v4
        with:
          fetch-depth: 0

      - name: Get latest dates in changelog
        run: |
          DATES=$(grep -E '^## [0-9]{4}-[0-9]{2}-[0-9]{2}' CHANGELOG.md | head -n 2 | awk '{print $2}')

          LATEST_DATE=$(echo "$DATES" | sed -n '1p')
          SECOND_LATEST_DATE=$(echo "$DATES" | sed -n '2p')
          TODAY=$(date -u +%Y-%m-%d)

          echo "TODAY=$TODAY" >> $GITHUB_ENV
          if [[ "$LATEST_DATE" == "$TODAY" ]]; then
            echo "LATEST_DATE=$SECOND_LATEST_DATE" >> $GITHUB_ENV
          else
            echo "LATEST_DATE=$LATEST_DATE" >> $GITHUB_ENV
          fi

      - name: Get categorized pull requests
        id: get-categorized-prs
        uses: actions/github-script@v7
        with:
          script: |
            async function main() {
              const fs = require('fs').promises;
              const path = require('path');

              const configPath = path.resolve(process.env.CONFIG_PATH);
              const fileContent = await fs.readFile(configPath, 'utf-8');
              const changelogConfig = JSON.parse(fileContent);

              const categorizedPRs = changelogConfig.map(obj => ({
                ...obj,
                notes: [],
                subCategories: obj.subCategories ?? (
                  obj.labels.includes("update script") ? [
                    { title: "🐞 Bug Fixes", labels: ["bugfix"], notes: [] },
                    { title: "✨ New Features", labels: ["feature"], notes: [] },
                    { title: "💥 Breaking Changes", labels: ["breaking change"], notes: [] },
                    { title: "🔧 Refactor", labels: ["refactor"], notes: [] },
                  ] :
                  obj.labels.includes("maintenance") ? [
                    { title: "🐞 Bug Fixes", labels: ["bugfix"], notes: [] },
                    { title: "✨ New Features", labels: ["feature"], notes: [] },
                    { title: "💥 Breaking Changes", labels: ["breaking change"], notes: [] },
                    { title: "📡 API", labels: ["api"], notes: [] },
                    { title: "Github", labels: ["github"], notes: [] },
                    { title: "📝 Documentation", labels: ["maintenance"], notes: [] },
                    { title: "🔧 Refactor", labels: ["refactor"], notes: [] }
                  ] :
                  obj.labels.includes("website") ? [
                    { title: "🐞 Bug Fixes", labels: ["bugfix"], notes: [] },
                    { title: "✨ New Features", labels: ["feature"], notes: [] },
                    { title: "💥 Breaking Changes", labels: ["breaking change"], notes: [] },
                    { title: "Script Information", labels: ["json"], notes: [] }
                  ] : []
                )
              }));

              const latestDateInChangelog = new Date(process.env.LATEST_DATE);
              latestDateInChangelog.setUTCHours(23, 59, 59, 999);

              const { data: pulls } = await github.rest.pulls.list({
                owner: context.repo.owner,
                repo: "ProxmoxVE",
                base: "main",
                state: "closed",
                sort: "updated",
                direction: "desc",
                per_page: 100,
              });

              const filteredPRs = pulls.filter(pr =>
                pr.merged_at &&
                new Date(pr.merged_at) > latestDateInChangelog &&
                !pr.labels.some(label =>
                  ["invalid", "wontdo", process.env.AUTOMATED_PR_LABEL].includes(label.name.toLowerCase())
                )
              );

              for (const pr of filteredPRs) {
                const prLabels = pr.labels.map(label => label.name.toLowerCase());
                if (pr.user.login.includes("push-app-to-main[bot]")) {

                  const scriptName = pr.title;
                    try {
                      const { data: relatedIssues } = await github.rest.issues.listForRepo({
                        owner: context.repo.owner,
                        repo: "ProxmoxVED",
                        state: "all",
                        labels: ["Started Migration To ProxmoxVE"]
                      });

                      const matchingIssue = relatedIssues.find(issue =>
                        issue.title.toLowerCase().includes(scriptName.toLowerCase())
                      );

                      if (matchingIssue) {
                        const issueAuthor = matchingIssue.user.login;
                        const issueAuthorUrl = `https://github.com/${issueAuthor}`;
                        prNote = `- ${pr.title} [@${issueAuthor}](${issueAuthorUrl}) ([#${pr.number}](${pr.html_url}))`;
                      }
                      else {
                        prNote = `- ${pr.title} ([#${pr.number}](${pr.html_url}))`;
                      }
                    } catch (error) {
                      console.error(`Error fetching related issues: ${error}`);
                      prNote = `- ${pr.title} ([#${pr.number}](${pr.html_url}))`;
                    }
                }else{
                  prNote = `- ${pr.title} [@${pr.user.login}](https://github.com/${pr.user.login}) ([#${pr.number}](${pr.html_url}))`;
                }


                if (prLabels.includes("new script")) {
                  const newScriptCategory = categorizedPRs.find(category =>
                  category.title === "New Scripts" || category.labels.includes("new script"));
                  if (newScriptCategory) {
                  newScriptCategory.notes.push(prNote);
                  }
                } else {

                  let categorized = false;
                  const priorityCategories = categorizedPRs.slice();
                  for (const category of priorityCategories) {
                  if (categorized) break;
                  if (category.labels.some(label => prLabels.includes(label))) {
                    if (category.subCategories && category.subCategories.length > 0) {
                    const subCategory = category.subCategories.find(sub =>
                      sub.labels.some(label => prLabels.includes(label))
                    );

                    if (subCategory) {
                      subCategory.notes.push(prNote);
                    } else {
                      category.notes.push(prNote);
                    }
                    } else {
                    category.notes.push(prNote);
                    }
                    categorized = true;
                  }
                  }
                }

              }

              return categorizedPRs;
            }

            return await main();

      - name: Update CHANGELOG.md
        uses: actions/github-script@v7
        with:
          script: |
            const fs = require('fs').promises;
            const path = require('path');

            const today = process.env.TODAY;
            const latestDateInChangelog = process.env.LATEST_DATE;
            const changelogPath = path.resolve('CHANGELOG.md');
            const categorizedPRs = ${{ steps.get-categorized-prs.outputs.result }};

            console.log(JSON.stringify(categorizedPRs, null, 2));


            let newReleaseNotes = `## ${today}\n\n`;
            for (const { title, notes, subCategories } of categorizedPRs) {
              const hasSubcategories = subCategories && subCategories.length > 0;
              const hasMainNotes = notes.length > 0;
              const hasSubNotes = hasSubcategories && subCategories.some(sub => sub.notes && sub.notes.length > 0);

              if (hasMainNotes || hasSubNotes) {
                newReleaseNotes += `### ${title}\n\n`;
              }

              if (hasMainNotes) {
                newReleaseNotes += `  ${notes.join("\n")}\n\n`;
              }
              if (hasSubcategories) {
                for (const { title: subTitle, notes: subNotes } of subCategories) {
                  if (subNotes && subNotes.length > 0) {
                    newReleaseNotes += `  - #### ${subTitle}\n\n`; 
                    newReleaseNotes += `    ${subNotes.join("\n    ")}\n\n`; 
                  }
                }
              }
            }        
            const changelogContent = await fs.readFile(changelogPath, 'utf-8');
            const changelogIncludesTodaysReleaseNotes = changelogContent.includes(`\n## ${today}`);

            const regex = changelogIncludesTodaysReleaseNotes 
              ? new RegExp(`## ${today}.*(?=## ${latestDateInChangelog})`, "gs") 
              : new RegExp(`(?=## ${latestDateInChangelog})`, "gs");

            const newChangelogContent = changelogContent.replace(regex, newReleaseNotes);
            await fs.writeFile(changelogPath, newChangelogContent);

      - name: Check for changes
        id: verify-diff
        run: |
          git diff --quiet . || echo "changed=true" >> $GITHUB_ENV

      - name: Commit and push changes
        if: env.changed == 'true'
        run: |
          git config --global user.name "github-actions[bot]"
          git config --global user.email "github-actions[bot]@users.noreply.github.com"
          git add CHANGELOG.md
          git commit -m "Update CHANGELOG.md"
          git checkout -b $BRANCH_NAME || git checkout $BRANCH_NAME
          git push origin $BRANCH_NAME --force

      - name: Create pull request if not exists
        if: env.changed == 'true'
        env:
          GH_TOKEN: ${{ steps.generate-token.outputs.token }}
        run: |
          PR_EXISTS=$(gh pr list --head "${BRANCH_NAME}" --json number --jq '.[].number')
          if [ -z "$PR_EXISTS" ]; then
            gh pr create --title "[Github Action] Update CHANGELOG.md" \
                         --body "This PR is auto-generated by a Github Action to update the CHANGELOG.md file." \
                         --head $BRANCH_NAME \
                         --base main \
                         --label "$AUTOMATED_PR_LABEL"
          fi

      - name: Approve pull request
        if: env.changed == 'true'
        env:
          GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
        run: |
          PR_NUMBER=$(gh pr list --head "${BRANCH_NAME}" --json number --jq '.[].number')
          if [ -n "$PR_NUMBER" ]; then
            gh pr review $PR_NUMBER --approve
          fi

      - name: Approve pull request and merge
        if: env.changed == 'true'
        env:
          GH_TOKEN: ${{ steps.generate-token-merge.outputs.token }}
        run: |
          git config --global user.name "github-actions-automege[bot]"
          git config --global user.email "github-actions-automege[bot]@users.noreply.github.com"
          PR_NUMBER=$(gh pr list --head "${BRANCH_NAME}" --json number --jq '.[].number')
          if [ -n "$PR_NUMBER" ]; then
            gh pr review $PR_NUMBER --approve
            gh pr merge $PR_NUMBER --squash --admin
          fi
