name: Upload PR Plugin to R2

concurrency:
  # Use the PR number from the workflow run or manual input to group uploads for the same PR
  # This ensures previous in-progress uploads for the same PR are cancelled
  group: pr-plugin-${{ inputs.pr_number || github.event.workflow_run.pull_requests[0].number || github.event.workflow_run.head_branch }}
  cancel-in-progress: true

on:
  workflow_run:
    workflows: ["Build PR Plugin"]
    types:
      - completed
  workflow_dispatch:
    inputs:
      pr_number:
        description: 'Pull Request number to build and upload'
        required: true
        type: string
      run_id:
        description: 'Workflow run ID to get artifacts from (optional, uses latest if not specified)'
        required: false
        type: string

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

jobs:
  upload-to-r2:
    runs-on: ubuntu-latest
    # Only run if the build workflow succeeded or manual trigger
    if: ${{ (github.event_name == 'workflow_run' && github.event.workflow_run.conclusion == 'success') || github.event_name == 'workflow_dispatch' }}
    defaults:
      run:
        shell: bash
    env:
      SHELLOPTS: errexit:pipefail

    steps:
      - name: Checkout code
        uses: actions/checkout@v4
        with:
          # SECURITY: Always checkout the default branch (trusted code)
          # Never checkout PR code in workflow_run context
          ref: ${{ github.event.repository.default_branch }}
          # Ensure we're checking out the base repository, not a fork
          repository: ${{ github.repository }}

      - name: Prepare artifact extraction directory
        run: |
          set -Eeuo pipefail
          IFS=$'\n\t'
          mkdir -p "${{ runner.temp }}/artifacts/"

      - name: Download artifacts from build workflow
        uses: actions/github-script@v7
        with:
          script: |
            // Determine run_id based on trigger type
            let run_id;
            if (context.eventName === 'workflow_dispatch') {
              const inputRunId = '${{ inputs.run_id }}';
              if (inputRunId && inputRunId !== '') {
                run_id = parseInt(inputRunId);
              } else {
                // Get latest run for the PR
                const workflowRuns = await github.rest.actions.listWorkflowRuns({
                  owner: context.repo.owner,
                  repo: context.repo.repo,
                  workflow_id: 'pr-plugin-build.yml',
                  status: 'success'
                });

                // Filter for runs from the specified PR
                const prNumber = parseInt('${{ inputs.pr_number }}');
                const prRuns = workflowRuns.data.workflow_runs.filter(run =>
                  run.pull_requests && run.pull_requests.some(pr => pr.number === prNumber)
                );

                if (prRuns.length === 0) {
                  core.setFailed(`No successful build runs found for PR #${prNumber}`);
                  return;
                }

                run_id = prRuns[0].id;
                console.log(`Using latest build run ${run_id} for PR #${prNumber}`);
              }
            } else {
              // For workflow_run events
              run_id = '${{ github.event.workflow_run.id }}';
              if (!run_id || run_id === '') {
                core.setFailed('No workflow run ID available');
                return;
              }
              run_id = parseInt(run_id);
            }

            let allArtifacts = await github.rest.actions.listWorkflowRunArtifacts({
               owner: context.repo.owner,
               repo: context.repo.repo,
               run_id: run_id,
            });

            let matchArtifact = allArtifacts.data.artifacts.filter((artifact) => {
              return artifact.name.startsWith('webgui-pr-plugin-')
            })[0];

            if (!matchArtifact) {
              core.setFailed('No artifacts found from build workflow');
              return;
            }

            let download = await github.rest.actions.downloadArtifact({
               owner: context.repo.owner,
               repo: context.repo.repo,
               artifact_id: matchArtifact.id,
               archive_format: 'zip',
            });

            let fs = require('fs');
            // Write to secure temp location
            const zipPath = process.env['RUNNER_TEMP'] + '/artifacts/artifacts.zip';
            fs.writeFileSync(zipPath, Buffer.from(download.data));

            core.setOutput('artifact_name', matchArtifact.name);

      - name: Extract artifacts
        run: |
          set -Eeuo pipefail
          IFS=$'\n\t'
          mkdir -p "${{ runner.temp }}/artifacts/unpacked"

          # Validate archive contents before extraction
          unzip -l "${{ runner.temp }}/artifacts/artifacts.zip" | awk '
            NR <= 3 || /^-/ || /^Archive:/ {next}
            /files$/ {exit}
            {
              # Extract the filename from unzip -l output (last field)
              filename = $NF
              if (filename ~ /^\// || filename ~ /\.\.\//) {
                print "INVALID:" filename > "/dev/stderr";
                exit 1
              }
            }
          '

          # Safe extraction using unzip
          unzip -o "${{ runner.temp }}/artifacts/artifacts.zip" -d "${{ runner.temp }}/artifacts/unpacked"
          ls -la "${{ runner.temp }}/artifacts/unpacked"

          # Check if metadata exists
          if [ ! -f "${{ runner.temp }}/artifacts/unpacked/pr-metadata.json" ]; then
            echo "No metadata file found, build may not have produced any changes"
            echo "has_artifacts=false" >> "$GITHUB_ENV"
            exit 0
          fi

          echo "has_artifacts=true" >> "$GITHUB_ENV"

          # Validate metadata schema
          echo "Metadata present; proceeding with schema validation."

      - name: Parse metadata
        if: env.has_artifacts == 'true'
        id: metadata
        run: |
          set -Eeuo pipefail
          IFS=$'\n\t'
          # Extract values from metadata
          PR_NUMBER=$(jq -r '.pr_number' "${{ runner.temp }}/artifacts/unpacked/pr-metadata.json")
          VERSION=$(jq -r '.version' "${{ runner.temp }}/artifacts/unpacked/pr-metadata.json")
          PR_VERSION=$(jq -r '.pr_version' "${{ runner.temp }}/artifacts/unpacked/pr-metadata.json")
          LOCAL_TXZ=$(jq -r '.local_txz' "${{ runner.temp }}/artifacts/unpacked/pr-metadata.json")
          REMOTE_TXZ=$(jq -r '.remote_txz' "${{ runner.temp }}/artifacts/unpacked/pr-metadata.json")
          PLUGIN_NAME=$(jq -r '.plugin_name' "${{ runner.temp }}/artifacts/unpacked/pr-metadata.json")

          # Generate R2 URLs and keys
          S3_BASE_URL="${{ secrets.CLOUDFLARE_PREVIEW_BUCKET_BASE_URL }}/pr-plugins/pr-${PR_NUMBER}"
          TXZ_URL="${S3_BASE_URL}/${REMOTE_TXZ}"
          PLUGIN_URL="${S3_BASE_URL}/${PLUGIN_NAME}"
          TXZ_KEY="pr-plugins/pr-${PR_NUMBER}/${REMOTE_TXZ}"
          PLUGIN_KEY="pr-plugins/pr-${PR_NUMBER}/${PLUGIN_NAME}"

          # Output for next steps
          echo "pr_number=$PR_NUMBER" >> $GITHUB_OUTPUT
          echo "version=$VERSION" >> $GITHUB_OUTPUT
          echo "pr_version=$PR_VERSION" >> $GITHUB_OUTPUT
          echo "local_txz=$LOCAL_TXZ" >> $GITHUB_OUTPUT
          echo "remote_txz=$REMOTE_TXZ" >> $GITHUB_OUTPUT
          echo "plugin_name=$PLUGIN_NAME" >> $GITHUB_OUTPUT
          echo "txz_url=$TXZ_URL" >> $GITHUB_OUTPUT
          echo "plugin_url=$PLUGIN_URL" >> $GITHUB_OUTPUT
          echo "txz_key=$TXZ_KEY" >> $GITHUB_OUTPUT
          echo "plugin_key=$PLUGIN_KEY" >> $GITHUB_OUTPUT

          # Also extract changed files for comment (limit to 100 files)
          jq -r '.changed_files[:100][]' "${{ runner.temp }}/artifacts/unpacked/pr-metadata.json" > "${{ runner.temp }}/artifacts/unpacked/changed_files.txt"
          FILE_COUNT=$(jq '.changed_files | length' "${{ runner.temp }}/artifacts/unpacked/pr-metadata.json")
          if [ "$FILE_COUNT" -gt 100 ]; then
            echo "Note: Showing first 100 of $FILE_COUNT changed files"
            echo "truncated=true" >> $GITHUB_OUTPUT
          else
            echo "truncated=false" >> $GITHUB_OUTPUT
          fi

      - name: Upload TXZ to R2
        if: env.has_artifacts == 'true'
        env:
          LOCAL_TXZ: ${{ steps.metadata.outputs.local_txz }}
          TXZ_KEY: ${{ steps.metadata.outputs.txz_key }}
          CLOUDFLARE_PREVIEW_BUCKET_NAME: ${{ secrets.CLOUDFLARE_PREVIEW_BUCKET_NAME }}
          CLOUDFLARE_S3_URL: ${{ secrets.CLOUDFLARE_S3_URL }}
          TXZ_URL: ${{ steps.metadata.outputs.txz_url }}
          AWS_ACCESS_KEY_ID: ${{ secrets.CLOUDFLARE_PREVIEW_ACCESS_KEY_ID }}
          AWS_SECRET_ACCESS_KEY: ${{ secrets.CLOUDFLARE_PREVIEW_SECRET_ACCESS_KEY }}
          AWS_DEFAULT_REGION: auto
          AWS_EC2_METADATA_DISABLED: true
          AWS_SHARED_CREDENTIALS_FILE: /dev/null
          AWS_CONFIG_FILE: /dev/null
        run: |
          set -Eeuo pipefail
          IFS=$'\n\t'
          # Copy from temp directory to working directory
          cp "${{ runner.temp }}/artifacts/unpacked/$LOCAL_TXZ" "./"

          # Upload to R2 with versioned filename
          aws s3 cp "$LOCAL_TXZ" \
            "s3://$CLOUDFLARE_PREVIEW_BUCKET_NAME/$TXZ_KEY" \
            --endpoint-url "$CLOUDFLARE_S3_URL" \
            --acl public-read

          echo "Uploaded TXZ to: $TXZ_URL"

      - name: Regenerate plugin file with correct R2 URLs
        if: env.has_artifacts == 'true'
        env:
          VERSION: ${{ steps.metadata.outputs.version }}
          PR_NUMBER: ${{ steps.metadata.outputs.pr_number }}
          PR_VERSION: ${{ steps.metadata.outputs.pr_version }}
          LOCAL_TXZ: ${{ steps.metadata.outputs.local_txz }}
          REMOTE_TXZ: ${{ steps.metadata.outputs.remote_txz }}
          TXZ_URL: ${{ steps.metadata.outputs.txz_url }}
          PLUGIN_URL: ${{ steps.metadata.outputs.plugin_url }}
        run: |
          set -Eeuo pipefail
          IFS=$'\n\t'
          # Regenerate the plugin with the actual R2 URLs
          bash .github/scripts/generate-pr-plugin.sh \
            "$VERSION" \
            "$PR_NUMBER" \
            "$(echo "$PR_VERSION" | cut -d. -f3)" \
            "$LOCAL_TXZ" \
            "$REMOTE_TXZ" \
            "$TXZ_URL" \
            "$PLUGIN_URL"

      - name: Upload PLG to R2
        if: env.has_artifacts == 'true'
        env:
          PLUGIN_NAME: ${{ steps.metadata.outputs.plugin_name }}
          PLUGIN_KEY: ${{ steps.metadata.outputs.plugin_key }}
          CLOUDFLARE_PREVIEW_BUCKET_NAME: ${{ secrets.CLOUDFLARE_PREVIEW_BUCKET_NAME }}
          CLOUDFLARE_S3_URL: ${{ secrets.CLOUDFLARE_S3_URL }}
          PLUGIN_URL: ${{ steps.metadata.outputs.plugin_url }}
          AWS_ACCESS_KEY_ID: ${{ secrets.CLOUDFLARE_PREVIEW_ACCESS_KEY_ID }}
          AWS_SECRET_ACCESS_KEY: ${{ secrets.CLOUDFLARE_PREVIEW_SECRET_ACCESS_KEY }}
          AWS_DEFAULT_REGION: auto
          AWS_EC2_METADATA_DISABLED: true
          AWS_SHARED_CREDENTIALS_FILE: /dev/null
          AWS_CONFIG_FILE: /dev/null
        run: |
          set -Eeuo pipefail
          IFS=$'\n\t'
          # Upload PLG - overwrite existing for updates
          aws s3 cp "$PLUGIN_NAME" \
            "s3://$CLOUDFLARE_PREVIEW_BUCKET_NAME/$PLUGIN_KEY" \
            --endpoint-url "$CLOUDFLARE_S3_URL" \
            --acl public-read

          echo "Uploaded PLG to: $PLUGIN_URL"

      - name: Format changed files list
        if: env.has_artifacts == 'true'
        id: format-files
        run: |
          set -Eeuo pipefail
          IFS=$'\n\t'
          # Format the file list for the comment with random delimiter
          DELIM="FILES_$(openssl rand -hex 8)"
          {
            echo "files<<$DELIM"
            cat "${{ runner.temp }}/artifacts/unpacked/changed_files.txt"
            echo "$DELIM"
          } >> "$GITHUB_OUTPUT"

      - name: Get PR info
        if: env.has_artifacts == 'true'
        id: pr-info
        uses: actions/github-script@v7
        env:
          PR_NUMBER: ${{ steps.metadata.outputs.pr_number }}
        with:
          script: |
            const pr_number = parseInt(process.env.PR_NUMBER);
            const pr = await github.rest.pulls.get({
              owner: context.repo.owner,
              repo: context.repo.repo,
              pull_number: pr_number
            });
            core.setOutput('pr_number', pr_number);

      - name: Comment on PR
        if: env.has_artifacts == 'true'
        uses: marocchino/sticky-pull-request-comment@v2
        with:
          number: ${{ steps.pr-info.outputs.pr_number }}
          header: pr-plugin
          message: |
            ## 🔧 PR Test Plugin Available

            A test plugin has been generated for this PR that includes the modified files.

            **Version:** `${{ steps.metadata.outputs.version }}`
            **Build:** [View Workflow Run](${{ github.event.workflow_run.html_url || github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }})

            ### 📥 Installation Instructions:

            **Install via Unraid Web UI:**
            1. Go to **Plugins → Install Plugin**
            2. Copy and paste this URL:
            ```
            ${{ steps.metadata.outputs.plugin_url }}
            ```
            3. Click **Install**

            **Alternative: Direct Download**
            - [📦 Download PLG](${{ steps.metadata.outputs.plugin_url }})
            - [📦 Download TXZ](${{ steps.metadata.outputs.txz_url }})

            ### ⚠️ Important Notes:

            - **Testing only:** This plugin is for testing PR changes
            - **Backup included:** Original files are automatically backed up
            - **Easy removal:** Files are restored when plugin is removed
            - **Conflicts:** Remove this plugin before installing production updates

            ### 📝 Modified Files:

            <details>
            <summary>Click to expand file list</summary>

            ```
            ${{ steps.format-files.outputs.files }}
            ```

            </details>

            ### 🔄 To Remove:

            Navigate to Plugins → Installed Plugins and remove `webgui-pr-${{ steps.metadata.outputs.pr_number }}`, or run:
            ```bash
            plugin remove webgui-pr-${{ steps.metadata.outputs.pr_number }}
            ```

            ---
            <sub>🤖 This comment is automatically generated and will be updated with each new push to this PR.</sub>
