#!/usr/bin/env bash
#
# render-smoke-admin.sh — authenticated render smoke test for the Navagoo ADMIN
# (backend) portal, plus an inline-JS syntax gate.
# =====================================================================================
#
# WHY THIS EXISTS
#   The frontend has tests/smoke/render-smoke.sh. The backend had none — which is how
#   a missing `}` in the admin Dashboard's inline <script> shipped: the page returned
#   HTTP 200 with no PHP error, so an HTML-only check passed, yet the whole chart/tab
#   IIFE silently died in the browser. This script closes that gap two ways:
#     1. renders each key admin page (login as admin) and asserts 200 + no PHP/Yii
#        error signature, AND
#     2. extracts every inline <script> from the RENDERED HTML (PHP already evaluated,
#        so it is real browser JS) and runs `node --check` on it — catching exactly
#        the class of bug above. If `node` is absent the JS gate is skipped (warned).
#
# USAGE
#   tests/smoke/render-smoke-admin.sh
#   SMOKE_VERBOSE=1 tests/smoke/render-smoke-admin.sh
#
# CONFIG (env — all default to LOCAL DEV values)
#   SMOKE_HOST   Host header the vhost matches   (default: backend.navagoo.localhost)
#   SMOKE_BASE   Base URL / origin to hit        (default: http://127.0.0.1)
#   SMOKE_USER   LoginForm[username]             (default: admin)
#   SMOKE_PASS   LoginForm[password]             (default: NavAdmin!2026)
#   SMOKE_TIMEOUT per-request curl timeout, secs (default: 25)
#
# CREDENTIALS NOTE
#   The default username/password are the shared LOCAL DEV admin account (rotated for
#   verification, see the admin-portal-parity-audit note). They are NOT secrets and are
#   only valid against the local docker stack. Override via SMOKE_USER / SMOKE_PASS;
#   never point this at production.
#
# EXIT CODES
#   0  all pages rendered cleanly AND all inline JS parsed
#   1  a page failed (non-200 / error signature) or inline JS had a syntax error
#   2  login / preflight failure
#
set -u

HOST="${SMOKE_HOST:-backend.navagoo.localhost}"
BASE="${SMOKE_BASE:-http://127.0.0.1}"
USER_NAME="${SMOKE_USER:-admin}"
USER_PASS="${SMOKE_PASS:-NavAdmin!2026}"
TIMEOUT="${SMOKE_TIMEOUT:-25}"
VERBOSE="${SMOKE_VERBOSE:-0}"

LOGIN_PATH="/sign-in/login"
CSRF_PARAM="_csrf-backend"   # backend/config/web.php → 'csrfParam'

# Key admin pages — the net-new BI cluster + consolidated surfaces. "<path>" or
# "<path>|<expected_status>" (defaults to 200).
PAGES=(
  "/"                              # Home (subscription pulse + attention)
  "/dashboard/index"               # Platform BI (5 tabs + charts)
  "/analytics/index"               # Platform P&L
  "/cost/index"                    # Costs (COGS) register
  "/timeline-event/audit"          # Events audit log
  "/timeline-event/audit-csv"      # Events CSV export
  "/users-roles/index"             # Users & Roles
  "/shop/offers"                   # Subscriptions → Offers
  "/shop/payment-methods"          # Subscriptions → Payment methods
  "/faq/support"                   # Support & content (FAQs / Policies / Contact)
)

ERROR_SIGNATURES='Class .* not found|Fatal error|Undefined variable|Undefined array key|on null|Stack trace|Call to a member|Uncaught'

SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
JS_CHECKER="${SCRIPT_DIR}/check-inline-js.js"
COOKIE_JAR="$(mktemp -t navagoo-admin-smoke.XXXXXX)"
HTML_TMP="$(mktemp -t navagoo-admin-html.XXXXXX)"
cleanup() { rm -f "$COOKIE_JAR" "$HTML_TMP" 2>/dev/null || true; }
trap cleanup EXIT

ccurl() {
  curl -sS --max-time "$TIMEOUT" -H "Host: ${HOST}" -b "$COOKIE_JAR" -c "$COOKIE_JAR" "$@"
}

log_preflight_fail() {
  echo "PREFLIGHT FAILURE: $1" >&2
  echo "  Host=${HOST}  Base=${BASE} — is the local docker stack up?" >&2
}

# 1a. GET login page → CSRF token + seed cookie jar.
LOGIN_PAGE="$(ccurl "${BASE}${LOGIN_PATH}")"
[ -z "$LOGIN_PAGE" ] && { log_preflight_fail "empty response from GET ${LOGIN_PATH}"; exit 2; }

