#!/usr/bin/env python3
from __future__ import annotations
import json, subprocess, tempfile, re, zipfile
from pathlib import Path
from datetime import datetime, timezone
ROOT=Path(__file__).resolve().parents[1]
DATA=ROOT/'assets/data/image-admin'
QA=ROOT/'qa_screenshots'; QA.mkdir(exist_ok=True)
report={'version':'v97-rc50-visual-review-correction-mode','createdAt':datetime.now(timezone.utc).isoformat(timespec='seconds'),'checks':{},'notes':[]}
# JSON validation
json_files=list(ROOT.rglob('*.json'))
errors=[]
for p in json_files:
    try: json.load(open(p,encoding='utf-8'))
    except Exception as e: errors.append(f'{p.relative_to(ROOT)}: {e}')
report['checks']['json']={'ok':not errors,'files':len(json_files),'errors':errors[:20]}
# JS syntax
js_files=list((ROOT/'assets/js').rglob('*.js')) if (ROOT/'assets/js').exists() else []
js_errors=[]
for p in js_files:
    r=subprocess.run(['node','--check',str(p)],capture_output=True,text=True)
    if r.returncode: js_errors.append({'file':str(p.relative_to(ROOT)),'stderr':r.stderr[-2000:]})
report['checks']['jsSyntax']={'ok':not js_errors,'files':len(js_files),'errors':js_errors[:20]}
# HTML inline script syntax for rc50 lab
html_errors=[]
for p in [ROOT/'tools/visual_review_correction_lab.html', ROOT/'tools/special_prop_binding_lab.html']:
    text=p.read_text(encoding='utf-8')
    scripts=re.findall(r'<script[^>]*>(.*?)</script>', text, re.S|re.I)
    for i,s in enumerate(scripts):
        with tempfile.NamedTemporaryFile('w',suffix='.js',delete=False,encoding='utf-8') as tmp:
            tmp.write(s); tmp_path=tmp.name
        r=subprocess.run(['node','--check',tmp_path],capture_output=True,text=True)
        if r.returncode: html_errors.append({'file':str(p.relative_to(ROOT)),'script':i,'stderr':r.stderr[-2000:]})
report['checks']['htmlInlineJs']={'ok':not html_errors,'files':2,'errors':html_errors}
# PHP lint
php_files=list(ROOT.rglob('*.php'))
php_errors=[]
for p in php_files:
    r=subprocess.run(['php','-l',str(p)],capture_output=True,text=True)
    if r.returncode: php_errors.append({'file':str(p.relative_to(ROOT)),'stderr':r.stderr+r.stdout})
report['checks']['phpLint']={'ok':not php_errors,'files':len(php_files),'errors':php_errors}
# Atlas integrity
packs=json.load(open(DATA/'pose-atlas-packs.json'))
seq=json.load(open(DATA/'pose-atlas-exercise-sequences.json'))
poses={p['poseKey']:p for p in packs['poses']}
missing_pose=[]; missing_sprite=[]; frames=0
for ex_key,ex in seq.get('sequences',{}).items():
    for fr in ex.get('poses',[]):
        frames+=1; pk=fr.get('poseKey')
        if pk not in poses: missing_pose.append({'exerciseKey':ex_key,'poseKey':pk})
for p in poses.values():
    for aid,a in p.get('athletes',{}).items():
        src=a.get('src')
        if src and not (ROOT/src).exists(): missing_sprite.append({'poseKey':p['poseKey'],'athleteId':aid,'src':src})
manifest=json.load(open(DATA/'rc50_visual_review_manifest.json'))
report['checks']['atlasIntegrity']={'ok':not missing_pose and not missing_sprite,'poseKeys':len(poses),'frames':frames,'athleteSprites':sum(len(p.get('athletes',{})) for p in poses.values()),'missingPoseKeys':missing_pose[:20],'missingSprites':missing_sprite[:20]}
report['checks']['rc50Manifest']={'ok':manifest['summary']['counts']['missingPoseKeys']==0 and manifest['summary']['runtimeChanged'] is False,'counts':manifest['summary']['counts'],'runtimeChanged':manifest['summary']['runtimeChanged']}

# Apply script self-test
sample=QA/'rc50_sample_visual_review_patch.json'
apply_ok=False; apply_stderr=''
if sample.exists():
    r=subprocess.run(['python3', str(ROOT/'tools/pose_atlas_rc50_apply_visual_review_patch.py'), str(sample), '--write-proposal', '--out', str(QA/'rc50_sample_visual_review_proposal.json')], capture_output=True, text=True)
    apply_ok=(r.returncode==0 and (QA/'rc50_sample_visual_review_proposal.json').exists())
    apply_stderr=(r.stderr or r.stdout)[-2000:]
else:
    apply_stderr='sample patch missing'
report['checks']['applyScriptSelfTest']={'ok':apply_ok,'stderr':apply_stderr}

# Screenshots exist
ss=[QA/'rc50_mobile_static_qa.png',QA/'rc50_ipad_static_qa.png',QA/'rc50_desktop_static_qa.png']
report['checks']['fallbackScreenshots']={'ok':all(p.exists() and p.stat().st_size>1000 for p in ss),'files':[str(p.relative_to(ROOT)) for p in ss]}
report['visualBrowserTest']={'performed':False,'reason':'Chromium/Playwright browser navigation is not reliable in this sandbox; RC50 screenshots are static fallback renderings.'}
report['safeToDeployForReview']=all(v.get('ok',False) for k,v in report['checks'].items())
out=QA/'rc50_package_qa_report.json'
out.write_text(json.dumps(report,ensure_ascii=False,indent=2)+'\n',encoding='utf-8')
print(json.dumps(report['checks'],indent=2,ensure_ascii=False))
print(out)
