name: Release Calendar

# Creates release PRs on a schedule or manually via workflow_dispatch.
#
# HOW IT WORKS:
# 1. Dev → Staging: Creates snapshot branch (release/staging-YYYY-MM-DD) to prevent PR drift
# 2. Staging → Main: Opens PR directly from staging (no feature branch needed)
# 3. New commits to dev won't auto-update staging PRs - you control what's released
#
# SCHEDULE:
# • Friday 17:00 UTC (10am PT): Creates release/staging-* branch from dev → staging PR
# • Sunday 17:00 UTC (10am PT): Creates staging → main PR (direct from staging)
#
# MANUAL TRIGGER:
# Run via workflow_dispatch from main branch:
#   - staging: Creates dev snapshot → staging PR
#   - production: Creates staging → main PR (direct from staging)
#
# REQUIREMENTS:
# • Scheduled cron only runs when this file exists on the default branch (main)
# • Manual triggers work from any branch, but should run from main for consistency

on:
  workflow_dispatch:
    inputs:
      job_to_run:
        description: "Which job to run (staging: dev→staging, production: staging→main)"
        required: false
        type: choice
        options:
          - staging
          - production
        default: staging
  schedule:
    # Friday 17:00 UTC (see timezone conversions above) to prepare the weekend staging PR.
    - cron: "0 17 * * 5"
    # Sunday 17:00 UTC (same times as above) to prepare the production release PR.
    - cron: "0 17 * * 0"