CSRF_TOKEN="$(printf '%s' "$LOGIN_PAGE" \
  | grep -oE "name=\"${CSRF_PARAM}\"[^>]*value=\"[^\"]*\"" | head -n1 \
  | sed -E 's/.*value="([^"]*)".*/\1/')"
[ -z "$CSRF_TOKEN" ] && CSRF_TOKEN="$(printf '%s' "$LOGIN_PAGE" \
  | grep -oE 'name="csrf-token"[^>]*content="[^"]*"' | head -n1 \
  | sed -E 's/.*content="([^"]*)".*/\1/')"
[ -z "$CSRF_TOKEN" ] && { log_preflight_fail "no ${CSRF_PARAM} token on the login page"; exit 2; }

# 1b. POST credentials.
ccurl -i \
  --data-urlencode "${CSRF_PARAM}=${CSRF_TOKEN}" \
  --data-urlencode "LoginForm[username]=${USER_NAME}" \
  --data-urlencode "LoginForm[password]=${USER_PASS}" \
  --data-urlencode "LoginForm[rememberMe]=1" \
  "${BASE}${LOGIN_PATH}" >/dev/null

# Confirm session: the login route now redirects away from the username/password form.
POST_LOGIN="$(ccurl -L "${BASE}${LOGIN_PATH}")"
if printf '%s' "$POST_LOGIN" | grep -qE "name=\"LoginForm\[password\]\""; then
  log_preflight_fail "login did not establish a session (creds? SMOKE_USER/SMOKE_PASS)"
  exit 2
fi

echo "Authenticated as ${USER_NAME} @ ${HOST}"
HAVE_NODE=1; command -v node >/dev/null 2>&1 || { HAVE_NODE=0; echo "  (node not found — inline-JS syntax gate skipped)"; }
echo

# --- inline-JS syntax gate ---------------------------------------------------------
# Parse every inline <script> in the rendered HTML (via check-inline-js.js) and fail
# on any JS syntax error — catches the Dashboard-class bug an HTTP check misses.
check_inline_js() {
  local body="$1" path="$2"
  [ "$HAVE_NODE" = "1" ] || return 0
  [ -f "$JS_CHECKER" ] || return 0
  printf '%s' "$body" > "$HTML_TMP"
  if ! node "$JS_CHECKER" "$HTML_TMP" 2>/tmp/navagoo-js-err; then
    echo "    ↳ inline JS syntax error on ${path}:" >&2
    sed -n '1,4p' /tmp/navagoo-js-err | sed 's/^/       /' >&2
    return 1
  fi
  return 0
}

# --- render sweep ------------------------------------------------------------------
FAILS=0; PASSES=0; ROWS=""
for entry in "${PAGES[@]}"; do
  path="${entry%%|*}"; expected="200"; [ "$entry" != "$path" ] && expected="${entry##*|}"
  resp="$(ccurl -L -w $'\n%{http_code}' "${BASE}${path}")"
  status="$(printf '%s' "$resp" | tail -n1)"
  body="$(printf '%s' "$resp" | sed '$d')"

  ok=1; reason=""
  if [ "$status" != "$expected" ]; then
    ok=0; reason="HTTP ${status:-???} (expected ${expected})"
  elif [ "$expected" = "200" ]; then
    hit="$(printf '%s' "$body" | grep -oE "$ERROR_SIGNATURES" | head -n1)"
    if [ -n "$hit" ]; then ok=0; reason="error sig: ${hit}"
    elif ! check_inline_js "$body" "$path"; then ok=0; reason="inline JS syntax error"; fi
  fi

  if [ "$ok" = "1" ]; then PASSES=$((PASSES+1)); ROWS="${ROWS}PASS|${status}|${path}|"$'\n'
  else FAILS=$((FAILS+1)); ROWS="${ROWS}FAIL|${status:-???}|${path}|${reason}"$'\n'; fi
done

printf '%-6s %-6s %s\n' "RESULT" "HTTP" "PAGE"
printf '%-6s %-6s %s\n' "------" "----" "----------------------------------------"
printf '%s' "$ROWS" | while IFS='|' read -r result code page note; do
  [ -z "$result" ] && continue
  if [ -n "$note" ]; then printf '%-6s %-6s %s  (%s)\n' "$result" "$code" "$page" "$note"
  else printf '%-6s %-6s %s\n' "$result" "$code" "$page"; fi
done

echo
echo "Summary: ${PASSES} passed, ${FAILS} failed, $(( PASSES + FAILS )) total."
[ "$FAILS" -eq 0 ] || exit 1
exit 0
