# dzw_safety_gate.js(블럭#5 JBI 안전게이트) 검증
# 흐름: 페이지 로드(콘솔에러 0) → 셀프테스트 방식으로 구간 등록(_selectAutoRegion→_registerSeamAsPath)
#       → 아크 ON/OFF로 구간 확정 → segsToJbi 호출 → 게이트 패널·판정·다운로드 1회 확인
# 사전: python dzw_server.py (8090) 실행 상태
import asyncio, json
from playwright.async_api import async_playwright

def is_noise(t):
    return 'Failed to load resource' in t  # 기존 404 폴링 노이즈(_torch_unlock.flag 등)

async def main():
    errors, noise, downloads = [], [], []
    async with async_playwright() as p:
        br = await p.chromium.launch(headless=True)
        ctx = await br.new_context(accept_downloads=True)
        pg = await ctx.new_page()
        await pg.set_viewport_size({'width':1400,'height':900})
        pg.on('console', lambda m: (noise if is_noise(m.text) else errors).append('[console.error] '+m.text) if m.type=='error' else None)
        pg.on('pageerror', lambda e: errors.append('[pageerror] '+str(e)))
        pg.on('download', lambda d: downloads.append(d.suggested_filename))
        await pg.goto('http://localhost:8090/DOZIKWORKS_OS_v5_locked.dc.html')
        await pg.wait_for_timeout(16000)
        print('=== 1. 로드 완료. 실질 콘솔에러:', len(errors), '/ 404노이즈:', len(noise))
        for e in errors: print('   ', e[:200])

        installed = await pg.evaluate("() => !!(window.DZW5_EDGEPATH && DZW5_EDGEPATH.segsToJbi && DZW5_EDGEPATH.segsToJbi.__dzwSafetyGate)")
        limits = await pg.evaluate("() => !!(window.DZW_ENGINE && window.DZW_ENGINE.checkJointLimits && window.DZW_DATA && window.DZW_DATA.jointLimits)")
        print('=== 2. 게이트 설치:', installed, '/ 한계엔진 로드:', limits)

        # 구간 등록 + 아크 ON/OFF (셀프테스트 32~65줄 재현)
        setup = await pg.evaluate("""() => {
            const app=window.__dzw, EP=window.DZW5_EDGEPATH, E=window.DZW5_ENGINE, T=app._T, km=app._knuckleMesh;
            if(!app||!km) return {ok:false, why:'앱 미초기화'};
            app._faceMode=1;
            const regA=EP._selectAutoRegion(km,14047), regB=EP._selectAutoRegion(km,10023);
            app._faceARegion=new Set(regA); app._faceBRegion=new Set(regB);
            const sb=E.sharedBoundary(km,app._faceARegion,app._faceBRegion);
            if(!sb.length) return {ok:false, why:'공유경계 0'};
            EP._registerSeamAsPath(app,sb);
            const seg=app._teachSegments[app._teachSegments.length-1];
            km.updateWorldMatrix(true,true);
            const prj=(arr)=>{ const v=new T.Vector3(...arr); km.localToWorld(v); v.project(app.three.cam); return {mx:v.x,my:v.y}; };
            const evt={button:0,clientX:500,clientY:400,pointerId:1,shiftKey:false};
            const n0=prj(seg.pts[2]), n1=prj(seg.pts[Math.min(seg.pts.length-1,80)]);
            EP.startArcOnMode(app);
            const r1=EP._handleArcClick(app,evt,n0.mx,n0.my,app.three.cam);
            const r2=EP._handleArcClick(app,evt,n1.mx,n1.my,app.three.cam);
            return {ok:r1===true&&r2===true&&app._arcSegPts.length===1, seamPts:seg.pts.length};
        }""")
        print('=== 3. 구간 등록:', json.dumps(setup, ensure_ascii=False))

        # 내보내기 실행 → 게이트 판정
        before_err = len(errors)
        await pg.evaluate("() => { DZW5_EDGEPATH.segsToJbi(window.__dzw); }")
        await pg.wait_for_timeout(3000)
        rep = await pg.evaluate("""() => {
            const r=window.__dzwSafetyGateLast, el=document.getElementById('dzw-safety-gate-panel');
            return { panel:!!el, panelHead: el?el.textContent.slice(0,120):null,
                     rep: r?{total:r.total, passCnt:r.passCnt, ik:r.ikFail.length, err:r.errFail.length,
                             lim:r.limFail.length, jump:r.jumpWarn.length, blocked:r.blocked,
                             limitsUnavailable:r.limitsUnavailable}:null };
        }""")
        print('=== 4. 게이트 리포트:', json.dumps(rep, ensure_ascii=False))
        print('=== 5. 다운로드 횟수:', len(downloads), downloads)
        print('=== 6. segsToJbi 중 신규 콘솔에러:', len(errors)-before_err)
        for e in errors[before_err:]: print('   ', e[:200])

        ok = (len(errors)==0 and installed and setup.get('ok') and rep['panel'] and rep['rep']
              and ((rep['rep']['blocked'] and len(downloads)==0) or (not rep['rep']['blocked'] and 1<=len(downloads)<=2 and downloads[0].endswith('.JBI'))))  # 블럭#8 의미파일 포함 2개까지 정상
        print('=== 최종:', 'PASS' if ok else 'FAIL')
        await br.close()

asyncio.run(main())
