#!/bin/bash
# Usage: ./validate-quality.sh <html-file> <generation-mode>
# Returns 0 if all quality gates pass, non-zero with failure details.
#
# Runs the 9 quality gates defined in docs/api/quality-gates.yaml against
# generated HyperFrames HTML. Each gate checks a specific structural or
# behavioral requirement. Forbidden-pattern gates (inverted) FAIL when
# the pattern IS found. Required-pattern gates FAIL when the pattern
# is NOT found.

HTML="$1"
MODE="${2:-generate_new_hyperframes_html}"
FAILS=0

# Print usage if no arguments provided
if [ -z "$HTML" ]; then
  echo "Usage: $0 <html-file> <generation-mode>"
  echo ""
  echo "Checks generated HyperFrames HTML against the 9 quality gates."
  echo ""
  echo "Arguments:"
  echo "  html-file        Path to the generated HTML file"
  echo "  generation-mode  Either 'generate_new_hyperframes_html' or"
  echo "                   'assemble_existing_catalog_items'"
  echo "                   (default: generate_new_hyperframes_html)"
  echo ""
  echo "Exit code: 0 if all gates pass, 1 if any gate fails."
  exit 1
fi

# Helper: check that a required pattern EXISTS in the HTML
# PASS if found, FAIL if missing
check() {
  local name="$1"
  local pattern="$2"
  local severity="${3:-error}"
  if grep -q "$pattern" "$HTML" 2>/dev/null; then
    echo "[PASS] $name"
  else
    if [ "$severity" = "error" ]; then
      echo "[FAIL] $name -- missing required pattern"
      FAILS=$((FAILS + 1))
    else
      echo "[WARN] $name -- missing recommended pattern"
    fi
  fi
}

# Helper: check that a forbidden pattern is ABSENT from the HTML
# PASS if absent, FAIL if found (inverted logic)
check_inverted() {
  local name="$1"
  local pattern="$2"
  if grep -q "$pattern" "$HTML" 2>/dev/null; then
    echo "[FAIL] $name (found forbidden pattern: $pattern)"
    FAILS=$((FAILS + 1))
  else
    echo "[PASS] $name"
  fi
}

echo "=== Quality Gates for $(basename "$HTML") (mode: $MODE) ==="
echo ""

# Gate 1: Layered lint — official hyperframes lint + custom checks
HF_LINT=$(dirname "$0")/hf-lint.py
PROJECT_DIR=$(dirname "$HTML")
if [ -f "$HF_LINT" ] && [ -d "$PROJECT_DIR" ]; then
  LINT_OUTPUT=$("$HF_LINT" "$PROJECT_DIR" 2>&1) || true
  if [ -n "$LINT_OUTPUT" ]; then
    echo "$LINT_OUTPUT"
    LINT_ERRORS=$(echo "$LINT_OUTPUT" | grep -c '^\[FAIL\]' 2>/dev/null || echo 0)
    if [ "$LINT_ERRORS" -gt 0 ] 2>/dev/null; then
      FAILS=$((FAILS + LINT_ERRORS))
    fi
  fi
else
  # Fallback: if hf-lint.py unavailable, run basic regex checks
  check "has-fixed-width" 'data-width=' error
  check "has-fixed-height" 'data-height=' error
  check "has-composition-id" 'data-composition-id=' error
  check "has-paused-timeline" 'gsap\.timeline.*paused.*true' error
  check "has-timeline-registration" 'window\.__timelines' error
  check "has-duration-coverage" 'data-duration=' error
fi

# Gate 6: No Date.now() for timing
check_inverted "no-date-now" 'Date\.now()'

# Gate 7: No setInterval for primary timing
check_inverted "no-setinterval" 'setInterval'

# Gate 8: No Math.random() — must use seeded PRNG (mulberry32)
check_inverted "no-math-random" 'Math\.random\(\)'

# Gate 9: Producer readiness gate (warning — render blocks without it)
check "has-render-ready" 'window\.__renderReady' warning