jobs:
  release_to_staging:
    name: Create dev to staging release PR
    runs-on: ubuntu-latest
    permissions:
      contents: write
      pull-requests: write
      issues: write
    steps:
      - name: Guard Friday schedule
        id: guard_schedule
        shell: bash
        run: |
          set -euo pipefail

          # Allow workflow_dispatch based on input
          if [ "${{ github.event_name }}" == "workflow_dispatch" ]; then
            JOB_TO_RUN="${{ inputs.job_to_run }}"
            if [ "$JOB_TO_RUN" == "staging" ]; then
              echo "Manual trigger: running staging job (job_to_run=${JOB_TO_RUN})"
              echo "continue=true" >> "$GITHUB_OUTPUT"
            else
              echo "Manual trigger: skipping staging job (job_to_run=${JOB_TO_RUN})"
              echo "continue=false" >> "$GITHUB_OUTPUT"
            fi
            exit 0
          fi

          # For schedule events, check day of week
          DOW=$(date -u +%u)
          if [ "$DOW" != "5" ]; then
            echo "Not Friday in UTC (current day-of-week: $DOW). Exiting job early."
            echo "continue=false" >> "$GITHUB_OUTPUT"
            exit 0
          fi

          echo "continue=true" >> "$GITHUB_OUTPUT"

      - name: Check out repository
        if: ${{ steps.guard_schedule.outputs.continue == 'true' }}
        uses: actions/checkout@v6
        with:
          fetch-depth: 0

      - name: Check for existing dev to staging PR
        if: ${{ steps.guard_schedule.outputs.continue == 'true' }}
        id: check_dev_staging
        env:
          GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
        shell: bash
        run: |
          set -euo pipefail
          PR_DATE=$(date +%Y-%m-%d)
          BRANCH_NAME="release/staging-${PR_DATE}"
          echo "date=${PR_DATE}" >> "$GITHUB_OUTPUT"
          echo "branch_name=${BRANCH_NAME}" >> "$GITHUB_OUTPUT"

          echo "Checking for existing pull requests from ${BRANCH_NAME} to staging..."
          EXISTING_PR=$(gh pr list --base staging --head "${BRANCH_NAME}" --state open --limit 1 --json number --jq '.[0].number // ""')
          echo "existing_pr=${EXISTING_PR}" >> "$GITHUB_OUTPUT"

          if [ -n "$EXISTING_PR" ]; then
            echo "Found existing release PR: #${EXISTING_PR}. Skipping creation."
          else
            echo "No existing release PR found. Proceeding to create a new one."
          fi

      - name: Log existing PR
        if: ${{ steps.guard_schedule.outputs.continue == 'true' && steps.check_dev_staging.outputs.existing_pr != '' }}
        run: |
          echo "Release PR already exists: #${{ steps.check_dev_staging.outputs.existing_pr }}"

      - name: Ensure release labels exist
        if: ${{ steps.guard_schedule.outputs.continue == 'true' && steps.check_dev_staging.outputs.existing_pr == '' }}
        env:
          GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
        shell: bash
        run: |
          set -euo pipefail

          for LABEL in release automated staging; do
            echo "Ensuring label exists: ${LABEL}"
            gh label create "${LABEL}" --color BFD4F2 --force || true
          done

      - name: Create release branch from dev
        if: ${{ steps.guard_schedule.outputs.continue == 'true' && steps.check_dev_staging.outputs.existing_pr == '' }}
        env:
          BRANCH_NAME: ${{ steps.check_dev_staging.outputs.branch_name }}
        shell: bash
        run: |
          set -euo pipefail

          echo "Creating release branch ${BRANCH_NAME} from dev"
          git fetch origin dev

          # Check if branch already exists locally
          if git show-ref --verify --quiet refs/heads/"${BRANCH_NAME}"; then
            echo "Branch ${BRANCH_NAME} already exists locally, checking out..."
            git checkout "${BRANCH_NAME}"
          else
            git checkout -b "${BRANCH_NAME}" origin/dev
          fi

          # Check if branch already exists on remote
          if git ls-remote --heads origin "${BRANCH_NAME}" | grep -q "${BRANCH_NAME}"; then
            echo "Branch ${BRANCH_NAME} already exists on remote. Skipping push."
          else
            echo "Pushing branch ${BRANCH_NAME} to remote..."
            if ! git push origin "${BRANCH_NAME}"; then
              echo "❌ ERROR: Failed to push branch ${BRANCH_NAME} to remote"
              exit 1
            fi
            echo "✓ Successfully pushed branch ${BRANCH_NAME}"
          fi

      - name: Create dev to staging release PR
        if: ${{ steps.guard_schedule.outputs.continue == 'true' && steps.check_dev_staging.outputs.existing_pr == '' }}
        env:
          GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
          PR_DATE: ${{ steps.check_dev_staging.outputs.date }}
          BRANCH_NAME: ${{ steps.check_dev_staging.outputs.branch_name }}
        shell: bash
        run: |
          set -euo pipefail

          python <<'PY'
          import os
          import pathlib
          import textwrap
          from datetime import datetime

          pr_date = os.environ["PR_DATE"]
          branch_name = os.environ["BRANCH_NAME"]
          formatted_date = datetime.strptime(pr_date, "%Y-%m-%d").strftime("%B %d, %Y")

          pathlib.Path("pr_body.md").write_text(textwrap.dedent(f"""\
          ## 🚀 Weekly Release to Staging

          **Release Date:** {formatted_date}
          **Release Branch:** `{branch_name}`

          This automated PR promotes a snapshot of `dev` to `staging` for testing.

          ### What's Included
          All commits merged to `dev` up to the branch creation time.

          **Note:** This PR uses a dedicated release branch, so new commits to `dev` will NOT automatically appear here.

          ### Review Checklist
          - [ ] All CI checks pass
          - [ ] Code review completed
          - [ ] QA team notified
          - [ ] Ready to merge to staging environment

          ### Next Steps
          After merging, the staging environment will be updated. A production release PR will be created on Sunday.

          ---
          *This PR was automatically created by the Release Calendar workflow on {formatted_date}*
          """))
          PY

          TITLE="Release to Staging - ${PR_DATE}"
          echo "Creating PR with title: ${TITLE} from branch ${BRANCH_NAME}"

          if ! gh pr create \
            --base staging \
            --head "${BRANCH_NAME}" \
            --title "${TITLE}" \
            --label release \
            --label automated \
            --label staging \
            --body-file pr_body.md; then
            echo "❌ ERROR: Failed to create PR"
            exit 1
          fi

          echo "✅ PR created successfully"

  release_to_production:
    name: Create staging to main release PR
    runs-on: ubuntu-latest
    permissions:
      contents: write
      pull-requests: write
      issues: write
    steps:
      - name: Guard Sunday schedule
        id: guard_schedule
        shell: bash
        run: |
          set -euo pipefail

          # Allow workflow_dispatch based on input
          if [ "${{ github.event_name }}" == "workflow_dispatch" ]; then
            JOB_TO_RUN="${{ inputs.job_to_run }}"
            if [ "$JOB_TO_RUN" == "production" ]; then
              echo "Manual trigger: running production job (job_to_run=${JOB_TO_RUN})"
              echo "continue=true" >> "$GITHUB_OUTPUT"
            else
              echo "Manual trigger: skipping production job (job_to_run=${JOB_TO_RUN})"
              echo "continue=false" >> "$GITHUB_OUTPUT"
            fi
            exit 0
          fi

          # For schedule events, check day of week
          DOW=$(date -u +%u)
          if [ "$DOW" != "7" ]; then
            echo "Not Sunday in UTC (current day-of-week: $DOW). Exiting job early."
            echo "continue=false" >> "$GITHUB_OUTPUT"
            exit 0
          fi

          echo "continue=true" >> "$GITHUB_OUTPUT"

      - name: Check out repository
        if: ${{ steps.guard_schedule.outputs.continue == 'true' }}
        uses: actions/checkout@v6
        with:
          fetch-depth: 0

      - name: Determine release readiness
        if: ${{ steps.guard_schedule.outputs.continue == 'true' }}
        id: production_status
        env:
          GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
        shell: bash
        run: |
          set -euo pipefail

          PR_DATE=$(date +%Y-%m-%d)
          echo "date=${PR_DATE}" >> "$GITHUB_OUTPUT"

          echo "Fetching latest branches..."
          git fetch origin main staging

          COMMITS_AHEAD=$(git rev-list --count origin/main..origin/staging)
          echo "commits=${COMMITS_AHEAD}" >> "$GITHUB_OUTPUT"

          if [ "$COMMITS_AHEAD" -eq 0 ]; then
            echo "staging_not_ahead=true" >> "$GITHUB_OUTPUT"
            echo "Staging is up to date with main. No release PR needed."
            exit 0
          fi

          echo "staging_not_ahead=false" >> "$GITHUB_OUTPUT"

          echo "Checking for existing pull requests from staging to main..."
          EXISTING_PR=$(gh pr list --base main --head staging --state open --limit 1 --json number --jq '.[0].number // ""')
          echo "existing_pr=${EXISTING_PR}" >> "$GITHUB_OUTPUT"

          if [ -n "$EXISTING_PR" ]; then
            echo "Found existing production release PR: #${EXISTING_PR}. Skipping creation."
          else
            echo "No existing production release PR found. Ready to create a new one."
          fi

      - name: Log staging up to date
        if: ${{ steps.guard_schedule.outputs.continue == 'true' && steps.production_status.outputs.staging_not_ahead == 'true' }}
        run: |
          echo "Staging branch is up to date with main. Skipping production release PR creation."

      - name: Log existing PR
        if: ${{ steps.guard_schedule.outputs.continue == 'true' && steps.production_status.outputs.existing_pr != '' }}
        run: |
          echo "Production release PR already exists: #${{ steps.production_status.outputs.existing_pr }}"

      - name: Ensure release labels exist
        if: ${{ steps.guard_schedule.outputs.continue == 'true' && steps.production_status.outputs.staging_not_ahead != 'true' && steps.production_status.outputs.existing_pr == '' }}
        env:
          GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
        shell: bash
        run: |
          set -euo pipefail

          for LABEL in release automated production; do
            echo "Ensuring label exists: ${LABEL}"
            gh label create "${LABEL}" --color BFD4F2 --force || true
          done

      - name: Create staging to main release PR
        if: ${{ steps.guard_schedule.outputs.continue == 'true' && steps.production_status.outputs.staging_not_ahead != 'true' && steps.production_status.outputs.existing_pr == '' }}
        env:
          GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
          PR_DATE: ${{ steps.production_status.outputs.date }}
          COMMITS_AHEAD: ${{ steps.production_status.outputs.commits }}
        shell: bash
        run: |
          set -euo pipefail

          python <<'PY'
          import os
          import pathlib
          import textwrap
          from datetime import datetime

          commits_ahead = os.environ["COMMITS_AHEAD"]
          pr_date = os.environ["PR_DATE"]
          formatted_date = datetime.strptime(pr_date, "%Y-%m-%d").strftime("%B %d, %Y")

          pathlib.Path("pr_body.md").write_text(textwrap.dedent(f"""\
          ## 🎯 Production Release

          **Release Date:** {formatted_date}
          **Commits ahead**: {commits_ahead}

          This automated PR promotes tested changes from `staging` to `main` for production deployment.

          ### What's Included
          All changes that have been verified in the staging environment.

          **Note:** This PR is directly from `staging`, so new commits merged to `staging` will automatically appear here.

          ### Pre-Deployment Checklist
          - [ ] All staging tests passed
          - [ ] QA sign-off received
          - [ ] Stakeholder approval obtained
          - [ ] Deployment plan reviewed
          - [ ] Rollback plan confirmed

          ### Deployment Notes
          Merging this PR will trigger production deployment.

          ---
          *This PR was automatically created by the Release Calendar workflow on {formatted_date}*
          """))
          PY

          TITLE="Release to Production - ${PR_DATE}"
          echo "Creating PR with title: ${TITLE} from staging with ${COMMITS_AHEAD} commits ahead."

          gh pr create \
            --base main \
            --head staging \
            --title "${TITLE}" \
            --label release \
            --label automated \
            --label production \
            --body-file pr_body.md
