# -*- coding: utf-8 -*-
"""
제관웹 오프라인 단일파일 — 한 줄 검증 진입점  [리팩토링 4단계 2026-09-15]

왜 만들었나
  구조감사가 지적한 구멍: 검증이 '여러 명령을 차례로 치는 일'이라 바쁠 때 건너뛰게 된다.
  게다가 브라우저 검증(sandbox_test.html)은 기본판만 보고 축소판·웹게시용은 안 봤다.
  → 빌드 3판 + 검증 4종을 이것 하나로 묶는다. 하나라도 PASS 가 아니면 0 이 아닌 값으로 끝난다.

쓰는 법
  python run_checks.py            빌드 3판 + 검증 4종 (표준)
  python run_checks.py --verify   빌드 건너뛰고 검증만 (이미 빌드한 산출물 점검)
  python run_checks.py --quick    기본판만 빌드·검증 (작업 도중 빠른 확인)

이것으로 안 되는 것 — 반드시 사람이 봐야 하는 것
  치수·스냅·사진·녹음·조립잠금·툴바 실측은 브라우저에서만 가능하다.
  _build/sandbox_test.html 을 세 판 모두(?f= 로 판을 바꿔가며) 열어 확인할 것.
  그리고 최종 판단은 대표의 아이패드 화면이다(제5조).
"""
import io
import json
import os
import subprocess
import sys

HERE = os.path.dirname(os.path.abspath(__file__))
PY = sys.executable

# 공정서 원본 = 제품 폴더 [CEO 원칙 2026-09-15 — "서류 폴더가 원본"]
#   표준 빌드는 항상 이 제품들을 얹는다. 제품을 늘리려면 여기 한 줄만 늘린다.
#   대표가 <제품폴더>\공정서\ 에 파일을 넣고 다시 빌드하면 그대로 반영된다.
PRODUCTS = [
    r'E:\VLA시스템\에버다임\광산기계\락드릴\프레임\E500PRO-R_프레임',
]
PRODUCT_ARGS = []
for _p in PRODUCTS:
    PRODUCT_ARGS += ['--product', _p]

# (이름, build.py 인자, verify.js 인자)
EDITIONS = [
    ('기본판',   [],         []),
    ('축소판',   ['--lite'], ['--lite']),
    ('웹게시용', ['--web'],  ['--web']),
]
# 불투명 오리진(아이패드 file:// 재현)은 기본판으로 한 번 더 본다 — 저장소 안전망 검사
EXTRA_VERIFY = [('기본판/불투명오리진', ['--opaque'])]


def run(title, cmd):
    print('\n' + '─' * 72)
    print('▶', title)
    print('  $', ' '.join(cmd))
    print('─' * 72)
    r = subprocess.run(cmd, cwd=HERE)
    if r.returncode != 0:
        print('  ✗ 실패 (종료코드 %d): %s' % (r.returncode, title))
    return r.returncode == 0


def verify(title, args):
    """verify.js 는 문제가 있어도 종료코드로 알리지 않고 'FAIL' 을 찍는다.
       그래서 출력을 읽어 판정한다 — '조용히 통과'를 막는 핵심이다."""
    cmd = ['node', 'verify.js'] + args
    print('\n' + '─' * 72)
    print('▶ 검증:', title)
    print('  $', ' '.join(cmd))
    print('─' * 72)
    r = subprocess.run(cmd, cwd=HERE, stdout=subprocess.PIPE,
                       stderr=subprocess.STDOUT)
    out = r.stdout.decode('utf-8', 'replace')
    sys.stdout.write(out)
    if r.returncode != 0:
        print('  ✗ verify.js 가 터졌다 (종료코드 %d)' % r.returncode)
        return False
    if '전체 판정: PASS' not in out:
        print('  ✗ PASS 가 아니다')
        return False
    return True


