# _ir_rehearsal_test.py — IR 발표(7/23) 시연 시나리오 리허설 자동시험 [CEO 승인 2026-07-21 "메인으로"]
# 시나리오: 로드 → 제품선택(패널 7탭) → 판A 클릭누적 → 판B 전환·클릭 → JBI 초안 생성
# 실행: python _ir_rehearsal_test.py [포트=8090]  | 결과: IR_REHEARSAL_LOG.txt 추가 기록, 실패 시 exit 1
# 녹화: shots/ir_rehearsal_*.webm (시연 실패 대비 증빙 영상)
import sys, os, time, datetime
from playwright.sync_api import sync_playwright

PORT = sys.argv[1] if len(sys.argv) > 1 else "8090"
URL = f"http://localhost:{PORT}/DOZIKWORKS_OS_v5_locked.dc.html"
HERE = os.path.dirname(os.path.abspath(__file__))
LOG = os.path.join(HERE, "IR_REHEARSAL_LOG.txt")
VID_DIR = os.path.join(os.path.dirname(HERE), "shots")

PROJ = """() => {
  const a = window.__dzw; if(!a || !a._knuckleMesh || !a.three) return [];
  const cam = a.three.cam || a.three.camera; const rnd = a.three.rnd || a.three.renderer;
  const rect = rnd.domElement.getBoundingClientRect(); const pts = [];
  a._knuckleMesh.traverse(o => {
    if (o.isMesh && pts.length < 30) {
      const pos = o.geometry && o.geometry.attributes && o.geometry.attributes.position; if(!pos) return;
      const step = Math.max(1, Math.floor(pos.count/10));
      for (let i = 0; i < pos.count && pts.length < 30; i += step) {
        const v = new THREE.Vector3().fromBufferAttribute(pos, i).applyMatrix4(o.matrixWorld);
        const p = v.clone().project(cam);
        if (p.z < 1 && Math.abs(p.x) < 0.85 && Math.abs(p.y) < 0.85)
          pts.push({x: rect.left + (p.x+1)/2*rect.width, y: rect.top + (1-(p.y+1)/2)*rect.height});
      }
    }
  });
  return pts;
}"""

results = []
def check(name, ok, detail=""):
    results.append((name, bool(ok), detail))
    print(("PASS " if ok else "FAIL ") + name + (" — " + str(detail) if detail else ""))

with sync_playwright() as p:
    b = p.chromium.launch()
    ctx = b.new_context(viewport={"width":1900,"height":950}, record_video_dir=VID_DIR)
    pg = ctx.new_page()
    rc_errs = []
    pg.on("console", lambda m: rc_errs.append(m.text[:100]) if m.type == "error" and "removeChild" in m.text else None)
    pg.goto(URL, wait_until="load")
    pg.wait_for_timeout(9000)

    # 1) 로드 무결
    check("1.로드-removeChild오류없음", len(rc_errs) == 0, f"{len(rc_errs)}건")
    check("1.앱기동", pg.evaluate("()=>!!window.__dzw && !!window.__dzw._knuckleMesh"))

    # 2) 제품 선택 → 패널 7탭
    pg.evaluate("()=>{window.__dzw._knuckleSelect&&window.__dzw._knuckleSelect();}")
    pg.wait_for_timeout(2000)
    tabs = pg.evaluate("()=>[...document.querySelectorAll('#dzw-xform-panel .dzw-tp-tab')].map(t=>t.textContent.trim())")
    check("2.티칭패널 7탭", len(tabs) == 7, tabs)

    # 3) 판A 클릭 누적 (실제 마우스 클릭)
    pg.evaluate("()=>{DZW5_EDGEPATH.startCurveMode(window.__dzw)}")
    pg.wait_for_timeout(400)
    pts = pg.evaluate(PROJ)
    for pt in pts[:3]:
        pg.mouse.click(pt["x"], pt["y"]); pg.wait_for_timeout(900)
    aSize = pg.evaluate("()=>window.__dzw._faceARegion?window.__dzw._faceARegion.size:0")
    check("3.판A 누적", aSize > 0, f"{aSize}면")
    fm = pg.evaluate("()=>window.__dzw._faceMode||0")
    check("3.판A 모드유지", fm in (1, 2), f"faceMode={fm}")

    # 4) 판B 전환 + 클릭
    pg.evaluate("()=>{DZW5_EDGEPATH.startToBMode(window.__dzw)}")
    pg.wait_for_timeout(400)
    for pt in pts[3:6]:
        pg.mouse.click(pt["x"], pt["y"]); pg.wait_for_timeout(900)
    bSize = pg.evaluate("()=>window.__dzw._faceBRegion?window.__dzw._faceBRegion.size:0")
    check("4.판B 누적", bSize > 0, f"{bSize}면")

    # 5) JBI 초안 생성 (포인트 2개 주입 → 다운로드 내용 검사; 화면 저장상태 오염 방지 위해 저장 안 함)
    pg.evaluate("""()=>{const S=DZW5_TEACH.state;
      S.pts.push({id:'RH1',seg:'구간1',type:'용접시작',cond:'W01',x:100,y:200,z:300,rx:-90,ry:0,rz:180});
      S.pts.push({id:'RH2',seg:'구간1',type:'용접끝',cond:'W01',x:110,y:200,z:300,rx:-90,ry:0,rz:180});}""")
    with pg.expect_download(timeout=8000) as dl:
        pg.evaluate("()=>DZW5_TEACH.exportJbi()")
    path = dl.value.path()
    txt = open(path, encoding="utf-8", errors="replace").read()
    ok_jbi = ("/JOB" in txt) and ("MOVL" in txt) and ("ARCON" in txt) and ("ARCOF" in txt) and txt.rstrip().endswith("END")
    check("5.JBI 초안 생성", ok_jbi, f"{len(txt)}바이트")

    pg.wait_for_timeout(1000)
    ctx.close()  # 녹화 저장
    b.close()

passed = sum(1 for _, ok, _ in results if ok)
total = len(results)
line = f"[{datetime.datetime.now():%Y-%m-%d %H:%M}] 포트{PORT} 리허설 {passed}/{total} " + ("전체통과" if passed == total else "실패: " + ", ".join(n for n, ok, _ in results if not ok))
with open(LOG, "a", encoding="utf-8") as f:
    f.write(line + "\n")
print("=== " + line)
sys.exit(0 if passed == total else 1)