# Gate 9b: Visual layout inspection (warning — code can be correct but visually broken)
if command -v hyperframes &>/dev/null && [ -d "$PROJECT_DIR" ]; then
  INSPECT_OUTPUT=$(hyperframes inspect "$PROJECT_DIR" --json 2>&1) || true
  if [ -n "$INSPECT_OUTPUT" ]; then
    INSPECT_ERRORS=$(echo "$INSPECT_OUTPUT" | python3 -c "
import sys,json
try:
    d=json.load(sys.stdin)
    print(d.get('errorCount',0))
except: print(0)
" 2>/dev/null || echo 0)
    if [ "$INSPECT_ERRORS" -gt 0 ] 2>/dev/null; then
      echo "[WARN] visual-inspect: $INSPECT_ERRORS layout/motion issue(s). Run 'hyperframes inspect <dir>' for details."
    else
      echo "[PASS] visual-inspect"
    fi
  fi
fi

# Gate 9c: Runtime validation (error — catches JS errors, missing assets, network failures)
if command -v hyperframes &>/dev/null && [ -d "$PROJECT_DIR" ]; then
  VALIDATE_OUTPUT=$(hyperframes validate "$PROJECT_DIR" --json 2>&1) || VALIDATE_EXIT=$?
  if [ -n "$VALIDATE_OUTPUT" ]; then
    VALIDATE_ERRORS=$(echo "$VALIDATE_OUTPUT" | python3 -c "
import sys,json
try:
    d=json.load(sys.stdin)
    print(d.get('errorCount',0))
except: print(0)
" 2>/dev/null || echo 0)
    if [ "$VALIDATE_ERRORS" -gt 0 ] 2>/dev/null; then
      echo "[FAIL] runtime-validate: $VALIDATE_ERRORS runtime error(s). Run 'hyperframes validate <dir>' for details."
      FAILS=$((FAILS + VALIDATE_ERRORS))
    else
      echo "[PASS] runtime-validate"
    fi
  fi
fi

# Gate 9: No unresolved paste comments
check_inverted "no-paste-comments" '<!-- paste from'

# Gate 10: Async readiness (inspection-only — check for key patterns)
check "async-readiness-fonts" 'document\.fonts\.ready' warning
check "async-readiness-promise" 'Promise\.all' warning

# Gate 12: Audio integration checks (warning-level — audio is optional)
check "has-beat-helper" 'function beat' warning
check "beat-array-present" 'var __beats' warning
check "no-stale-beat-path" '// beat.*valid' warning

# Gate 12b: Audio element id check (error when audio present but missing id)
AUDIO_NO_ID=$(grep -c '<audio' "$HTML" 2>/dev/null || printf "0")
AUDIO_WITH_ID=$(grep -c '<audio[^>]*id=' "$HTML" 2>/dev/null || printf "0")
# Strip any trailing whitespace/newlines from grep -c output
AUDIO_NO_ID=$(echo "$AUDIO_NO_ID" | tr -d '[:space:]')
AUDIO_WITH_ID=$(echo "$AUDIO_WITH_ID" | tr -d '[:space:]')
# Default empty to 0
AUDIO_NO_ID=${AUDIO_NO_ID:-0}
AUDIO_WITH_ID=${AUDIO_WITH_ID:-0}
if [ "$AUDIO_NO_ID" -gt 0 ] 2>/dev/null && [ "$AUDIO_WITH_ID" -lt "$AUDIO_NO_ID" ] 2>/dev/null; then
  MISSING_ID=$((AUDIO_NO_ID - AUDIO_WITH_ID))
  echo "[FAIL] audio-missing-id — $MISSING_ID <audio> element(s) missing id attribute. The renderer requires id to discover media elements; audio will be SILENT."
  FAILS=$((FAILS + 1))
elif [ "$AUDIO_NO_ID" -gt 0 ] 2>/dev/null; then
  echo "[PASS] audio-missing-id"
fi

# Gate 13: Explicit catalog item references (if intent profile provided)
INTENT_PROFILE="${3:-}"
if [ -n "$INTENT_PROFILE" ] && [ -f "$INTENT_PROFILE" ]; then
  echo ""
  echo "--- Checking explicit catalog refs ---"
  REFS=$(python3 -c "
import json, sys
with open('$INTENT_PROFILE') as f:
    profile = json.load(f)
refs = profile.get('explicitCatalogRefs', [])
for r in refs:
    print(r.get('id', ''))
" 2>/dev/null)
  if [ -n "$REFS" ]; then
    MISSING=0
    for ref_id in $REFS; do
      if grep -q "$ref_id" "$HTML" 2>/dev/null; then
        echo "[PASS] explicit-catalog-ref: $ref_id"
      else
        echo "[FAIL] explicit-catalog-ref: $ref_id — user explicitly requested this catalog item but it is NOT present in the HTML"
        FAILS=$((FAILS + 1))
        MISSING=$((MISSING + 1))
      fi
    done
    if [ $MISSING -eq 0 ]; then
      echo "All explicit catalog references satisfied."
    fi
  fi
fi

# Note: Common-mistakes lint is now integrated into hf-lint.py (Gate 1)

echo ""
echo "---"
if [ $FAILS -eq 0 ]; then
  echo "All quality gates passed."
  exit 0
else
  echo "$FAILS quality gate(s) failed."
  exit 1
fi