def roundtrip():
    """'되돌아오는 길' 한 판 [신규기능 2~4, 2026-09-15]

    왜 — 이 구조 전체의 목적은 "제품 폴더가 원본"이다. 대표가 아이패드에서 내보낸
      공정이력.json 을 제품 폴더\\공정서\\ 에 넣고 다시 빌드하면 화면에 반영돼야 한다.
      그런데 빌드가 파일을 '싣기만' 하고 쉼에 그 파일을 '읽는 가지'가 없으면
      오류 하나 없이 그냥 아무 일도 안 일어난다 — 대표는 이유를 알 수 없다.
      실제로 이번에 그 가지가 빠져 있었다(공정이력.json 분기 없음).
    어떻게 — 가짜 제품 폴더를 임시로 만들어 한 판 빌드하고, 쉼이 그것을 읽어내는지 본다.
      대표의 진짜 제품 폴더에는 아무것도 쓰지 않는다(읽기만 한다는 원칙 그대로).
      산출물은 뒤이은 표준 빌드가 덮어쓰므로 반드시 '맨 먼저' 돈다.
    """
    import shutil
    import tempfile
    real = os.path.join(PRODUCTS[0], '공정서', 'chapters.json')
    if not os.path.exists(real):
        print('  (되돌아오는 길 시험 건너뜀 — chapters.json 이 없다)')
        return True
    tmp = tempfile.mkdtemp(prefix='왕복시험_')
    try:
        pdir = os.path.join(tmp, 'E500PRO-R_프레임')     # 이름은 진짜와 같아야 챕터가 맞는다
        proc = os.path.join(pdir, '공정서')
        os.makedirs(proc)
        shutil.copyfile(real, os.path.join(proc, 'chapters.json'))
        # [CEO 2026-09-16] 1판 항목 하나 + 2판 항목 하나를 일부러 섞어 넣는다.
        #   새 칸(묶음 부품·위치·이유·주의·사진·카메라)이 내보내기→폴더→다시 빌드 왕복에서
        #   살아남는지, 그리고 대표가 이미 저장해 둔 1판 항목이 같이 읽히는지를 한 번에 본다.
        book = {
            'kind': 'dozikworks-공정이력', 'version': 2, '제품': '왕복시험',
            '순서': {'프레임': {'version': 2,
                                '항목': [{'partIndex': 3, '작업': 'ㄱ', 'tag': '가접', 'note': ''},
                                         {'kind': 'add', 'parts': [5, 6], 'partIndex': 5,
                                          '작업': 'ㄴ', '위치': '스탠드 좌측면 기준선에',
                                          '이유': '여기가 기준면이라 먼저 잡는다',
                                          '주의': '정반 수평 먼저 확인',
                                          'tag': '선작업', 'note': '',
                                          'photos': ['가접/프레임 가접/20260708_082349.jpg'],
                                          'camera': {'position': [1000.0, 800.0, 1200.0],
                                                     'target': [0.0, 0.0, 0.0]},
                                          'at': '2026-09-16T00:00:00.000Z', 'by': 'CEO'}],
                                'confirmed': {'by': 'CEO', 'at': '2026-09-15'},
                                'at': '2026-09-15T00:00:00.000Z'}},
            '이력': [{'partIndex': 3, 'at': '2026-09-15T00:00:00.000Z',
                      'tag': '개선', 'text': '왕복시험', 'by': 'CEO'}],
            '사진연결': [{'partIndex': 3, 'photo': '가접/프레임 가접/20260708_082349.jpg',
                          'at': '2026-09-15T00:00:00.000Z', 'by': 'CEO'}],
        }
        with io.open(os.path.join(proc, '공정이력.json'), 'w', encoding='utf-8') as f:
            f.write(json.dumps(book, ensure_ascii=False, indent=1))
        if not run('빌드: 되돌아오는 길(가짜 제품 폴더)',
                   [PY, 'build.py', '--product', pdir]):
            return False
        return verify('되돌아오는 길', ['--roundtrip'])
    finally:
        shutil.rmtree(tmp, ignore_errors=True)


def main():
    quick = '--quick' in sys.argv
    skip_build = '--verify' in sys.argv
    eds = EDITIONS[:1] if quick else EDITIONS
    extras = [] if quick else EXTRA_VERIFY

    results = []
    # '되돌아오는 길'은 반드시 맨 먼저 — 가짜 제품 폴더로 빌드한 산출물을
    #   뒤이은 표준 빌드가 덮어써야 현장에 가짜가 남지 않는다.
    if not skip_build and not quick:
        results.append(('되돌아오는 길', roundtrip()))

    if not skip_build and all(ok for _, ok in results):
        for name, bargs, _ in eds:
            results.append(('빌드 ' + name,
                            run('빌드: ' + name, [PY, 'build.py'] + bargs + PRODUCT_ARGS)))
            if not results[-1][1]:
                break                      # 빌드가 깨졌으면 검증은 의미가 없다

    if all(ok for _, ok in results):
        for name, _, vargs in eds:
            results.append(('검증 ' + name, verify(name, vargs)))
        for name, vargs in extras:
            results.append(('검증 ' + name, verify(name, vargs)))

    print('\n' + '=' * 72)
    for name, ok in results:
        print('  %s  %s' % ('PASS' if ok else 'FAIL', name))
    bad = [n for n, ok in results if not ok]
    print('=' * 72)
    if bad:
        print('전체: FAIL — %d건 (%s)' % (len(bad), ', '.join(bad)))
        print('※ 나빠졌으면 고치려 들기 전에 직전 성공 커밋으로 되돌려라 (제2조).')
        return 1
    print('전체: PASS — %d건 전부 통과' % len(results))
    print('※ 여기까지는 자동 검증이다. 치수·사진·녹음 실동작은 sandbox_test.html 로,')
    print('   최종 확인은 대표의 아이패드 화면으로 해야 한다 (제5조).')
    return 0


if __name__ == '__main__':
    sys.exit(main())
