# -*- coding: utf-8 -*-
"""TASK-D3 site gates — mechanical checks against the static site's known failure mode:
silent per-page drift. Run from the repo root:  python scripts/site_gates.py

Gates (all must pass; non-zero exit on any failure):
  1. BOM        — every website/*.html begins with EF BB BF (byte-checked)
  2. emoji      — zero emoji/pictographs in website/*.html (codepoint ranges; NEVER grep —
                  Windows grep false-negatives multibyte emoji, proven in D0)
  3. nav        — the <nav>…</nav> and <footer>…</footer> blocks of all pages are identical
                  after stripping the active-link marker
  4. analytics  — the umami snippet is present on every public page (nonpublic draft exempt)
  5. no-CDN     — zero fonts.googleapis.com / fonts.gstatic.com references
  6. banned     — zero legacy hexes / 999px radius / box-shadow in pages + site.css
  7. style      — zero embedded <style> blocks in pages (everything lives in site.css)
  8. cachebust  — every site.css / icons.svg reference carries ?v=<N>
"""
import io, os, re, sys

ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
W = os.path.join(ROOT, "website")

PAGES = ["index.html", "kapcsolat.html", "alkalmazasok.html", "technologiak.html",
         "biztonsagimentes.html", "gyik.html", "szolgaltatasok-nonpublic.html"]
ANALYTICS_EXEMPT = {"szolgaltatasok-nonpublic.html"}
ANALYTICS_MARK = "https://stats.felhom.eu/script.js"

BANNED = ["fonts.googleapis.com", "fonts.gstatic.com",
          "#0d1117", "#161b22", "#1c2128", "#30363d", "#238636", "#da3633", "#d29922",
          "border-radius: 999px", "box-shadow"]

fails = []


def fail(msg):
    fails.append(msg)
    print("FAIL:", msg)


def is_emoji(ch):
    o = ord(ch)
    return any(a <= o <= b for a, b in [
        (0x1F000, 0x1FAFF),  # pictographs, emoticons, transport, symbols-ext, cards
        (0x2600, 0x27BF),    # misc symbols + dingbats (incl. ✓ ✗ ★ ⚠ — sprite icons instead)
        (0x2300, 0x23FF),    # misc technical (⏱ ⏳ ⌛ …)
        (0x2B00, 0x2BFF),    # ⭐ etc.
        (0xFE00, 0xFE0F),    # variation selectors
        (0x1F1E6, 0x1F1FF),  # regional indicators
    ])


def norm_block(b):
    b = b.replace(' class="active"', "")
    b = re.sub(r'\s*aria-current="[^"]*"', "", b)
    return b


pages = {}
for p in PAGES:
    path = os.path.join(W, p)
    raw = io.open(path, "rb").read()
    # gate 1: BOM
    if raw[:3] != b"\xef\xbb\xbf":
        fail("%s: missing UTF-8 BOM (first bytes: %s)" % (p, raw[:3].hex()))
    pages[p] = raw.decode("utf-8-sig")

# gate 2: emoji (pages + the shared stylesheet)
total_emoji = 0
emoji_targets = dict(pages)
_css = os.path.join(W, "assets", "site.css")
if os.path.exists(_css):
    emoji_targets["assets/site.css"] = io.open(_css, encoding="utf-8").read()
for p, s in emoji_targets.items():
    hits = [(i, ch) for i, ch in enumerate(s) if is_emoji(ch)]
    if hits:
        total_emoji += len(hits)
        sample = " ".join(ch for _, ch in hits[:10])
        fail("%s: %d emoji (%s ...)" % (p, len(hits), sample))
if total_emoji:
    print("  emoji total: %d" % total_emoji)

# gate 3: nav + footer consistency
ref_nav = ref_footer = None
for p, s in pages.items():
    nm = re.search(r"<nav>.*?</nav>", s, re.DOTALL)
    fm = re.search(r"<footer>.*?</footer>", s, re.DOTALL)
    if not nm or not fm:
        fail("%s: missing <nav> or <footer>" % p)
        continue
    nav, foot = norm_block(nm.group(0)), norm_block(fm.group(0))
    if ref_nav is None:
        ref_nav, ref_footer, ref_page = nav, foot, p
    else:
        if nav != ref_nav:
            fail("%s: <nav> differs from %s (after active-marker normalization)" % (p, ref_page))
        if foot != ref_footer:
            fail("%s: <footer> differs from %s" % (p, ref_page))

# gate 4: analytics presence
for p, s in pages.items():
    if p in ANALYTICS_EXEMPT:
        continue
    if ANALYTICS_MARK not in s:
        fail("%s: analytics snippet missing (%s)" % (p, ANALYTICS_MARK))

# gate 5+6: banned strings in pages + site.css
targets = dict(pages)
css_path = os.path.join(W, "assets", "site.css")
if os.path.exists(css_path):
    targets["assets/site.css"] = io.open(css_path, encoding="utf-8").read()
for name, s in targets.items():
    low = s.lower()
    for pat in BANNED:
        c = low.count(pat.lower())
        if c:
            fail("%s: banned %r ×%d" % (name, pat, c))

# gate 7: no embedded style blocks
for p, s in pages.items():
    c = len(re.findall(r"<style[\s>]", s))
    if c:
        fail("%s: %d embedded <style> block(s)" % (p, c))

# gate 8: cache-busting on shared assets
for p, s in pages.items():
    for m in re.finditer(r"/assets/(site\.css|icons\.svg)([^\"'#\s>]*)", s):
        if not m.group(2).startswith("?v="):
            fail("%s: %s referenced without ?v= (got %r)" % (p, m.group(1), m.group(0)))

if fails:
    print("\nSITE GATES FAILED: %d problem(s)" % len(fails))
    sys.exit(1)
print("site gates OK — BOM, emoji=0, nav/footer consistent, analytics present, no CDN, no legacy tokens, no <style>, cache-busted assets")
