import asyncio
from playwright.async_api import async_playwright

async def main():
    async with async_playwright() as p:
        br = await p.chromium.launch(headless=False)
        pg = await br.new_page()
        await pg.set_viewport_size({'width':1400,'height':900})

        errors = []
        pg.on('pageerror', lambda err: errors.append(f'PAGEERROR: {str(err)[:300]}'))
        pg.on('console', lambda msg: errors.append(f'[{msg.type}] {msg.text[:300]}') if msg.type=='error' else None)

        await pg.goto('http://localhost:8090/DOZIKWORKS_OS_v4_pathcreate.dc.html')
        print('로드 중...')
        await pg.wait_for_timeout(14000)

        real_errors = [e for e in errors if 'favicon' not in e.lower()]
        print(f'\n새 콘솔 에러 (favicon 제외): {len(real_errors)}개')
        for e in real_errors:
            print(f'  {e}')

        # DZW_ENGINE 노출 + FK 4점 검증
        result = await pg.evaluate("""() => {
            const app = window.__dzw;
            if (!app) return {error: '__dzw 없음'};
            const E = window.DZW_ENGINE;
            return {
                DZW_ENGINE_type: typeof E,
                DZW_ENGINE_keys: E ? Object.keys(E) : [],
                fk_is_function: typeof (E && E.fk),
                checkJointLimits_is_function: typeof (E && E.checkJointLimits),
                class_fk_delegates: typeof app.fk === 'function',
                tcAlive: !!(app._tc && typeof app._tc.setMode === 'function'),
                fkOk: typeof app.fk === 'function',
            };
        }""")
        print('\n=== DZW_ENGINE 노출 확인 ===')
        print(f"  DZW_ENGINE 타입: {result.get('DZW_ENGINE_type')}")
        print(f"  DZW_ENGINE 키: {result.get('DZW_ENGINE_keys')}")
        print(f"  fk 함수: {result.get('fk_is_function')}")
        print(f"  checkJointLimits 함수: {result.get('checkJointLimits_is_function')}")
        print(f"  클래스.fk 위임: {result.get('class_fk_delegates')}")
        print(f"  기즈모(_tc): {result.get('tcAlive')}")

        # FK 4점 검증 (기준값 유지 확인)
        fk_result = await pg.evaluate("""async () => {
            const app = window.__dzw;
            const r = await fetch('/weld_path.json');
            const wp = await r.json();
            const arcPoses = wp.poses.filter(p => p.arc === 1);
            const segFlat = wp.segs.flat();
            if (arcPoses.length !== segFlat.length)
                return {error: `불일치: ${arcPoses.length} vs ${segFlat.length}`};
            const n = arcPoses.length;
            return [0, Math.floor(n/3), Math.floor(n*2/3), n-1].map(i => {
                const fkr = app.fk(arcPoses[i]);
                const t = segFlat[i];
                const dist = Math.sqrt((fkr.x-t[0])**2+(fkr.y-t[1])**2+(fkr.z-t[2])**2);
                return {idx:i, dist:+dist.toFixed(4), pass:dist<=1.0};
            });
        }""")
        expected = [0.0013, 0.0015, 0.0011, 0.0016]
        print('\n=== FK 4점 오차 (기준: 0.0016mm) ===')
        if isinstance(fk_result, list):
            all_pass = all(r['pass'] for r in fk_result)
            for i, r in enumerate(fk_result):
                match = '✅' if r['pass'] else '❌'
                same = '동일' if r['dist'] == expected[i] else f"이전:{expected[i]}"
                print(f"  {match} idx={r['idx']} {r['dist']}mm ({same})")
            print(f"  최종: {'✅ 합격' if all_pass else '❌ 불합격'}")
        else:
            print(' ', fk_result)

        # checkJointLimits 동작 증명 — weld_path.json 관절각으로 검사
        limits_result = await pg.evaluate("""async () => {
            const app = window.__dzw;
            const E = window.DZW_ENGINE;
            if (!E || !E.checkJointLimits) return {error: 'checkJointLimits 없음'};

            const r = await fetch('/weld_path.json');
            const wp = await r.json();
            const poses = wp.poses.filter(p => p.arc === 1);

            // 각 시임별 초과 집계
            const summary = {total: poses.length, violations_by_axis: {}, total_violations: 0, sample_violations: []};
            poses.forEach((pose, idx) => {
                const chk = E.checkJointLimits(pose);
                if (!chk.ok) {
                    summary.total_violations++;
                    chk.violations.forEach(v => {
                        summary.violations_by_axis[v.axis] = (summary.violations_by_axis[v.axis]||0)+1;
                        if (summary.sample_violations.length < 3)
                            summary.sample_violations.push({idx, axis:v.axis, value:+v.value.toFixed(2), min:v.min, max:v.max, over:v.over});
                    });
                }
            });
            // 미검사 축
            const test = E.checkJointLimits({S:0,L:0,U:0,R:0,B:0,T:0});
            summary.unchecked = test.unchecked;
            return summary;
        }""")
        print('\n=== checkJointLimits SEAM 전체 결과 ===')
        if 'error' in limits_result:
            print(' ', limits_result['error'])
        else:
            print(f"  검사 점수: {limits_result.get('total')}개")
            print(f"  한계 초과 점수: {limits_result.get('total_violations')}개")
            print(f"  축별 초과 횟수: {limits_result.get('violations_by_axis')}")
            print(f"  미검사 축: {limits_result.get('unchecked')}")
            print(f"  초과 샘플(최대3):")
            for s in limits_result.get('sample_violations', []):
                print(f"    idx={s['idx']} {s['axis']}축={s['value']}° (한계:{s['min']}~{s['max']}, 초과:{s['over']}°)")

        await pg.screenshot(path='engine_split_verify.png')
        print('\n스크린샷 → engine_split_verify.png')
        await br.close()

asyncio.run(main())
