name: Validate Release Assets

on:
  workflow_call:
    inputs:
      tag:
        description: 'Release tag (e.g., v4.29.0)'
        required: true
        type: string
      version:
        description: 'Version number without v prefix (e.g., 4.29.0)'
        required: true
        type: string
      release_id:
        description: 'GitHub release ID'
        required: true
        type: string
      draft:
        description: 'Whether the release is still a draft'
        required: true
        type: boolean
      target_commitish:
        description: 'Commit SHA associated with the release'
        required: true
        type: string
  release:
    types: [edited]
  workflow_dispatch:
    inputs:
      tag:
        description: 'Release tag (e.g., v4.29.0)'
        required: true
        type: string
      version:
        description: 'Version number without v prefix (e.g., 4.29.0)'
        required: true
        type: string
      release_id:
        description: 'GitHub release ID'
        required: true
        type: string
      draft:
        description: 'Set to true to run against a draft release'
        required: true
        type: boolean
      target_commitish:
        description: 'Commit SHA associated with the release'
        required: true
        type: string

jobs:
  validate:
    runs-on: ubuntu-latest
    permissions:
      contents: write
      issues: write

    steps:
      - name: Checkout repository
        uses: actions/checkout@v4

      - name: Determine release context
        id: context
        env:
          EVENT_NAME: ${{ github.event_name }}
          INPUT_TAG: ${{ inputs.tag }}
          INPUT_VERSION: ${{ inputs.version }}
          INPUT_RELEASE_ID: ${{ inputs.release_id }}
          INPUT_DRAFT: ${{ inputs.draft }}
          INPUT_COMMIT: ${{ inputs.target_commitish }}
        run: |
          python3 <<'EOF' > context.env
          import json, os, sys

          event_name = os.environ.get("EVENT_NAME", "")
          result = {}

          if event_name == "release":
              with open(os.environ["GITHUB_EVENT_PATH"], "r", encoding="utf-8") as handle:
                  data = json.load(handle)
              release = data.get("release") or {}
              result["tag"] = release.get("tag_name", "")
              tag = result["tag"]
              result["version"] = tag[1:] if tag.startswith("v") else tag
              result["release_id"] = str(release.get("id", ""))
              result["target_commitish"] = release.get("target_commitish", "")
              result["draft"] = str(release.get("draft", False)).lower()
          else:
              result["tag"] = os.environ.get("INPUT_TAG", "")
              result["version"] = os.environ.get("INPUT_VERSION", "")
              result["release_id"] = os.environ.get("INPUT_RELEASE_ID", "")
              result["target_commitish"] = os.environ.get("INPUT_COMMIT", "")
              draft_value = os.environ.get("INPUT_DRAFT", "false")
              result["draft"] = str(draft_value).lower()

          if not result["tag"] or not result["release_id"]:
              sys.stderr.write("::error::Release metadata is missing. Provide tag, version, release_id, and target_commitish.\n")
              sys.exit(1)

          result["should_run"] = "true"

          for key, value in result.items():
              print(f"{key}={value}")
          EOF
          cat context.env >> "$GITHUB_OUTPUT"
          cat context.env

      - name: Download all release assets
        if: steps.context.outputs.should_run == 'true'
        id: download
        env:
          GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
        run: |
          echo "Downloading all assets from release ${{ steps.context.outputs.tag }}..."

          mkdir -p release
          cd release

          # Get asset info (id and name) - API works with GITHUB_TOKEN for draft releases
          # Use --paginate to handle releases with >30 assets
          ASSETS_JSON=$(gh api --paginate "repos/${{ github.repository }}/releases/${{ steps.context.outputs.release_id }}/assets")

          if [ "$(echo "$ASSETS_JSON" | jq '. | length')" -eq 0 ]; then
            echo "::error::No assets found in release"
            echo "has_assets=false" >> $GITHUB_OUTPUT
            exit 1
          fi

          echo "has_assets=true" >> $GITHUB_OUTPUT
          echo "Found $(echo "$ASSETS_JSON" | jq '. | length') assets"

          # Download each asset using the API (works with draft releases)
          echo "$ASSETS_JSON" | jq -r '.[] | "\(.id) \(.name)"' | while read -r asset_id filename; do
            echo "Downloading $filename (ID: $asset_id)..."

            # Use GitHub API to download with authentication
            curl -L -H "Authorization: token $GH_TOKEN" \
              -H "Accept: application/octet-stream" \
              "https://api.github.com/repos/${{ github.repository }}/releases/assets/$asset_id" \
              -o "$filename"

            if [ $? -eq 0 ] && [ -f "$filename" ]; then
              echo "✓ Downloaded $filename ($(du -h "$filename" | cut -f1))"
            else
              echo "::error::Failed to download $filename"
              exit 1
            fi
          done

          echo ""
          echo "All assets downloaded:"
          ls -lh

      - name: Install Docker
        if: steps.context.outputs.should_run == 'true'
        run: docker --version

      - name: Pull Docker image (with retry logic)
        if: steps.context.outputs.should_run == 'true'
        id: docker
        continue-on-error: true
        run: |
          IMAGE="rcourtman/pulse:${{ steps.context.outputs.tag }}"
          echo "Attempting to pull Docker image: $IMAGE"
          echo "Docker Hub CDN propagation can take 2-5 minutes after push..."
          echo ""

          # Retry logic: 10 attempts with exponential backoff
          # Total time: ~10 minutes max
          MAX_ATTEMPTS=10
          ATTEMPT=1
          WAIT_TIME=30

          while [ $ATTEMPT -le $MAX_ATTEMPTS ]; do
            echo "Attempt $ATTEMPT/$MAX_ATTEMPTS: Pulling image..."

            if docker pull "$IMAGE" 2>/dev/null; then
              echo "✓ Docker image available: $IMAGE"
              echo "image_available=true" >> $GITHUB_OUTPUT
              echo "image=$IMAGE" >> $GITHUB_OUTPUT
              exit 0
            fi

            if [ $ATTEMPT -lt $MAX_ATTEMPTS ]; then
              echo "⚠️  Image not yet available, waiting ${WAIT_TIME}s before retry..."
              sleep $WAIT_TIME
              WAIT_TIME=$((WAIT_TIME * 2))  # Exponential backoff
              if [ $WAIT_TIME -gt 120 ]; then
                WAIT_TIME=120  # Cap at 2 minutes
              fi
            fi

            ATTEMPT=$((ATTEMPT + 1))
          done

          echo "⚠️  Docker image not available after $MAX_ATTEMPTS attempts"
          echo "⚠️  Will skip Docker image validation"
          echo "image_available=false" >> $GITHUB_OUTPUT

      - name: Run validation script
        if: steps.context.outputs.should_run == 'true'
        id: validate
        run: |
          set +e
          echo "Running validation script..."
          chmod +x scripts/validate-release.sh
          OUTPUT_FILE=$(mktemp)

          if [ "${{ steps.docker.outputs.image_available }}" = "true" ]; then
            echo "Running full validation (Docker + assets)..."
            scripts/validate-release.sh \
              "${{ steps.context.outputs.version }}" \
              "${{ steps.docker.outputs.image }}" \
              "release" 2>&1 | tee "$OUTPUT_FILE"
          else
            echo "Running assets-only validation (Docker image not available)..."
            scripts/validate-release.sh \
              "${{ steps.context.outputs.version }}" \
              --skip-docker \
              "release" 2>&1 | tee "$OUTPUT_FILE"
          fi

          VALIDATION_EXIT_CODE=$?

          echo "VALIDATION_OUTPUT<<EOF" >> $GITHUB_OUTPUT
          cat "$OUTPUT_FILE" >> $GITHUB_OUTPUT
          echo "EOF" >> $GITHUB_OUTPUT

          if [ $VALIDATION_EXIT_CODE -eq 0 ]; then
            echo "validation_passed=true" >> $GITHUB_OUTPUT
            echo "✅ Validation PASSED"
          else
            echo "validation_passed=false" >> $GITHUB_OUTPUT
            echo "❌ Validation FAILED"
          fi

          exit $VALIDATION_EXIT_CODE

      - name: Set commit status - Success
        if: steps.context.outputs.should_run == 'true' && steps.validate.outputs.validation_passed == 'true'
        run: |
          curl -X POST \
            -H "Authorization: token ${{ secrets.GITHUB_TOKEN }}" \
            -H "Accept: application/vnd.github+json" \
            "https://api.github.com/repos/${{ github.repository }}/statuses/${{ steps.context.outputs.target_commitish }}" \
            -d '{
              "state": "success",
              "target_url": "${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}",
              "description": "All release assets validated successfully",
              "context": "Release Asset Validation"
            }'

      - name: Update release body - Success
        if: steps.context.outputs.should_run == 'true' && steps.validate.outputs.validation_passed == 'true'
        run: |
          echo "✅ Validation passed - updating release description"

          INITIAL_STATE="${{ steps.context.outputs.draft }}"
          if [ "$INITIAL_STATE" = "true" ]; then
              HEADER_LINE="## ✅ Release Asset Validation: PASSED"
              STATUS_LINE="**Status**: Ready for publication ✅"
              INTRO_LINE="All release assets have been validated successfully!"
          else
              HEADER_LINE="## ✅ Release Asset Validation (Post-Publish): PASSED"
              STATUS_LINE="**Status**: Live release assets re-validated ✅"
              INTRO_LINE="Assets were revalidated after publication due to a release edit."
          fi

          CURRENT_BODY=$(curl -s -H "Authorization: token ${{ secrets.GITHUB_TOKEN }}" \
            "https://api.github.com/repos/${{ github.repository }}/releases/${{ steps.context.outputs.release_id }}" \
            | jq -r '.body // ""')

          CURRENT_BODY=$(echo "$CURRENT_BODY" | sed '/<!-- VALIDATION_STATUS_START -->/,/<!-- VALIDATION_STATUS_END -->/d')

          read -r -d '' VALIDATION_BLOCK <<'EOF' || true
          <!-- VALIDATION_STATUS_START -->
          HEADER_PLACEHOLDER

          INTRO_PLACEHOLDER

          STATUS_PLACEHOLDER
          **Validated**: TIMESTAMP_PLACEHOLDER
          **Workflow**: WORKFLOW_LINK_PLACEHOLDER

          ### Validation Summary
          - All required assets present ✓
          - Checksums verified ✓
          - Version strings correct ✓
          - Binary architectures validated ✓

          <!-- VALIDATION_STATUS_END -->
          EOF

          VALIDATION_BLOCK="${VALIDATION_BLOCK//HEADER_PLACEHOLDER/$HEADER_LINE}"
          VALIDATION_BLOCK="${VALIDATION_BLOCK//STATUS_PLACEHOLDER/$STATUS_LINE}"
          VALIDATION_BLOCK="${VALIDATION_BLOCK//INTRO_PLACEHOLDER/$INTRO_LINE}"
          VALIDATION_BLOCK="${VALIDATION_BLOCK//TIMESTAMP_PLACEHOLDER/$(date -u +"%Y-%m-%d %H:%M:%S UTC")}"
          VALIDATION_BLOCK="${VALIDATION_BLOCK//WORKFLOW_LINK_PLACEHOLDER/[${{ github.workflow }} #${{ github.run_number }}](${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }})}"

          NEW_BODY="$VALIDATION_BLOCK

          $CURRENT_BODY"

          curl -X PATCH \
            -H "Authorization: token ${{ secrets.GITHUB_TOKEN }}" \
            -H "Accept: application/vnd.github+json" \
            "https://api.github.com/repos/${{ github.repository }}/releases/${{ steps.context.outputs.release_id }}" \
            -d "$(jq -n --arg body "$NEW_BODY" '{body: $body}')"

      - name: Set commit status - Failure
        if: steps.context.outputs.should_run == 'true' && (failure() || steps.validate.outputs.validation_passed == 'false')
        run: |
          curl -X POST \
            -H "Authorization: token ${{ secrets.GITHUB_TOKEN }}" \
            -H "Accept: application/vnd.github+json" \
            "https://api.github.com/repos/${{ github.repository }}/statuses/${{ steps.context.outputs.target_commitish }}" \
            -d '{
              "state": "failure",
              "target_url": "${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}",
              "description": "Release asset validation failed",
              "context": "Release Asset Validation"
            }'

      - name: Delete all release assets on failure
        if: steps.context.outputs.should_run == 'true' && (failure() || steps.validate.outputs.validation_passed == 'false')
        run: |
          echo "❌ Validation failed - deleting all release assets"

          INITIAL_STATE="${{ steps.context.outputs.draft }}"
          if [ "$INITIAL_STATE" != "true" ]; then
            echo "Release was published; reverting to draft before deleting assets..."
            curl -X PATCH \
              -H "Authorization: token ${{ secrets.GITHUB_TOKEN }}" \
              -H "Accept: application/vnd.github+json" \
              "https://api.github.com/repos/${{ github.repository }}/releases/${{ steps.context.outputs.release_id }}" \
              -d '{"draft": true}'
            echo "Release marked as draft to block downloads during remediation."
            sleep 5
          fi

          ASSET_IDS=$(curl -s -H "Authorization: token ${{ secrets.GITHUB_TOKEN }}" \
            "https://api.github.com/repos/${{ github.repository }}/releases/${{ steps.context.outputs.release_id }}/assets" \
            | jq -r '.[].id')

          if [ -n "$ASSET_IDS" ]; then
            echo "$ASSET_IDS" | while read -r asset_id; do
              if [ -n "$asset_id" ] && [ "$asset_id" != "null" ]; then
                echo "Deleting asset ID: $asset_id"
                curl -X DELETE \
                  -H "Authorization: token ${{ secrets.GITHUB_TOKEN }}" \
                  "https://api.github.com/repos/${{ github.repository }}/releases/assets/$asset_id"
              fi
            done
            echo "✓ Asset deletion process completed"
          else
            echo "No assets to delete"
          fi

      - name: Update release body - Failure
        if: steps.context.outputs.should_run == 'true' && (failure() || steps.validate.outputs.validation_passed == 'false')
        run: |
          echo "❌ Validation failed - updating release description"

          INITIAL_STATE="${{ steps.context.outputs.draft }}"
          if [ "$INITIAL_STATE" = "true" ]; then
              HEADER_LINE="## ❌ Release Asset Validation: FAILED"
              STATUS_LINE="**Status**: ⛔ Draft release blocked until validation passes"
              INTRO_LINE="Release assets failed validation checks. All assets have been deleted to prevent publishing an invalid release."
          else
              HEADER_LINE="## ❌ Release Asset Validation (Post-Publish): FAILED"
              STATUS_LINE="**Status**: ⛔ Release reverted to draft while assets are rebuilt"
              INTRO_LINE="A published release edit introduced invalid assets. The release has been reverted to draft and assets were deleted to stop further downloads."
          fi

          CURRENT_BODY=$(curl -s -H "Authorization: token ${{ secrets.GITHUB_TOKEN }}" \
            "https://api.github.com/repos/${{ github.repository }}/releases/${{ steps.context.outputs.release_id }}" \
            | jq -r '.body // ""')

          CURRENT_BODY=$(echo "$CURRENT_BODY" | sed '/<!-- VALIDATION_STATUS_START -->/,/<!-- VALIDATION_STATUS_END -->/d')

          VALIDATION_OUTPUT="${{ steps.validate.outputs.VALIDATION_OUTPUT }}"
          if [ -n "$VALIDATION_OUTPUT" ]; then
            ERRORS=$(echo "$VALIDATION_OUTPUT" | grep -i '\[ERROR\]' | head -20 || echo "See workflow logs for details")
          else
            ERRORS="Validation script failed to run. Check workflow logs."
          fi

          read -r -d '' VALIDATION_BLOCK <<'EOF' || true
          <!-- VALIDATION_STATUS_START -->
          HEADER_PLACEHOLDER

          INTRO_PLACEHOLDER

          STATUS_PLACEHOLDER
          **Failed**: TIMESTAMP_PLACEHOLDER
          **Workflow**: WORKFLOW_LINK_PLACEHOLDER

          ### What Happened
          The automated validation process detected issues with the uploaded release assets. This could be due to:
          - Missing required files
          - Checksum mismatches
          - Incorrect version strings in binaries
          - Corrupted or incomplete uploads

          ### Action Required
          1. Review the validation errors in the workflow logs
          2. Fix the underlying issues in the release build process
          3. Re-upload the corrected assets
          4. Validation will run automatically when assets are edited

          ### Validation Errors (first 20 lines)
          ```
          ERRORS_PLACEHOLDER
          ```

          [View full workflow logs for complete details](WORKFLOW_URL_PLACEHOLDER)

          <!-- VALIDATION_STATUS_END -->
          EOF

          VALIDATION_BLOCK="${VALIDATION_BLOCK//HEADER_PLACEHOLDER/$HEADER_LINE}"
          VALIDATION_BLOCK="${VALIDATION_BLOCK//STATUS_PLACEHOLDER/$STATUS_LINE}"
          VALIDATION_BLOCK="${VALIDATION_BLOCK//INTRO_PLACEHOLDER/$INTRO_LINE}"
          VALIDATION_BLOCK="${VALIDATION_BLOCK//TIMESTAMP_PLACEHOLDER/$(date -u +"%Y-%m-%d %H:%M:%S UTC")}"
          VALIDATION_BLOCK="${VALIDATION_BLOCK//WORKFLOW_LINK_PLACEHOLDER/[${{ github.workflow }} #${{ github.run_number }}](${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }})}"
          VALIDATION_BLOCK="${VALIDATION_BLOCK//WORKFLOW_URL_PLACEHOLDER/${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}}"
          VALIDATION_BLOCK="${VALIDATION_BLOCK//ERRORS_PLACEHOLDER/$ERRORS}"

          NEW_BODY="$VALIDATION_BLOCK

          $CURRENT_BODY"

          curl -X PATCH \
            -H "Authorization: token ${{ secrets.GITHUB_TOKEN }}" \
            -H "Accept: application/vnd.github+json" \
            "https://api.github.com/repos/${{ github.repository }}/releases/${{ steps.context.outputs.release_id }}" \
            -d "$(jq -n --arg body "$NEW_BODY" '{body: $body}')"

      - name: Fail the workflow
        if: steps.context.outputs.should_run == 'true' && (failure() || steps.validate.outputs.validation_passed == 'false')
        run: |
          echo "::error::Release asset validation failed. All assets have been deleted."
          exit 1
