"""토치 회전 기능 검증: 선택→회전 모드 전환이 실제로 _tc에 걸리는지 + 회전 시 토치 자세 변화."""
import asyncio
from playwright.async_api import async_playwright
URL='http://localhost:8090/DOZIKWORKS_OS_v5_locked.dc.html'

async def main():
    async with async_playwright() as p:
        br=await p.chromium.launch(channel='msedge',headless=True)
        pg=await br.new_page(viewport={'width':1536,'height':864})
        logs=[]
        pg.on('console',lambda m:logs.append(m.text))
        await pg.goto(URL)
        # 토치 로드 대기
        await pg.wait_for_function("window.__dzw && window.__dzw._torchPivot", timeout=15000)
        res=await pg.evaluate("""()=>{
          const d=window.__dzw; const out={};
          out.hasTc = !!d._tc;
          // 1) 토치 조정 시작 (선택→토치클릭과 동일 경로)
          d.torchAdjust();
          out.adjustOn = d._torchAdjustOn;
          out.mode_after_adjust = d._tc ? d._tc.mode : null;
          out.attached_to = (d._tc && d._tc.object) ? (d._tc.object===d._torchPivot?'pivot':d._tc.object.name||'other') : null;
          // 2) 회전 아이콘 클릭 경로
          d.selectTool('rotate');
          out.mode_after_rotate_tool = d._tc ? d._tc.mode : null;
          out.still_attached = (d._tc && d._tc.object) ? (d._tc.object===d._torchPivot?'pivot':'other'):null;
          out.torchAdjustOn_after = d._torchAdjustOn;
          // 3) 실제 회전 — X/Y/Z 각각 20도, 노즐끝과 플랜지끝(먼 점) 이동량 측정
          const T=d._T; const me=d._torchMesh; const piv=d._torchPivot;
          out.pivot_is_parent = (me.parent===piv);
          out.piv_autoUpdate = piv.matrixAutoUpdate;
          out.me_autoUpdate = me.matrixAutoUpdate;
          me.geometry.computeBoundingBox(); const bb=me.geometry.boundingBox;
          const farLocal=new T.Vector3(0,0,bb.min.z); // 플랜지끝(로컬)
          function measure(axis){
            me.updateMatrixWorld(true);
            const n0=new T.Vector3().setFromMatrixPosition(me.matrixWorld);
            const f0=farLocal.clone().applyMatrix4(me.matrixWorld);
            const ang=20*Math.PI/180;
            if(axis==='x') piv.rotateX(ang); if(axis==='y') piv.rotateY(ang); if(axis==='z') piv.rotateZ(ang);
            piv.updateMatrixWorld(true); me.updateMatrixWorld(true);
            const n1=new T.Vector3().setFromMatrixPosition(me.matrixWorld);
            const f1=farLocal.clone().applyMatrix4(me.matrixWorld);
            if(axis==='x') piv.rotateX(-ang); if(axis==='y') piv.rotateY(-ang); if(axis==='z') piv.rotateZ(-ang);
            piv.updateMatrixWorld(true);
            return {nozzle:+n0.distanceTo(n1).toFixed(1), flange:+f0.distanceTo(f1).toFixed(1)};
          }
          out.rotX=measure('x'); out.rotY=measure('y'); out.rotZ=measure('z');
          // ★ 판별: 피벗 20도 회전 시 메시 월드 쿼터니언 변화각 (점거리와 무관, 전파여부 직결)
          function qdelta(axis){
            const q0=new T.Quaternion(); me.getWorldQuaternion(q0);
            const ang=20*Math.PI/180;
            if(axis==='x') piv.rotateX(ang); if(axis==='y') piv.rotateY(ang); if(axis==='z') piv.rotateZ(ang);
            piv.updateMatrixWorld(true); me.updateMatrixWorld(true);
            const q1=new T.Quaternion(); me.getWorldQuaternion(q1);
            if(axis==='x') piv.rotateX(-ang); if(axis==='y') piv.rotateY(-ang); if(axis==='z') piv.rotateZ(-ang);
            piv.updateMatrixWorld(true);
            return +(q0.angleTo(q1)*180/Math.PI).toFixed(2);
          }
          out.qdelta_deg={x:qdelta('x'),y:qdelta('y'),z:qdelta('z')};
          return out;
        }""")
        print("=== 회전 검증 결과 ===")
        for k,v in res.items(): print(f"  {k}: {v}")
        print("=== 관련 콘솔 로그 ===")
        for l in logs:
            if '토치' in l or 'torch' in l.lower() or '모드' in l: print("  ",l)
        await br.close()

asyncio.run(main())
