#!/usr/bin/env python3
"""Phase 9 — QA-Sweep über alle Prototyp-Seiten.
Prüft: Console-Errors, tote interne Links, img ohne alt/width/height, Inputs ohne Label,
Headings-Hierarchie, fehlende lang/canonical, horizontale Scrollbalken (390px), Screenshots 4 Viewports.
"""
import asyncio, json, os, glob
from playwright.async_api import async_playwright

BASE = "http://127.0.0.1:8471"
OUT = "/home/aria/projekte/orlando-redesign/evidence/audits"
SHOTS = "/home/aria/projekte/orlando-redesign/evidence/screenshots"
os.makedirs(OUT, exist_ok=True)

pages = sorted(os.path.basename(p) for p in glob.glob("/home/aria/projekte/orlando-redesign/prototype/de/*.html"))
targets = [f"/de/{p}" for p in pages] + ["/en/index.html"]

CHECK_JS = """() => {
  const out = {};
  out.imgsNoAlt = [...document.querySelectorAll('img:not([alt])')].map(i => i.src.split('/').pop());
  out.imgsNoDim = [...document.querySelectorAll('img:not([width]), img:not([height])')].map(i => i.src.split('/').pop());
  out.inputsNoLabel = [...document.querySelectorAll('input:not([type=hidden]), select, textarea')]
    .filter(el => !el.labels?.length && !el.getAttribute('aria-label') && !el.closest('.visually-hidden'))
    .map(el => el.name || el.id || el.type);
  const hs = [...document.querySelectorAll('h1,h2,h3,h4,h5,h6')].map(h => +h.tagName[1]);
  out.h1Count = hs.filter(x => x === 1).length;
  out.headingJumps = hs.filter((x, i) => i && x - hs[i-1] > 1).length;
  out.lang = document.documentElement.lang || 'FEHLT';
  out.hasCanonical = !!document.querySelector('link[rel=canonical]');
  out.hasDescription = !!document.querySelector('meta[name=description]');
  out.title = document.title;
  out.links = [...document.querySelectorAll('a[href]')].map(a => a.getAttribute('href'))
    .filter(h => h && !h.startsWith('http') && !h.startsWith('#') && !h.startsWith('mailto') && !h.startsWith('tel'));
  out.hScroll = document.documentElement.scrollWidth > document.documentElement.clientWidth + 1;
  out.focusables = document.querySelectorAll('a,button,input,select,textarea,[tabindex]').length;
  return out;
}"""

async def main():
    report = {}
    dead = {}
    async with async_playwright() as pw:
        b = await pw.chromium.launch()
        for path in targets:
            report[path] = {}
            for w, h, label in [(1440, 900, "1440"), (1024, 768, "1024"), (768, 1024, "768"), (390, 844, "390")]:
                ctx = await b.new_context(viewport={"width": w, "height": h})
                p = await ctx.new_page()
                errs = []
                p.on("console", lambda m: errs.append(m.text) if m.type == "error" else None)
                p.on("pageerror", lambda e: errs.append(str(e)))
                resp = await p.goto(BASE + path, wait_until="networkidle")
                await p.evaluate("""async () => {
                    document.documentElement.style.scrollBehavior = 'auto';
                    for (let y = 0; y <= document.body.scrollHeight; y += 500) {
                        window.scrollTo({top: y, behavior: 'instant'});
                        await new Promise(r => setTimeout(r, 40));
                    }
                    window.scrollTo({top: 0, behavior: 'instant'});
                }""")
                await p.wait_for_timeout(400)
                checks = await p.evaluate(CHECK_JS)
                checks["consoleErrors"] = errs[:5]
                checks["httpStatus"] = resp.status if resp else None
                name = path.strip("/").replace("/", "-").replace(".html", "")
                await p.screenshot(path=f"{SHOTS}/qa-{name}__{label}.png", full_page=True)
                report[path][label] = checks
                # Link-Check nur einmal pro Seite (1440)
                if label == "1440":
                    for href in set(checks["links"]):
                        target = os.path.normpath(os.path.join(os.path.dirname(path), href.split("?")[0].split("#")[0]))
                        fs = "/home/aria/projekte/orlando-redesign/prototype" + target
                        if not os.path.exists(fs):
                            dead.setdefault(path, []).append(href)
                await ctx.close()
        await b.close()
    with open(f"{OUT}/qa-report.json", "w") as f:
        json.dump({"pages": report, "deadLinks": dead}, f, indent=1, ensure_ascii=False)
    # Kompakt-Ausgabe
    problems = 0
    for path, vps in report.items():
        c = vps["1440"]
        issues = []
        if c["consoleErrors"]: issues.append(f"console:{len(c['consoleErrors'])}")
        if c["imgsNoAlt"]: issues.append(f"img-ohne-alt:{c['imgsNoAlt']}")
        if c["inputsNoLabel"]: issues.append(f"input-ohne-label:{c['inputsNoLabel']}")
        if c["h1Count"] != 1: issues.append(f"h1-count:{c['h1Count']}")
        if c["headingJumps"]: issues.append(f"heading-jumps:{c['headingJumps']}")
        if not c["hasCanonical"]: issues.append("canonical-fehlt")
        if not c["hasDescription"]: issues.append("description-fehlt")
        if vps["390"]["hScroll"]: issues.append("H-SCROLL@390!")
        if path in dead: issues.append(f"tote-links:{dead[path]}")
        if issues:
            problems += 1
            print(f"{path}: {' | '.join(str(i) for i in issues)}")
        else:
            print(f"{path}: ✓")
    print(f"\n{len(targets)} Seiten geprüft, {problems} mit Befunden. Details: {OUT}/qa-report.json")

asyncio.run(main())
