"""
BUILD #10 판선택 모드 검증 스크립트
- 너클붐 선택 → 패널 열기
- 판 교차선 모드 버튼 클릭 (faceMode=1)
- 너클붐 면 클릭 → 판 A 빨강 (region mesh)
- 다른 좌표 면 클릭 → 판 B 파랑 (region mesh)
- 각 단계 실제 스크린샷 저장
headless=False 필수. CEO 실제 확인용.
"""
import asyncio, os
from playwright.async_api import async_playwright

OUT = r'E:\도진팩토리\3D스캔및티칭시스템\shots'
os.makedirs(OUT, exist_ok=True)

async def shot(pg, name, label=''):
    path = os.path.join(OUT, name)
    await pg.screenshot(path=path, full_page=False)
    print(f'  [스샷] {name}  {label}')

async def main():
    async with async_playwright() as p:
        br = await p.chromium.launch(
            headless=False,
            args=['--window-size=1536,864']
        )
        pg = await br.new_page(viewport={'width':1536,'height':864})
        errs = []
        pg.on('pageerror', lambda e: errs.append(str(e)))
        pg.on('console', lambda m: print(f'  [{m.type}] {m.text}') if m.type in ('error','warn','log') else None)

        print('=== 1: 페이지 로드 ===')
        await pg.goto('http://localhost:8090/DOZIKWORKS_OS_v5_locked.dc.html')
        await pg.wait_for_timeout(4000)
        banner = await pg.evaluate('() => document.getElementById("dzw-build-banner")?.textContent || "없음"')
        print(f'  BUILD 배너: {banner}')
        await shot(pg, 'r10_01_load.png', f'배너={banner}')

        if 'BUILD #10' not in banner:
            print(f'  ⚠️  BUILD #10 배너 없음 (현재: {banner}) — 캐시 문제일 수 있음')
            await br.close()
            return

        print('\n=== 2: 너클붐 선택 + 패널 열기 ===')
        state = await pg.evaluate('''() => {
            const app = window.__dzw;
            if(!app) return {err:'__dzw없음'};
            if(!app._knuckleMesh) return {err:'knuckleMesh없음'};
            app._knuckleSelect();
            return {
                panelDisplay: document.getElementById('dzw-xform-panel')?.style?.display,
            };
        }''')
        print(f'  패널: {state}')
        await pg.wait_for_timeout(500)
        await shot(pg, 'r10_02_panel.png', f'panel={state.get("panelDisplay")}')

        if state.get('panelDisplay') != 'block':
            print('  ⚠️  패널 안 열림')
            await br.close()
            return

        print('\n=== 3: 판 선택 버튼 클릭 ===')
        btn_info = await pg.evaluate('''() => {
            const btn = document.getElementById('dzw-edge-mode-btn');
            if(!btn) return {err:'버튼없음'};
            const r = btn.getBoundingClientRect();
            return {x:Math.round(r.left+r.width/2), y:Math.round(r.top+r.height/2),
                    inView: r.right<1536&&r.bottom<864&&r.left>0&&r.top>0};
        }''')
        print(f'  버튼: {btn_info}')
        if not btn_info.get('inView'):
            print('  ⚠️  버튼 화면 밖')
            await br.close()
            return

        await pg.mouse.click(btn_info['x'], btn_info['y'])
        await pg.wait_for_timeout(400)
        mode = await pg.evaluate('() => window.__dzw._faceMode')
        print(f'  faceMode: {mode}')
        await shot(pg, 'r10_03_mode_on.png', f'faceMode={mode}')

        if mode != 1:
            print(f'  ⚠️  faceMode≠1')
            await br.close()
            return

        print('\n=== 4: 판 A 클릭 (너클붐 면) ===')
        knuckle_xy = await pg.evaluate('''() => {
            const app = window.__dzw;
            const mesh = app._knuckleMesh;
            const cam = app.three.cam;
            // canvas 실제 rect 사용
            const canvas = document.querySelector('canvas');
            const r = canvas.getBoundingClientRect();
            for(let py=r.top+30; py<r.bottom-30; py+=20)
              for(let px=r.left+30; px<r.right-30; px+=20){
                const nx=((px-r.left)/r.width)*2-1;
                const ny=-((py-r.top)/r.height)*2+1;
                app._ray.setFromCamera({x:nx,y:ny}, cam);
                const h=app._ray.intersectObjects([mesh],true);
                if(h.length) return {px:Math.round(px), py:Math.round(py), faceIndex:h[0].faceIndex};
              }
            return null;
        }''')
        print(f'  너클붐 좌표: {knuckle_xy}')
        if not knuckle_xy:
            print('  ⚠️  너클붐 못 찾음')
            await br.close()
            return

        await pg.mouse.click(knuckle_xy['px'], knuckle_xy['py'])
        await pg.wait_for_timeout(1500)  # region growing 처리 대기

        face_a = await pg.evaluate('''() => {
            const app = window.__dzw;
            return {
                faceMode: app._faceMode,
                hasFaceARegion: app._faceARegion instanceof Set,
                regionSize: app._faceARegion ? app._faceARegion.size : 0,
                hasMesh: !!app._faceAMesh,
                hasBorder: !!app._faceABorder,
                overlay: document.getElementById('dzw-state-overlay')?.textContent
            };
        }''')
        print(f'  판 A 결과: {face_a}')
        await shot(pg, 'r10_04_face_a.png',
                   f'region={face_a.get("regionSize")}tri, mesh={face_a.get("hasMesh")}, border={face_a.get("hasBorder")}')

        if not face_a.get('hasFaceARegion'):
            print('  ⚠️  판 A region 없음')
            await br.close()
            return

        print('\n=== 5: 판 B 클릭 (법선 각도 크게 다른 면) ===')
        b_xy = await pg.evaluate('''() => {
            const app = window.__dzw;
            const mesh = app._knuckleMesh;
            const cam = app.three.cam;
            const canvas = document.querySelector('canvas');
            const r = canvas.getBoundingClientRect();
            const regionA = app._faceARegion;
            const pos = mesh.geometry.attributes.position;
            const nor = mesh.geometry.attributes.normal;
            // 판 A 평균 법선
            let anx=0,any=0,anz=0,cnt=0;
            for(const t of regionA){
              anx+=nor.getX(t*3); any+=nor.getY(t*3); anz+=nor.getZ(t*3); cnt++;
            }
            anx/=cnt; any/=cnt; anz/=cnt;
            // 법선 각도 차이가 큰(cos<0.5 → 60도 이상) 면 우선 선택
            let best=null, bestDot=1;
            for(let py=r.top+40; py<r.bottom-40; py+=15)
              for(let px=r.left+40; px<r.right-40; px+=15){
                const nx=((px-r.left)/r.width)*2-1;
                const ny=-((py-r.top)/r.height)*2+1;
                app._ray.setFromCamera({x:nx,y:ny}, cam);
                const h=app._ray.intersectObjects([mesh],true);
                if(!h.length) continue;
                const fi=h[0].faceIndex;
                if(regionA.has(fi)) continue;
                const bnx=nor.getX(fi*3),bny=nor.getY(fi*3),bnz=nor.getZ(fi*3);
                const dot=anx*bnx+any*bny+anz*bnz;
                if(dot<bestDot){ bestDot=dot; best={px:Math.round(px),py:Math.round(py),faceIndex:fi,dot:dot.toFixed(2)}; }
              }
            return best;
        }''')
        print(f'  판 B 좌표: {b_xy}')
        if not b_xy:
            print('  ⚠️  판 B 다른 면 못 찾음')
            await br.close()
            return

        await pg.mouse.click(b_xy['px'], b_xy['py'])
        await pg.wait_for_timeout(1500)

        face_b = await pg.evaluate('''() => {
            const app = window.__dzw;
            return {
                faceMode: app._faceMode,
                hasFaceBRegion: app._faceBRegion instanceof Set,
                regionSize: app._faceBRegion ? app._faceBRegion.size : 0,
                hasMesh: !!app._faceBMesh,
                hasBorder: !!app._faceBBorder
            };
        }''')
        print(f'  판 B 결과: {face_b}')
        await shot(pg, 'r10_05_face_b.png',
                   f'region={face_b.get("regionSize")}tri, mesh={face_b.get("hasMesh")}, border={face_b.get("hasBorder")}')

        if errs:
            print(f'\n⚠️  페이지 에러: {errs}')

        print('\n=== 완료 ===')
        print(f'shots 폴더: {OUT}')
        print('CEO가 r10_04_face_a.png, r10_05_face_b.png 확인 — 판이 색칠되어 있어야 성공')
        await br.close()

asyncio.run(main())
