name: Weekly Security Maintenance

on:
  schedule:
    - cron: '0 2 * * 0' # Sundays at 2 AM UTC
  workflow_dispatch:
    inputs:
      max-age-days:
        description: 'Maximum SHA age in days before considered stale'
        required: false
        type: number
        default: 30

concurrency:
  group: ${{ github.workflow }}
  cancel-in-progress: false

permissions:
  contents: read

jobs:
    # Job 1: Validate dependency pinning
  validate-pinning:
    name: Validate Dependency Pinning Compliance
    uses: ./.github/workflows/dependency-pinning-scan.yml
    permissions:
      contents: read
      security-events: write  # Required for SARIF upload to Security tab
    with:
      soft-fail: true
      upload-sarif: true
      upload-artifact: true

  # Job 2: Check for stale SHA pins
  check-staleness:
    name: Check SHA Staleness
    uses: ./.github/workflows/sha-staleness-check.yml
    permissions:
      contents: read
    with:
      max-age-days: ${{ inputs.max-age-days || 30 }}

  # Job 3: Run CodeQL security analysis
  codeql-analysis:
    name: CodeQL Security Analysis
    uses: ./.github/workflows/codeql-analysis.yml
    permissions:
      contents: read
      security-events: write  # Required for SARIF upload to Security tab
      actions: read

  # Job 4: Generate consolidated summary
  summary:
    name: Security Maintenance Summary
    needs: [validate-pinning, check-staleness, codeql-analysis]
    if: always()
    runs-on: ubuntu-latest
    permissions:
      contents: read
      issues: write
    steps:
      - name: Generate summary
        shell: pwsh
        run: |
          $pinningScore = '${{ needs.validate-pinning.outputs.compliance-score }}'
          $unpinnedCount = '${{ needs.validate-pinning.outputs.unpinned-count }}'
          $staleCount = '${{ needs.check-staleness.outputs.stale-count }}'
          $codeqlResult = '${{ needs.codeql-analysis.result }}'
          
          # Determine overall status
          $hasIssues = $false
          if ($unpinnedCount -ne '0') { $hasIssues = $true }
          if ($staleCount -ne '0') { $hasIssues = $true }
          
          @"
          # 🔐 Weekly Security Maintenance Report
          
          **Execution Time:** $(Get-Date -Format 'yyyy-MM-dd HH:mm:ss UTC')
          **Trigger:** ${{ github.event_name }}
          
          ## 📊 Dependency Health
          
          | Metric | Status | Details |
          |--------|--------|---------|
          | Dependency Pinning Compliance | $(if ($unpinnedCount -eq '0') { '✅ Pass' } else { '⚠️ Warning' }) | Score: $pinningScore% |
          | Unpinned Dependencies | $(if ($unpinnedCount -eq '0') { '✅ None' } else { "⚠️ $unpinnedCount found" }) | $(if ($unpinnedCount -ne '0') { 'Review warnings above' } else { 'All pinned' }) |
          | Stale SHAs (>${{ inputs.max-age-days }} days) | $(if ($staleCount -eq '0') { '✅ None' } else { "⚠️ $staleCount found" }) | $(if ($staleCount -ne '0') { 'Review warnings above' } else { 'All current' }) |
          
          ## 🛡️ Security Analysis
          
          | Component | Status |
          |-----------|--------|
          | CodeQL Analysis | $(if ($codeqlResult -eq 'success') { '✅ Complete' } else { '⚠️ See details' }) |
          | Secret Scanning | ✅ Enabled (GitHub native) |
          | Dependabot Alerts | ✅ Enabled (GitHub native) |
          
          $(if ($unpinnedCount -ne '0') {
            @"
          
          ## ⚠️ Action Required: Unpinned Dependencies
          
          **$unpinnedCount dependencies are not properly pinned.** This is a security risk.
          
          **To fix:**
          ``````powershell
          # Review the warnings above, then pin each dependency to a SHA
          # Example: Change 'actions/checkout@v4' to 'actions/checkout@<commit-sha> # v4.2.2'
          ``````
          
          "@
          })
          
          $(if ($staleCount -ne '0') {
            # Load the staleness report to show details
            # Note: Artifacts are not automatically shared between jobs
            # This will only work if the file exists in the runner's filesystem
            # Consider using job outputs or downloading artifacts explicitly
            $stalenessReport = if (Test-Path 'logs/sha-staleness-results.json') {
              Get-Content 'logs/sha-staleness-results.json' | ConvertFrom-Json
            }
            
            $thresholdDays = ${{ inputs.max-age-days || 30 }}
            $staleDetails = ""
            if ($stalenessReport -and $stalenessReport.Dependencies) {
              foreach ($staleDep in $stalenessReport.Dependencies) {
                if ($staleDep.DaysOld -gt $thresholdDays) {
                  $staleDetails += "- **$($staleDep.File)**: $($staleDep.Action)@$($staleDep.CurrentVersion) ($($staleDep.DaysOld) days old, latest: $($staleDep.LatestVersion))`n"
                }
              }
            }
            
            @"
          
          ## ℹ️ Recommended: Update Stale SHAs
          
          **$staleCount SHA pins are outdated (>${{ inputs.max-age-days || 30 }} days old).** Consider updating them.
          
          $staleDetails
          
          **To update:**
          ``````powershell
          # Run the update script to get latest SHAs
          scripts/security/Update-ActionSHAPinning.ps1 -Path .github/workflows -UpdateStale
          ``````
          
          "@
          })
          
          $(if (!$hasIssues) {
            @"
          
          ## ✅ All Clear!
          
          Repository security posture is excellent:
          - All dependencies are properly pinned
          - All SHAs are current (< ${{ inputs.max-age-days || 30 }} days old)
          - CodeQL analysis completed successfully
          - GitHub Secret Scanning and Dependabot actively monitoring
          
          "@
          })
          
          ---
          
          📥 **Detailed Reports:** Download artifacts from this workflow run  
          🔗 **Workflow Run:** ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}
          
          $(if ($hasIssues) {
            @"
          
          ### 📋 Next Steps
          
          1. Review the build warnings generated in this workflow run
          2. For unpinned dependencies: Pin them to SHAs immediately (security risk)
          3. For stale SHAs (>${{ inputs.max-age-days }} days): Update them when convenient (maintenance task)
          4. Download the JSON report artifacts for detailed analysis
          
          "@
          })
          "@ | Out-File -FilePath $env:GITHUB_STEP_SUMMARY -Encoding UTF8

      - name: Set workflow conclusion
        shell: pwsh
        run: |
          $unpinnedCount = '${{ needs.validate-pinning.outputs.unpinned-count }}'
          $staleCount = '${{ needs.check-staleness.outputs.stale-count }}'
          
          # Informational output - workflow succeeds but logs warnings
          if ($unpinnedCount -ne '0') {
            Write-Output "::warning::Found $unpinnedCount unpinned dependencies. Review build warnings for details."
          }
          
          if ($staleCount -ne '0') {
            Write-Output "::warning::Found $staleCount stale SHA pins (>${{ inputs.max-age-days || 30 }} days old). Consider updating them."
          }
          
          if ($unpinnedCount -eq '0' -and $staleCount -eq '0') {
            Write-Output "::notice::All dependencies are properly pinned and up-to-date!"
          }
      - name: Download staleness report
        if: ${{ !cancelled() && needs.check-staleness.result == 'success' }}
        uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
        with:
          name: sha-staleness-results
          path: logs
        continue-on-error: true

      - name: Create or Update Staleness Issue
        if: ${{ !cancelled() && needs.check-staleness.result == 'success' }}
        shell: pwsh
        env:
          GH_TOKEN: ${{ github.token }}
          STALE_COUNT: ${{ needs.check-staleness.outputs.stale-count }}
          THRESHOLD_DAYS: ${{ inputs.max-age-days || 30 }}
          REPO: ${{ github.repository }}
          SERVER_URL: ${{ github.server_url }}
          RUN_ID: ${{ github.run_id }}
        run: |
          $staleCount = [int]$env:STALE_COUNT
          $thresholdDays = $env:THRESHOLD_DAYS
          $automationMarker = '<!-- automation:sha-staleness -->'

          $searchQuery = "repo:$env:REPO is:issue is:open in:body `"automation:sha-staleness`""
          $existingIssue = gh issue list `
            --repo $env:REPO `
            --search $searchQuery `
            --limit 1 `
            --json number `
            --jq '.[0].number // empty'

          $existingIssue = if ($existingIssue) { $existingIssue.Trim() } else { $null }

          # Build stale dependency details from the report artifact
          $staleDetails = ''
          $resultsPath = 'logs/sha-staleness-results.json'
          if (Test-Path $resultsPath) {
            $report = Get-Content $resultsPath | ConvertFrom-Json
            foreach ($dep in $report.Dependencies) {
              if ($dep.DaysOld -gt [int]$thresholdDays) {
                $staleDetails += "- **$($dep.File):** ``$($dep.Action)@$($dep.CurrentVersion)`` — $($dep.DaysOld) days old (latest: ``$($dep.LatestVersion)``)`n"
              }
            }
          }

          $issueTitle = 'Security: Stale dependency SHAs detected'
          $issueBody = @"
          ## Stale GitHub Action SHAs Detected

          **$staleCount** dependencies have SHA pins older than **$thresholdDays** days.

          ### Stale Dependencies

          $staleDetails

          ---

          **Workflow Run:** $env:SERVER_URL/$env:REPO/actions/runs/$env:RUN_ID
          **Detection Date:** $(Get-Date -Format 'yyyy-MM-dd' -AsUTC)

          ### Action Required

          - [ ] Run ``npm run sha-staleness`` to review current state
          - [ ] Update outdated SHAs: ``scripts/security/Update-ActionSHAPinning.ps1 -Path .github/workflows -UpdateStale``
          - [ ] Close this issue manually after fixes are merged

          $automationMarker
          "@

          if ($staleCount -gt 0) {
            if ($existingIssue) {
              Write-Host "Updating existing issue #$existingIssue"
              gh issue edit $existingIssue `
                --repo $env:REPO `
                --body $issueBody

              $updateDate = Get-Date -Format 'yyyy-MM-dd' -AsUTC
              gh issue comment $existingIssue `
                --repo $env:REPO `
                --body "🔄 **Weekly staleness update:** $staleCount stale SHAs detected as of $updateDate"
            } else {
              Write-Host "Creating new staleness issue"
              gh issue create `
                --repo $env:REPO `
                --title $issueTitle `
                --body $issueBody `
                --label "security,staleness,automated,needs-triage"
            }
          } elseif ($existingIssue) {
            gh issue comment $existingIssue `
              --repo $env:REPO `
              --body "✅ All action SHAs are now within the $thresholdDays-day freshness threshold. Closing automatically."

            gh issue close $existingIssue `
              --repo $env:REPO
          } else {
            Write-Host "No stale SHAs and no existing issue — nothing to do"
          }
