"""Runs ON gex44 (GPU). usage: parts_probe.py <site_url> <out_dir>
Moving-part check on browse/vehicles.html: every rotor must stay on its hub while it spins (box centre drift < 3 % of its
size over 6 frozen frames); guns, turrets and control surfaces are filmed at 4 poses (close-up) for a human look.
<out>/parts.json = drifts + errors; <out>/<slug>-<node>-<i>.jpg."""
import asyncio, json, os, sys
from playwright.async_api import async_playwright
GPU = ['--no-sandbox', '--use-angle=gl-egl', '--use-gl=angle', '--ignore-gpu-blocklist', '--enable-gpu']


async def main(url, out):
    os.makedirs(out, exist_ok=True)
    rep = {'drift': {}, 'bad': [], 'page_errors': []}
    async with async_playwright() as p:
        b = await p.chromium.launch(args=GPU)
        pg = await b.new_page(viewport={'width': 900, 'height': 600})
        pg.on('pageerror', lambda e: rep['page_errors'].append(str(e)[:300]))
        await pg.goto(url + 'vehicles.html'); await pg.wait_for_function('window.__ready === true', timeout=180000)
        for v in await pg.evaluate('window.__list'):
            n = v['nodes']
            rotors, guns = n.get('rotors') or [], n.get('guns') or []
            if not (rotors or guns or n.get('turret') or n.get('surfaces')):
                continue
            await pg.evaluate(f"window.__show({json.dumps(v['id'])})")
            for r in rotors:
                cs = []
                for i in range(6):
                    await pg.evaluate(f'window.__freeze({i * 0.037}, 0)')
                    await pg.wait_for_timeout(60)
                    cs.append(await pg.evaluate(f'window.__partBox({json.dumps(r)})'))
                # the blades' mean point sits on the hub for evenly spaced blades whatever their count (a box centre wobbles
                # with 3 or 5 blades even on a perfect pivot)
                size = max(cs[0]['s']); drift = max(max(abs(c['mean'][k] - cs[0]['mean'][k]) for k in range(3)) for c in cs)
                rep['drift'][f"{v['slug']}/{r}"] = round(drift / size, 4)
                if drift / size > 0.03: rep['bad'].append(f"{v['slug']} {r} drifts {drift:.2f} m while spinning")
                if 'Tail' in r or 'Rear' in r:
                    for i, sp in enumerate((0.0, 0.02, 0.05)):
                        await pg.evaluate(f'window.__freeze({sp}, 0)'); await pg.evaluate(f"window.__focus({json.dumps(r)}, [1, 0.25, 0.35], 2.2)")
                        await pg.wait_for_timeout(80); await pg.locator('#c').screenshot(path=f"{out}/{v['slug']}-{r}-{i}.jpg", type='jpeg', quality=85)
            for g in guns + (n.get('turret') or [])[:1] + (n.get('surfaces') or [])[:2]:
                for i, st in enumerate((0.0, 1.7, 3.5, 5.2)):
                    await pg.evaluate(f'window.__freeze(0, {st})')
                    await pg.evaluate(f"window.__focus({json.dumps(g)}, [1, 0.35, 0.5], {3.0 if g in guns else 1.6})")
                    await pg.wait_for_timeout(80); await pg.locator('#c').screenshot(path=f"{out}/{v['slug']}-{g}-{i}.jpg", type='jpeg', quality=85)
        await b.close()
    json.dump(rep, open(f'{out}/parts.json', 'w'), indent=1)
    print(json.dumps({'bad': rep['bad'], 'page_errors': rep['page_errors'][:3], 'rotors': len(rep['drift'])}, indent=1))

asyncio.run(main(*sys.argv[1:3]))
