#!/bin/bash
# Frontend design-system lint guards.
# See _private/docs/balanced_breakfast/design-system.md "Inline-style rules"
# and "Cross-cutting rules" for the source of truth.
# Exit 0 = clean. Exit non-zero = violations found (printed with file:line).

set -u
ROOT="$(cd "$(dirname "$0")/.." && pwd)"
FRONTEND="$ROOT/src-tauri/frontend"
SRC_JS="$FRONTEND/js"
SRC_HTML="$FRONTEND/index.html"
SRC_CSS="$FRONTEND/css/styles.css"

violations=0

report() {
    local rule="$1"; shift
    local msg="$1"; shift
    if [ -n "$*" ]; then
        echo
        echo "[$rule] $msg"
        echo "$*"
        violations=$((violations + 1))
    fi
}

# 1. no-raw-hex
#    No raw hex literals in JS or source HTML. HTML entities (&#NNNN;) and the
#    themes.js theme engine are exempt — themes.js is the only place that
#    builds CSS values via string concatenation, by charter.
hits=$(grep -rnE '#[0-9a-fA-F]{3,8}\b' "$SRC_JS" "$SRC_HTML" 2>/dev/null \
    | grep -vE '&#[0-9]+;' \
    | grep -v 'js/themes.js' \
    | grep -v 'js/tests/' \
    || true)
report "no-raw-hex" "Raw hex literal in JS/HTML — use a CSS class or themed token." "$hits"

# 2. no-csstext
#    style.cssText injection is forbidden — it usually means color/border/font
#    values are being set from JS and the result is unthemeable.
hits=$(grep -rn 'cssText' "$SRC_JS" 2>/dev/null | grep -v 'js/tests/' || true)
report "no-csstext" "style.cssText injection — move styles into a CSS class." "$hits"

# 3. no-var-fallback-hex
#    No var(--token, #fallback). Fallback hex bypasses the theme contract.
hits=$(grep -rnE 'var\(--[a-z-]+,\s*#' "$FRONTEND" \
    --include='*.js' --include='*.html' --include='styles.css' 2>/dev/null || true)
report "no-var-fallback-hex" "var(--token, #fallback) — drop the fallback; it bypasses themes." "$hits"

# 4. no-styled-attrs
#    No inline style="..." that touches color / background / border / shadow /
#    font / padding values. Layout-only (display / gap / flex / margin) is
#    tolerated; the goal is zero on the color and typography axes.
hits=$(grep -rnE 'style="[^"]*(color|background|border|shadow|font-size|font-family|padding)' \
    "$SRC_JS" "$SRC_HTML" 2>/dev/null || true)
report "no-styled-attrs" "Inline style= with color/background/border/shadow/font/padding — use a class." "$hits"

# 5. no-style-color-from-js
#    No .style.<color-axis-property> assignments. Dynamic positioning
#    (.style.left / .top / .width / .transform) and display toggles are fine;
#    color, background, border, font, padding values must come from CSS.
hits=$(grep -rnE '\.style\.(color|background|backgroundColor|borderColor|font|fontFamily|fontSize|paddingTop|paddingBottom|paddingLeft|paddingRight|margin|marginTop|marginBottom|marginLeft|marginRight|gap|opacity)\b' \
    "$SRC_JS" 2>/dev/null | grep -v 'js/tests/' || true)
report "no-style-color-from-js" "JS-set color/background/border/font/margin/padding/gap/opacity — use a class." "$hits"

# 6. no-native-dialogs
#    window.confirm / window.prompt / window.alert are banned — they're
#    unstyled on every platform and disabled in iOS WKWebView. Use the
#    BB.ui.show{Confirm,Prompt}Dialog / BB.ui.showToast helpers instead.
hits=$(grep -rnE '\b(window\.)?(confirm|prompt|alert)\s*\(' "$SRC_JS" 2>/dev/null \
    | grep -vE 'showConfirmDialog|showPromptDialog|confirmAction|confirmDelete|confirmBtn|\.confirm-message|/\*|\*\s|// ' \
    | grep -v 'js/tests/' || true)
report "no-native-dialogs" "window.confirm/prompt/alert are banned — use BB.ui.show{Confirm,Prompt}Dialog or showToast." "$hits"

# 7. theme-token-coverage
#    Every :root token in styles.css must either be mapped by themes.js
#    COLOR_MAP, derived in applyTheme, or carry an /* invariant */ or
#    /* composition */ annotation on the same line. Catches dangling
#    tokens that silently fall back to the hardcoded :root value.
root_tokens=$(awk '/^:root \{/{flag=1; next} /^\}/{flag=0} flag' "$SRC_CSS" \
    | grep -oE -- '--[a-z-]+' | sort -u || true)
unmapped=""
for tok in $root_tokens; do
    # Allow tokens marked invariant or composition on the declaration line.
    if grep -E -- "$tok:[^;]*;.*(invariant|composition)" "$SRC_CSS" >/dev/null 2>&1; then continue; fi
    # Mapped directly or referenced as a property in themes.js?
    if grep -F -- "'$tok'" "$SRC_JS/themes.js" >/dev/null 2>&1; then continue; fi
    unmapped="$unmapped$tok"$'\n'
done
if [ -n "$unmapped" ]; then
    report "theme-token-coverage" "Token in :root is neither themed nor marked invariant/composition." "$unmapped"
fi

if [ $violations -eq 0 ]; then
    echo "frontend lint: clean"
    exit 0
else
    echo
    echo "frontend lint: $violations rule(s) failed"
    exit 1
fi
