name: Auto PR V2 Deployment

on:
  pull_request:
    types: [opened, synchronize, reopened, closed]


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

jobs:
  check-pr:
    if: github.event.action != 'closed'
    runs-on: ubuntu-latest
    outputs:
      should_deploy: ${{ steps.check-conditions.outputs.should_deploy }}
      pr_number: ${{ github.event.number }}
      pr_repository: ${{ steps.get-pr-info.outputs.repository }}
      pr_ref: ${{ steps.get-pr-info.outputs.ref }}
    steps:
      - name: Harden Runner
        uses: step-security/harden-runner@002fdce3c6a235733a90a27c80493a3241e56863 # v2.12.1
        with:
          egress-policy: audit

      - name: Check deployment conditions
        id: check-conditions
        env:
          PR_TITLE: ${{ github.event.pull_request.title }}
          PR_AUTHOR: ${{ github.event.pull_request.user.login }}
          PR_BRANCH: ${{ github.event.pull_request.head.ref }}
        run: |
          echo "PR Title: $PR_TITLE"
          echo "PR Author: $PR_AUTHOR"
          echo "PR Branch: $PR_BRANCH"
          echo "PR Base Branch: ${{ github.event.pull_request.base.ref }}"
          
          # Define authorized users
          authorized_users=(
            "Frooodle"
            "sf298"
            "Ludy87"
            "LaserKaspar"
            "sbplat"
            "reecebrowne"
            "DarioGii"
            "ConnorYoh"
            "EthanHealy01"
            "jbrunton96"
          )
          
          # Check if author is in the authorized list
          is_authorized=false
          for user in "${authorized_users[@]}"; do
            if [[ "$PR_AUTHOR" == "$user" ]]; then
              is_authorized=true
              break
            fi
          done
          
          # If PR is targeting V2 and user is authorized, deploy unconditionally
          PR_BASE_BRANCH="${{ github.event.pull_request.base.ref }}"
          if [[ "$PR_BASE_BRANCH" == "V2" && "$is_authorized" == "true" ]]; then
            echo "✅ Deployment forced: PR targets V2 and author is authorized."
            echo "should_deploy=true" >> $GITHUB_OUTPUT
            exit 0
          fi
          
          # Otherwise, continue with original keyword checks
          has_v2_keyword=false
          if [[ "$PR_TITLE" =~ [Vv]2 ]] || [[ "$PR_TITLE" =~ [Vv]ersion.?2 ]] || [[ "$PR_TITLE" =~ [Vv]ersion.?[Tt]wo ]]; then
            has_v2_keyword=true
          fi
          
          has_branch_keyword=false
          if [[ "$PR_BRANCH" =~ [Vv]2 ]] || [[ "$PR_BRANCH" =~ [Rr]eact ]]; then
            has_branch_keyword=true
          fi
          
          if [[ "$is_authorized" == "true" && ( "$has_v2_keyword" == "true" || "$has_branch_keyword" == "true" ) ]]; then
            echo "✅ Deployment conditions met"
            echo "should_deploy=true" >> $GITHUB_OUTPUT
          else
            echo "❌ Deployment conditions not met"
            echo "  - Authorized user: $is_authorized"
            echo "  - Has V2 keyword in title: $has_v2_keyword"
            echo "  - Has V2/React keyword in branch: $has_branch_keyword"
            echo "should_deploy=false" >> $GITHUB_OUTPUT
          fi

      - name: Get PR repository and ref
        id: get-pr-info
        if: steps.check-conditions.outputs.should_deploy == 'true'
        run: |
          # For forks, use the full repository name, for internal PRs use the current repo
          if [[ "${{ github.event.pull_request.head.repo.fork }}" == "true" ]]; then
            repository="${{ github.event.pull_request.head.repo.full_name }}"
          else
            repository="${{ github.repository }}"
          fi
          
          echo "repository=$repository" >> $GITHUB_OUTPUT
          echo "ref=${{ github.event.pull_request.head.ref }}" >> $GITHUB_OUTPUT

  deploy-v2-pr:
    needs: check-pr
    runs-on: ubuntu-latest
    if: needs.check-pr.outputs.should_deploy == 'true'
    # Concurrency control - only one deployment per PR at a time
    concurrency:
      group: v2-deploy-pr-${{ needs.check-pr.outputs.pr_number }}
      cancel-in-progress: true
    permissions:
      contents: read
      issues: write
      pull-requests: write

    steps:
      - name: Harden Runner
        uses: step-security/harden-runner@002fdce3c6a235733a90a27c80493a3241e56863 # v2.12.1
        with:
          egress-policy: audit

      - name: Checkout main repository
        uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
        with:
          repository: ${{ github.repository }}
          ref: main

      - name: Setup GitHub App Bot
        if: github.actor != 'dependabot[bot]'
        id: setup-bot
        uses: ./.github/actions/setup-bot
        continue-on-error: true
        with:
          app-id: ${{ secrets.GH_APP_ID }}
          private-key: ${{ secrets.GH_APP_PRIVATE_KEY }}

      - name: Add deployment started comment
        id: deployment-started
        uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1
        with:
          github-token: ${{ steps.setup-bot.outputs.token }}
          script: |
            const { owner, repo } = context.repo;
            const prNumber = ${{ needs.check-pr.outputs.pr_number }};
            
            // Delete previous V2 deployment comments to avoid clutter
            const { data: comments } = await github.rest.issues.listComments({
              owner,
              repo,
              issue_number: prNumber,
              per_page: 100
            });
            
            const v2Comments = comments.filter(comment => 
              comment.body.includes('🚀 **Auto-deploying V2 version**') ||
              comment.body.includes('## 🚀 V2 Auto-Deployment Complete!') ||
              comment.body.includes('❌ **V2 Auto-deployment failed**')
            );
            
            for (const comment of v2Comments) {
              console.log(`Deleting old V2 comment: ${comment.id}`);
              await github.rest.issues.deleteComment({
                owner,
                repo,
                comment_id: comment.id
              });
            }
            
            // Create new deployment started comment
            const { data: newComment } = await github.rest.issues.createComment({
              owner,
              repo,
              issue_number: prNumber,
              body: `🚀 **Auto-deploying V2 version** for PR #${prNumber}...\n\n_This is an automated deployment triggered by V2/version2 keywords in the PR title or V2/React keywords in the branch name._\n\n⚠️ **Note:** If new commits are pushed during deployment, this build will be cancelled and replaced with the latest version.`
            });
            
            return newComment.id;

      - name: Checkout PR
        uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
        with:
          repository: ${{ needs.check-pr.outputs.pr_repository }}
          ref: ${{ needs.check-pr.outputs.pr_ref }}
          token: ${{ secrets.GITHUB_TOKEN }}
          fetch-depth: 0  # Fetch full history for commit hash detection


      - name: Set up Docker Buildx
        uses: docker/setup-buildx-action@e468171a9de216ec08956ac3ada2f0791b6bd435 # v3.11.1

      - name: Get version number
        id: versionNumber
        run: |
          VERSION=$(grep "^version =" build.gradle | awk -F'"' '{print $2}')
          echo "versionNumber=$VERSION" >> $GITHUB_OUTPUT

      - name: Login to Docker Hub
        uses: docker/login-action@74a5d142397b4f367a81961eba4e8cd7edddf772 # v3.4.0
        with:
          username: ${{ secrets.DOCKER_HUB_USERNAME }}
          password: ${{ secrets.DOCKER_HUB_API }}

      - name: Get commit hashes for frontend and backend
        id: commit-hashes
        run: |
          # Get last commit that touched the frontend folder, docker/frontend, or docker/compose
          FRONTEND_HASH=$(git log -1 --format="%H" -- frontend/ docker/frontend/ docker/compose/ 2>/dev/null || echo "")
          if [ -z "$FRONTEND_HASH" ]; then
            FRONTEND_HASH="no-frontend-changes"
          fi
          
          # Get last commit that touched backend code, docker/backend, or docker/compose
          BACKEND_HASH=$(git log -1 --format="%H" -- app/ docker/backend/ docker/compose/ 2>/dev/null || echo "")
          if [ -z "$BACKEND_HASH" ]; then
            BACKEND_HASH="no-backend-changes"
          fi
          
          echo "Frontend hash: $FRONTEND_HASH"
          echo "Backend hash: $BACKEND_HASH"
          
          echo "frontend_hash=$FRONTEND_HASH" >> $GITHUB_OUTPUT
          echo "backend_hash=$BACKEND_HASH" >> $GITHUB_OUTPUT
          
          # Short hashes for tags
          if [ "$FRONTEND_HASH" = "no-frontend-changes" ]; then
            echo "frontend_short=no-frontend" >> $GITHUB_OUTPUT
          else
            echo "frontend_short=${FRONTEND_HASH:0:8}" >> $GITHUB_OUTPUT
          fi
          
          if [ "$BACKEND_HASH" = "no-backend-changes" ]; then
            echo "backend_short=no-backend" >> $GITHUB_OUTPUT
          else
            echo "backend_short=${BACKEND_HASH:0:8}" >> $GITHUB_OUTPUT
          fi

      - name: Check if frontend image exists
        id: check-frontend
        run: |
          if docker manifest inspect ${{ secrets.DOCKER_HUB_USERNAME }}/test:v2-frontend-${{ steps.commit-hashes.outputs.frontend_short }} >/dev/null 2>&1; then
            echo "exists=true" >> $GITHUB_OUTPUT
            echo "Frontend image already exists, skipping build"
          else
            echo "exists=false" >> $GITHUB_OUTPUT
            echo "Frontend image needs to be built"
          fi

      - name: Check if backend image exists
        id: check-backend
        run: |
          if docker manifest inspect ${{ secrets.DOCKER_HUB_USERNAME }}/test:v2-backend-${{ steps.commit-hashes.outputs.backend_short }} >/dev/null 2>&1; then
            echo "exists=true" >> $GITHUB_OUTPUT
            echo "Backend image already exists, skipping build"
          else
            echo "exists=false" >> $GITHUB_OUTPUT
            echo "Backend image needs to be built"
          fi

      - name: Build and push V2 frontend image
        if: steps.check-frontend.outputs.exists == 'false'
        uses: docker/build-push-action@263435318d21b8e681c14492fe198d362a7d2c83 # v6.18.0
        with:
          context: .
          file: ./docker/frontend/Dockerfile
          push: true
          tags: ${{ secrets.DOCKER_HUB_USERNAME }}/test:v2-frontend-${{ steps.commit-hashes.outputs.frontend_short }}
          build-args: VERSION_TAG=v2-alpha
          platforms: linux/amd64

      - name: Build and push V2 backend image
        if: steps.check-backend.outputs.exists == 'false'
        uses: docker/build-push-action@263435318d21b8e681c14492fe198d362a7d2c83 # v6.18.0
        with:
          context: .
          file: ./docker/backend/Dockerfile
          push: true
          tags: ${{ secrets.DOCKER_HUB_USERNAME }}/test:v2-backend-${{ steps.commit-hashes.outputs.backend_short }}
          build-args: VERSION_TAG=v2-alpha
          platforms: linux/amd64

      - name: Set up SSH
        run: |
          mkdir -p ~/.ssh/
          echo "${{ secrets.VPS_SSH_KEY }}" > ../private.key
          sudo chmod 600 ../private.key

      - name: Deploy V2 to VPS
        id: deploy
        run: |
          # Use same port strategy as regular PRs - just the PR number
          V2_PORT=${{ needs.check-pr.outputs.pr_number }}
          BACKEND_PORT=$((V2_PORT + 10000))  # Backend on higher port to avoid conflicts
          
          # Create docker-compose for V2 with separate frontend and backend
          cat > docker-compose.yml << EOF
          version: '3.3'
          services:
            stirling-pdf-v2-backend:
              container_name: stirling-pdf-v2-backend-pr-${{ needs.check-pr.outputs.pr_number }}
              image: ${{ secrets.DOCKER_HUB_USERNAME }}/test:v2-backend-${{ steps.commit-hashes.outputs.backend_short }}
              ports:
                - "${BACKEND_PORT}:8080"    # Backend API port
              volumes:
                - /stirling/V2-PR-${{ needs.check-pr.outputs.pr_number }}/data:/usr/share/tessdata:rw
                - /stirling/V2-PR-${{ needs.check-pr.outputs.pr_number }}/config:/configs:rw
                - /stirling/V2-PR-${{ needs.check-pr.outputs.pr_number }}/logs:/logs:rw
              environment:
                DISABLE_ADDITIONAL_FEATURES: "true"
                SECURITY_ENABLELOGIN: "false"
                SYSTEM_DEFAULTLOCALE: en-GB
                UI_APPNAME: "Stirling-PDF V2 PR#${{ needs.check-pr.outputs.pr_number }}"
                UI_HOMEDESCRIPTION: "V2 PR#${{ needs.check-pr.outputs.pr_number }} - Frontend/Backend Split Architecture"
                UI_APPNAMENAVBAR: "V2 PR#${{ needs.check-pr.outputs.pr_number }}"
                SYSTEM_MAXFILESIZE: "100"
                METRICS_ENABLED: "true"
                SYSTEM_GOOGLEVISIBILITY: "false"
                SWAGGER_SERVER_URL: "https://${V2_PORT}.ssl.stirlingpdf.cloud"
                baseUrl: "https://${V2_PORT}.ssl.stirlingpdf.cloud"
              restart: on-failure:5
              
            stirling-pdf-v2-frontend:
              container_name: stirling-pdf-v2-frontend-pr-${{ needs.check-pr.outputs.pr_number }}
              image: ${{ secrets.DOCKER_HUB_USERNAME }}/test:v2-frontend-${{ steps.commit-hashes.outputs.frontend_short }}
              ports:
                - "${V2_PORT}:80"          # Frontend port (same as regular PRs)
              environment:
                VITE_API_BASE_URL: "http://${{ secrets.VPS_HOST }}:${BACKEND_PORT}"
              depends_on:
                - stirling-pdf-v2-backend
              restart: on-failure:5
          EOF

          # Deploy to VPS
          scp -i ../private.key -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null docker-compose.yml ${{ secrets.VPS_USERNAME }}@${{ secrets.VPS_HOST }}:/tmp/docker-compose-v2.yml

          ssh -i ../private.key -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -T ${{ secrets.VPS_USERNAME }}@${{ secrets.VPS_HOST }} << ENDSSH
            # Create V2 PR-specific directories
            mkdir -p /stirling/V2-PR-${{ needs.check-pr.outputs.pr_number }}/{data,config,logs}

            # Move docker-compose file to correct location
            mv /tmp/docker-compose-v2.yml /stirling/V2-PR-${{ needs.check-pr.outputs.pr_number }}/docker-compose.yml

            # Stop any existing container and clean up
            cd /stirling/V2-PR-${{ needs.check-pr.outputs.pr_number }}
            docker-compose down --remove-orphans 2>/dev/null || true
            
            # Start the new container
            docker-compose pull
            docker-compose up -d
            
            # Clean up unused Docker resources to save space
            docker system prune -af --volumes || true
            
            # Clean up old backend/frontend images (older than 2 weeks)
            docker image prune -af --filter "until=336h" --filter "label!=keep=true" || true
          ENDSSH

          # Set port for output
          echo "v2_port=${V2_PORT}" >> $GITHUB_OUTPUT

      - name: Post V2 deployment URL to PR
        if: success()
        uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1
        with:
          github-token: ${{ steps.setup-bot.outputs.token }}
          script: |
            const { owner, repo } = context.repo;
            const prNumber = ${{ needs.check-pr.outputs.pr_number }};
            const v2Port = ${{ steps.deploy.outputs.v2_port }};
            
            // Delete the "deploying..." comment since we're posting the final result
            const deploymentStartedId = ${{ steps.deployment-started.outputs.result }};
            if (deploymentStartedId) {
              console.log(`Deleting deployment started comment: ${deploymentStartedId}`);
              try {
                await github.rest.issues.deleteComment({
                  owner,
                  repo,
                  comment_id: deploymentStartedId
                });
              } catch (error) {
                console.log(`Could not delete deployment started comment: ${error.message}`);
              }
            }
            
            const deploymentUrl = `http://${{ secrets.VPS_HOST }}:${v2Port}`;
            const httpsUrl = `https://${v2Port}.ssl.stirlingpdf.cloud`;

            const commentBody = `## 🚀 V2 Auto-Deployment Complete!\n\n` +
                              `Your V2 PR with the new frontend/backend split architecture has been deployed!\n\n` +
                              `🔗 **Direct Test URL (non-SSL)** [${deploymentUrl}](${deploymentUrl})\n\n` +
                              `🔐 **Secure HTTPS URL**: [${httpsUrl}](${httpsUrl})\n\n` +
                              `_This deployment will be automatically cleaned up when the PR is closed._\n\n` +
                              `🔄 **Auto-deployed** because PR title or branch name contains V2/version2/React keywords.`;

            await github.rest.issues.createComment({
              owner,
              repo,
              issue_number: prNumber,
              body: commentBody
            });

  cleanup-v2-deployment:
    if: github.event.action == 'closed'
    runs-on: ubuntu-latest
    permissions:
      contents: read
      issues: write
      pull-requests: write

    steps:
      - name: Harden Runner
        uses: step-security/harden-runner@6c439dc8bdf85cadbbce9ed30d1c7b959517bc49 # v2.12.2
        with:
          egress-policy: audit

      - name: Checkout repository
        uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2

      - name: Setup GitHub App Bot
        if: github.actor != 'dependabot[bot]'
        id: setup-bot
        uses: ./.github/actions/setup-bot
        continue-on-error: true
        with:
          app-id: ${{ secrets.GH_APP_ID }}
          private-key: ${{ secrets.GH_APP_PRIVATE_KEY }}

      - name: Clean up V2 deployment comments
        uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1
        with:
          github-token: ${{ steps.setup-bot.outputs.token }}
          script: |
            const { owner, repo } = context.repo;
            const prNumber = ${{ github.event.pull_request.number }};
            
            // Find and delete V2 deployment comments
            const { data: comments } = await github.rest.issues.listComments({
              owner,
              repo,
              issue_number: prNumber
            });
            
            const v2Comments = comments.filter(c =>
              c.body?.includes("## 🚀 V2 Auto-Deployment Complete!") &&
              c.user?.type === "Bot"
            );
            
            for (const comment of v2Comments) {
              await github.rest.issues.deleteComment({
                owner,
                repo,
                comment_id: comment.id
              });
              console.log(`Deleted V2 deployment comment (ID: ${comment.id})`);
            }

      - name: Set up SSH
        run: |
          mkdir -p ~/.ssh/
          echo "${{ secrets.VPS_SSH_KEY }}" > ../private.key
          sudo chmod 600 ../private.key

      - name: Cleanup V2 deployment
        run: |
          ssh -i ../private.key -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -T ${{ secrets.VPS_USERNAME }}@${{ secrets.VPS_HOST }} << 'ENDSSH'
            if [ -d "/stirling/V2-PR-${{ github.event.pull_request.number }}" ]; then
              echo "Found V2 PR directory, proceeding with cleanup..."

              # Stop and remove V2 containers
              cd /stirling/V2-PR-${{ github.event.pull_request.number }}
              docker-compose down || true

              # Go back to root before removal
              cd /

              # Remove V2 PR-specific directories
              rm -rf /stirling/V2-PR-${{ github.event.pull_request.number }}

              # Clean up V2 containers by name (in case compose cleanup missed them)
              docker rm -f stirling-pdf-v2-frontend-pr-${{ github.event.pull_request.number }} || true
              docker rm -f stirling-pdf-v2-backend-pr-${{ github.event.pull_request.number }} || true

              echo "V2 cleanup completed"
            else
              echo "V2 PR directory not found, nothing to clean up"
            fi
            
            # Clean up old unused images (older than 2 weeks) but keep recent ones for reuse
            docker image prune -af --filter "until=336h" --filter "label!=keep=true" || true
            
            # Note: We don't remove the commit-based images since they can be reused across PRs
            # Only remove PR-specific containers and directories
          ENDSSH

      - name: Cleanup temporary files
        if: always()
        run: |
          rm -f ../private.key
        continue-on-error: true

