import hashlib
import json
import pathlib
import subprocess
import sys
import time

PREP=pathlib.Path('/data/codex-artifacts/contextra/action-model-eval/2026/2026-10-26_contextra-camera24-prep_r01')
BASE=PREP.parent/'2026-10-26_contextra-camera24-eval_r01'
sys.path.insert(0,str(PREP/'helpers'))
from remote import run,SSH
LOCAL=pathlib.Path(__file__).resolve().parent.parent
REMOTE='/storage/contextra/experiments/action-model-eval/2026/'+LOCAL.name
OLD='/storage/contextra/experiments/action-model-eval/2026/'+BASE.name
RP='/storage/contextra/experiments/action-model-eval/2026/'+PREP.name
SOURCE=pathlib.Path('/data/playlist/uploads/260914_아이서티_컨텍스트라_테스트용_kisa_영상들_클립들/(검증용)kisa_data/싸움/배포/clips')
FILES=sorted(SOURCE.glob('*.mp4'))
CLIPS=['01_camera24']+[f'{i+2:02d}_KISA_'+p.stem.split('_s')[0] for i,p in enumerate(FILES)]
def checked(args,**kw):
    p=run(args,**kw)
    if p.returncode: raise RuntimeError(p.stderr.decode()[-3000:]+p.stdout.decode()[-1000:])
    return p.stdout
def scp(src,dst):
    subprocess.run(['scp','-q','-r','-i',SSH[2],'-o',SSH[4],'-o','StrictHostKeyChecking=yes','-P','1022',src,dst],check=True,timeout=900)
def snapshot(label):
    data=checked(['docker','exec','-i','contextra-ai2-analytics-1','python3','-'],data=(PREP/'helpers/snapshot.py').read_bytes(),timeout=90)
    (LOCAL/'reports'/f'production_{label}.json').write_bytes(data)
def prepare():
    assert len(FILES)==6
    checked(['test','!','-e',REMOTE]); checked(['mkdir',REMOTE],sudo=True); checked(['chown','si:si',REMOTE],sudo=True)
    checked(['chmod','700',REMOTE])
    dirs=['helpers','basehelpers','reports','sources','download','clips','fonts']
    for clip in CLIPS:
        dirs += [f'clips/{clip}',f'clips/{clip}/inputs',f'clips/{clip}/reports',f'clips/{clip}/outputs',f'clips/{clip}/pose_models',f'clips/{clip}/pose_models/RFDETRKeypointPreview',f'clips/{clip}/pose_models/RFDETRKeypointPreview/8',f'download/{clip}']
    checked(['mkdir','-p']+[REMOTE+'/'+d for d in dirs])
    for p in (LOCAL/'helpers').glob('*.py'): scp(str(p),'si@58.233.207.31:'+REMOTE+'/helpers/')
    for name in ['pose_extract.py','evaluate.py']: scp(str(BASE/'helpers'/name),'si@58.233.207.31:'+REMOTE+'/basehelpers/')
    scp('/usr/share/fonts/opentype/noto/NotoSansCJK-Regular.ttc','si@58.233.207.31:'+REMOTE+'/fonts/')
    manifest=[]
    for key,p in zip(CLIPS[1:],FILES):
        scp(str(p),'si@58.233.207.31:'+REMOTE+'/sources/'+key+'.mp4')
        digest=hashlib.sha256(p.read_bytes()).hexdigest()
        assert checked(['sha256sum',REMOTE+'/sources/'+key+'.mp4']).decode().split()[0]==digest
        manifest.append(dict(clip=key,source_file=p.name,sha256=digest))
    checked(['cp',OLD+'/inputs/pose.json',REMOTE+'/clips/01_camera24/inputs/pose.json'])
    (LOCAL/'reports/source_manifest.json').write_text(json.dumps(manifest,indent=2))
    snapshot('before'); print('PREPARED',flush=True)
def trial(stage,clip,model=None):
    free=int(checked(['df','-B1','--output=avail','/']).decode().splitlines()[-1]); assert free>=15*1024**3
    gpu=stage in ['pose','evaluate']
    if gpu:
        freegpu=int(checked(['nvidia-smi','--query-gpu=memory.free','--format=csv,noheader,nounits']).decode().strip()); assert freegpu>=8192
    analytic=checked(['docker','inspect','contextra-ai2-analytics-1','--format','{{.Image}}']).decode().strip()
    image='contextra-action-eval:20261026-camera24-r03' if stage=='evaluate' else analytic
    name='contextra-short-r01-'+stage+'-'+clip.lower().replace('_','-')+('-'+model if model else '')
    args=['docker','run','--rm','--name',name,'--user','0:0','--read-only','--cap-drop','ALL','--cap-add','DAC_OVERRIDE','--security-opt','no-new-privileges','--cpus','2','--memory','8g','--pids-limit','256','--network','none','--shm-size','256m','--tmpfs','/tmp:rw,size=512m','-e','PYTHONDONTWRITEBYTECODE=1','-e','OMP_NUM_THREADS=2','-e','OPENBLAS_NUM_THREADS=2','-e','CLIP='+clip,'-e','MPLCONFIGDIR=/tmp/mpl','-v',REMOTE+'/clips/'+clip+':/eval','-v',REMOTE+'/helpers:/helpers:ro','-v',REMOTE+'/basehelpers:/basehelpers:ro','-v',RP+':/work:ro','-v',OLD+':/old:ro','-v',REMOTE+'/download:/download','-v',REMOTE+'/fonts:/fonts:ro']
    if gpu: args+=['--gpus','device=0']
    else: args+=['--runtime','runc','-e','NVIDIA_VISIBLE_DEVICES=void']
    if stage=='pose': args+=['-v',REMOTE+'/sources/'+clip+'.mp4:/source.mp4:ro','-v','/storage/contextra/models/triton/RFDETRKeypointPreview/8:/eval/pose_models/RFDETRKeypointPreview/8:ro']
    args+=['--entrypoint','python3',image,'/helpers/'+stage+'.py']+([model] if model else [])
    print('START',stage,clip,model,flush=True)
    try: p=run(args,timeout=1800)
    except BaseException:
        run(['docker','stop','--time','15',name],timeout=40); raise
    stamp=str(int(time.time()))
    (LOCAL/'reports'/f'{stage}_{clip}_{model}_{stamp}.log').write_bytes(p.stdout+p.stderr)
    print(p.stdout.decode()[-1200:],p.stderr.decode()[-1600:],flush=True)
    if p.returncode: raise RuntimeError(f'{stage}/{clip}/{model} failed')
    print('DONE',stage,clip,model,flush=True)
if __name__=='__main__':
    (LOCAL/'reports').mkdir(exist_ok=True)
    if sys.argv[1]=='prepare': prepare()
    elif sys.argv[1]=='pose':
        for clip in CLIPS[1:]: trial('pose',clip)
        snapshot('after_pose')
    elif sys.argv[1]=='evaluate':
        for clip in CLIPS:
            if clip!='01_camera24':
                for attempt in range(240):
                    p=run(['test','-f',REMOTE+'/clips/'+clip+'/reports/pose_summary.json'])
                    if p.returncode==0: break
                    time.sleep(5)
                else: raise TimeoutError('Pose prerequisite missing '+clip)
            for model in ['stgcnpp','posec3d','ava_slowonly','yowov2']: trial('evaluate',clip,model)
        snapshot('after_models')
    elif sys.argv[1]=='one': trial(sys.argv[2],sys.argv[3],sys.argv[4] if len(sys.argv)>4 else None)
