name: Repository Cleanup

on:
  schedule:
    # Registry cleanup: run daily at 03:00 UTC
    - cron: '0 3 * * *'
    # Artifact cleanup and storage check: run every 6 hours
    - cron: '0 */6 * * *'
  workflow_dispatch:
    inputs:
      target:
        description: 'Cleanup target'
        required: true
        default: 'all'
        type: choice
        options:
          - all
          - artifacts
          - registry
      registry_dry_run:
        description: 'Dry run registry cleanup'
        required: true
        default: 'true'
        type: boolean

permissions:
  contents: read
  actions: write

jobs:
  cleanup-and-check-artifacts:
    if: >-
      github.event_name == 'workflow_dispatch' &&
      (github.event.inputs.target == 'all' || github.event.inputs.target == 'artifacts') ||
      github.event_name == 'schedule' &&
      github.event.schedule == '0 */6 * * *'
    runs-on: ubuntu-latest

    steps:
      - name: Delete expired artifacts and artifacts older than 3 days
        env:
          GH_TOKEN: ${{ github.token }}
        run: |
          echo "Fetching all artifacts..."
          gh api "repos/${{ github.repository }}/actions/artifacts" \
            --paginate -q '.artifacts[] | "\(.id)\t\(.created_at)\t\(.expired)\t\(.name)"' > /tmp/all_artifacts.txt

          total=$(wc -l < /tmp/all_artifacts.txt)
          echo "Found $total total artifacts"

          cutoff=$(date -u -d '3 days ago' '+%Y-%m-%dT%H:%M:%SZ')
          echo "Deleting expired artifacts and artifacts created before $cutoff"

          deleted=0
          while IFS=$'\t' read -r id created_at expired name; do
            if [[ "$expired" == "true" || "$created_at" < "$cutoff" ]]; then
              gh api -X DELETE "repos/${{ github.repository }}/actions/artifacts/$id" 2>/dev/null && deleted=$((deleted + 1))
              if [ $((deleted % 50)) -eq 0 ]; then
                echo "Deleted $deleted artifacts so far..."
              fi
            fi
          done < /tmp/all_artifacts.txt

          echo "Cleanup complete. Deleted $deleted artifacts out of $total total."
          rm -f /tmp/all_artifacts.txt

      - name: Check artifact storage size
        env:
          GH_TOKEN: ${{ github.token }}
        run: |
          # Calculate active artifact storage. The artifacts API can list expired
          # artifacts until they are explicitly deleted, so do not count them.
          total_bytes=$(gh api "repos/${{ github.repository }}/actions/artifacts" \
            --paginate -q '.artifacts[] | select(.expired == false) | .size_in_bytes' | awk '{sum+=$1} END {print sum}')

          total_mb=$(( ${total_bytes:-0} / 1024 / 1024 ))
          echo "Total artifact storage: ${total_mb} MB"

          # Fail if over 500MB
          if [ "$total_mb" -gt 500 ]; then
            echo "::error::Artifact storage is ${total_mb} MB, which exceeds the 500 MB threshold!"
            echo ""
            echo "Largest artifacts:"
            # Save to temp file to avoid SIGPIPE/broken pipe errors with head
            gh api "repos/${{ github.repository }}/actions/artifacts" \
              --paginate -q '.artifacts[] | select(.expired == false) | "\(.size_in_bytes)\t\(.name)\t\(.created_at)"' > /tmp/artifacts.txt
            sort -rn /tmp/artifacts.txt | head -20 | \
              awk -F'\t' '{printf "%d MB\t%s\t%s\n", $1/1024/1024, $2, $3}'
            rm -f /tmp/artifacts.txt
            exit 1
          fi

          echo "Storage is within acceptable limits."

  cleanup-registry:
    if: >-
      github.event_name == 'workflow_dispatch' &&
      (github.event.inputs.target == 'all' || github.event.inputs.target == 'registry') ||
      github.event_name == 'schedule' &&
      github.event.schedule == '0 3 * * *'
    runs-on: ubuntu-latest

    steps:
      - name: Install doctl
        uses: digitalocean/action-doctl@v2
        with:
          token: ${{ secrets.DO_REGISTRY_TOKEN }}

      - name: Cleanup old images
        env:
          DRY_RUN: ${{ github.event_name == 'workflow_dispatch' && github.event.inputs.registry_dry_run == 'true' }}
        run: |
          set -e

          RETENTION_DAYS=5
          CUTOFF_DATE=$(date -d "-${RETENTION_DAYS} days" +%s)
          REGISTRY="eagle0"

          echo "Cleaning up images older than ${RETENTION_DAYS} days"
          echo "Cutoff date: $(date -d "@${CUTOFF_DATE}" -Iseconds)"
          echo "Dry run: ${DRY_RUN}"
          echo ""

          # List repositories via JSON. Text output can wrap table rows and make
          # headers/tags/digests look like repository names.
          REPOS=$(doctl registry repository list-v2 "${REGISTRY}" --output json | jq -r '.[] | .name // .Name // empty')

          for REPO in $REPOS; do
            echo "=== Processing repository: ${REPO} ==="

            # Get all manifests with their tags and dates using JSON output for reliable parsing
            MANIFESTS_JSON=$(doctl registry repository list-manifests "${REPO}" --output json 2>/dev/null || echo "[]")

            if [ "$MANIFESTS_JSON" = "[]" ] || [ -z "$MANIFESTS_JSON" ]; then
              echo "  No manifests found"
              continue
            fi

            # Parse JSON and process each manifest
            echo "$MANIFESTS_JSON" | jq -r '.[] | "\(.digest) \(.updated_at) \(.tags // [] | join(","))"' | while read -r DIGEST UPDATED_AT TAGS; do
              # Skip if no digest or if it doesn't look like a valid digest (sha256:...)
              if [ -z "$DIGEST" ] || ! echo "$DIGEST" | grep -q '^sha256:'; then
                continue
              fi

              # Parse the date (ISO 8601 format from JSON)
              MANIFEST_DATE=$(date -d "$UPDATED_AT" +%s 2>/dev/null || echo "0")

              # Skip protected tags (latest, arm64-latest)
              if echo ",$TAGS," | grep -qE ',(latest|arm64-latest),'; then
                echo "  KEEP: ${DIGEST:0:20}... (protected tag: $TAGS)"
                continue
              fi

              # Check if older than cutoff
              if [ "$MANIFEST_DATE" -lt "$CUTOFF_DATE" ]; then
                echo "  DELETE: ${DIGEST:0:20}... (updated: $UPDATED_AT, tags: $TAGS)"
                if [ "$DRY_RUN" != "true" ]; then
                  doctl registry repository delete-manifest "${REPO}" "$DIGEST" --force
                fi
              else
                echo "  KEEP: ${DIGEST:0:20}... (updated: $UPDATED_AT, tags: $TAGS)"
              fi
            done

            echo ""
          done

      - name: Run garbage collection
        if: github.event_name == 'schedule' || (github.event_name == 'workflow_dispatch' && github.event.inputs.registry_dry_run == 'false')
        run: |
          echo "Starting garbage collection..."
          set +e
          GC_OUTPUT=$(doctl registry garbage-collection start --force 2>&1)
          GC_STATUS=$?
          set -e

          echo "$GC_OUTPUT"
          if [ "$GC_STATUS" -eq 0 ]; then
            echo "Garbage collection started. It may take a few minutes to complete."
            exit 0
          fi

          if echo "$GC_OUTPUT" | grep -q "automated garbage collection is enabled"; then
            echo "Automated garbage collection is enabled for this registry; skipping manual garbage collection."
            exit 0
          fi

          exit "$GC_STATUS"
