name: Userfront E2E Full Nightly

on:
    schedule:
        - cron: "0 18 * * *"
    workflow_dispatch:

permissions:
    contents: write

jobs:
    lint:
        runs-on: ubuntu-latest
        steps:
            - name: Checkout code
              uses: actions/checkout@v4

            - name: Setup Node.js
              uses: actions/setup-node@v4
              with:
                  node-version: "24"

            - name: Setup Go
              uses: actions/setup-go@v5
              with:
                  go-version: "1.25"
                  cache-dependency-path: backend/go.sum

            - name: Setup Flutter
              uses: subosito/flutter-action@v2
              with:
                  channel: "stable"
                  cache: true

            - name: Run common lint checks
              run: |
                  make code-check-lint

    full-test-policy:
        runs-on: ubuntu-latest
        outputs:
            should_run: ${{ steps.policy.outputs.should_run }}
            reason: ${{ steps.policy.outputs.reason }}
        steps:
            - name: Checkout code
              uses: actions/checkout@v4
              with:
                  fetch-depth: 0

            - name: Decide whether full E2E is needed
              id: policy
              run: |
                  set -euo pipefail

                  target_sha="${GITHUB_SHA}"
                  should_run="true"
                  reason="manual-dispatch"

                  if [ "${GITHUB_EVENT_NAME}" = "schedule" ]; then
                    reason="missing-full-result"
                    git fetch origin "+refs/heads/badges:refs/remotes/origin/badges" || true
                    if git show-ref --verify --quiet refs/remotes/origin/badges && \
                       git cat-file -e "refs/remotes/origin/badges:dev/${target_sha}/badges.json" 2>/dev/null; then
                      full_message="$(
                        git show "refs/remotes/origin/badges:dev/${target_sha}/badges.json" |
                          node -e "let input=''; process.stdin.on('data', c => input += c); process.stdin.on('end', () => { const data = JSON.parse(input); const keys = ['userfront-chrome', 'userfront-firefox', 'userfront-safari']; const messages = keys.map((key) => data.badges?.[key]?.message || 'unknown'); process.stdout.write(messages.join(',')); });"
                      )"
                      if [ -n "${full_message}" ] && ! printf '%s' "${full_message}" | grep -q "unknown"; then
                        should_run="false"
                        reason="full-result-exists:${full_message}"
                      fi
                    fi
                  fi

                  echo "should_run=${should_run}" >> "$GITHUB_OUTPUT"
                  echo "reason=${reason}" >> "$GITHUB_OUTPUT"
                  echo "target_sha=${target_sha}"
                  echo "should_run=${should_run}"
                  echo "reason=${reason}"

    userfront-e2e-full:
        needs:
            - lint
            - full-test-policy
        if: ${{ needs.lint.result == 'success' && needs.full-test-policy.outputs.should_run == 'true' }}
        runs-on: ubuntu-latest
        timeout-minutes: 80
        outputs:
            chromium_desktop: ${{ steps.full-results.outputs.chromium_desktop }}
            chromium_mobile: ${{ steps.full-results.outputs.chromium_mobile }}
            firefox_desktop: ${{ steps.full-results.outputs.firefox_desktop }}
            firefox_mobile: ${{ steps.full-results.outputs.firefox_mobile }}
            webkit_desktop: ${{ steps.full-results.outputs.webkit_desktop }}
            webkit_mobile: ${{ steps.full-results.outputs.webkit_mobile }}
        steps:
            - name: Checkout code
              uses: actions/checkout@v4

            - name: Setup Node.js
              uses: actions/setup-node@v4
              with:
                  node-version: "24"
                  cache: "npm"
                  cache-dependency-path: userfront-e2e/package-lock.json

            - name: Setup Flutter
              uses: subosito/flutter-action@v2
              with:
                  channel: "stable"
                  cache: true

            - name: Sync userfront locales
              run: |
                  /bin/sh ./scripts/sync_userfront_locales.sh

            - name: Install userfront-e2e dependencies
              run: |
                  cd userfront-e2e
                  npm ci

            - name: Build userfront WASM
              run: |
                  cd userfront
                  rm -rf build/web
                  flutter build web --wasm --release
                  cd ..
                  node userfront/scripts/optimize-web-build.mjs userfront/build/web

            - name: Provision full browser matrix
              run: |
                  cd userfront-e2e
                  npx playwright install --with-deps

            - name: Run full userfront-e2e tests
              id: full-results
              run: |
                  mkdir -p reports
                  cd userfront-e2e
                  workers="${PLAYWRIGHT_WORKERS:-4}"
                  case "$workers" in
                    ''|*[!0-9]*|0) workers=4 ;;
                  esac
                  any_failure=0

                  run_project() {
                    output_name="$1"
                    project_name="$2"
                    log_path="../reports/userfront-e2e-full-${project_name}.log"

                    set +e
                    echo "[userfront-e2e-full] PLAYWRIGHT_WORKERS=${workers} npx playwright test --project=${project_name}" | tee "$log_path"
                    PLAYWRIGHT_WORKERS="$workers" npx playwright test --project="$project_name" --reporter=list 2>&1 | tee -a "$log_path"
                    exit_code=${PIPESTATUS[0]}
                    set -e

                    if [ "$exit_code" -eq 0 ]; then
                      result="success"
                    else
                      result="failure"
                      any_failure=1
                    fi
                    echo "${output_name}=${result}" >> "$GITHUB_OUTPUT"
                  }

                  run_project chromium_desktop chromium-desktop
                  run_project chromium_mobile chromium-mobile-webapp
                  run_project firefox_desktop firefox-desktop
                  echo "firefox_mobile=skipped" >> "$GITHUB_OUTPUT"
                  run_project webkit_desktop webkit-desktop
                  run_project webkit_mobile webkit-mobile-webapp

                  exit "$any_failure"

            - name: Upload userfront-e2e full artifacts
              if: ${{ always() }}
              uses: actions/upload-artifact@v3
              continue-on-error: true
              with:
                  name: userfront-e2e-full-report
                  path: |
                      reports/userfront-e2e-full-*.log
                      userfront-e2e/playwright-report
                      userfront-e2e/test-results
                  if-no-files-found: ignore

    badge-updater:
        needs:
            - lint
            - full-test-policy
            - userfront-e2e-full
        if: ${{ always() && needs.lint.result == 'success' && needs.full-test-policy.outputs.should_run == 'true' && github.ref == 'refs/heads/dev' }}
        runs-on: ubuntu-latest
        steps:
            - name: Checkout code
              uses: actions/checkout@v4
              with:
                  fetch-depth: 0

            - name: Setup Node.js
              uses: actions/setup-node@v4
              with:
                  node-version: "24"

            - name: Restore published badge state
              run: |
                  git fetch origin "+refs/heads/badges:refs/remotes/origin/badges" || true
                  if git show-ref --verify --quiet refs/remotes/origin/badges && \
                     git cat-file -e refs/remotes/origin/badges:latest/badges.json 2>/dev/null; then
                    mkdir -p docs/badges
                    git archive --format=tar refs/remotes/origin/badges latest | tar -x
                    cp latest/* docs/badges/
                    rm -rf latest
                  else
                    echo "No published badge state found."
                  fi

            - name: Update full E2E badge files
              env:
                  USERFRONT_E2E_RESULT: ${{ needs.userfront-e2e-full.result }}
                  USERFRONT_E2E_FULL: "true"
                  USERFRONT_E2E_CHROMIUM_DESKTOP_RESULT: ${{ needs.userfront-e2e-full.outputs.chromium_desktop }}
                  USERFRONT_E2E_CHROMIUM_MOBILE_RESULT: ${{ needs.userfront-e2e-full.outputs.chromium_mobile }}
                  USERFRONT_E2E_FIREFOX_DESKTOP_RESULT: ${{ needs.userfront-e2e-full.outputs.firefox_desktop }}
                  USERFRONT_E2E_FIREFOX_MOBILE_RESULT: ${{ needs.userfront-e2e-full.outputs.firefox_mobile }}
                  USERFRONT_E2E_WEBKIT_DESKTOP_RESULT: ${{ needs.userfront-e2e-full.outputs.webkit_desktop }}
                  USERFRONT_E2E_WEBKIT_MOBILE_RESULT: ${{ needs.userfront-e2e-full.outputs.webkit_mobile }}
                  BADGE_UPDATE_CODE_CHECK: "false"
                  BADGE_SOURCE_BRANCH: dev
                  BADGE_SOURCE_SHA: ${{ github.sha }}
              run: |
                  node scripts/update_code_check_badges.mjs
                  cat docs/badges/badges.json

            - name: Publish full E2E badge assets
              run: |
                  if [ -z "$(git status --porcelain docs/badges)" ]; then
                    echo "No badge changes."
                    exit 0
                  fi

                  BADGE_BRANCH=badges
                  BADGE_WORKTREE="$(mktemp -d)"
                  BADGE_LATEST_DIR="${BADGE_WORKTREE}/latest"
                  BADGE_SHA_DIR="${BADGE_WORKTREE}/dev/${GITHUB_SHA}"
                  trap 'rm -rf "${BADGE_WORKTREE}"' EXIT

                  git config user.name "gitea-actions"
                  git config user.email "gitea-actions@hmac.kr"

                  git fetch origin "+refs/heads/${BADGE_BRANCH}:refs/remotes/origin/${BADGE_BRANCH}" || true
                  if git show-ref --verify --quiet "refs/remotes/origin/${BADGE_BRANCH}"; then
                    git worktree add --detach "${BADGE_WORKTREE}" "origin/${BADGE_BRANCH}"
                  else
                    git worktree add --detach "${BADGE_WORKTREE}"
                    git -C "${BADGE_WORKTREE}" checkout --orphan "${BADGE_BRANCH}"
                    git -C "${BADGE_WORKTREE}" rm -rf . || true
                  fi

                  find "${BADGE_WORKTREE}" -mindepth 1 -maxdepth 1 ! -name .git -exec rm -rf {} +
                  mkdir -p "${BADGE_LATEST_DIR}" "${BADGE_SHA_DIR}"
                  cp docs/badges/*.svg "${BADGE_LATEST_DIR}/"
                  cp docs/badges/badges.json "${BADGE_LATEST_DIR}/badges.json"
                  cp docs/badges/*.svg "${BADGE_SHA_DIR}/"
                  cp docs/badges/badges.json "${BADGE_SHA_DIR}/badges.json"

                  git -C "${BADGE_WORKTREE}" add .
                  if [ -z "$(git -C "${BADGE_WORKTREE}" status --porcelain)" ]; then
                    echo "No published badge changes."
                    exit 0
                  fi

                  git -C "${BADGE_WORKTREE}" commit -m "chore: publish userfront e2e full badge [skip ci]"
                  git -C "${BADGE_WORKTREE}" push origin HEAD:${BADGE_BRANCH}
