"""One user-approved additional attempt per camera; preserve every prior run."""
import ast
import csv
import hashlib
import json
import pathlib
import re
import shutil
import subprocess
import sys

P = pathlib.Path
ROOT = P(__file__).resolve().parent.parent
PRIOR = ROOT.parent / '2026-11-02_contextra-approved16-recordings_r01'
IDS = [12, 13, 17, 18, 19, 20, 21]
sys.path.insert(0, '/data/codex-artifacts/contextra/action-model-eval/2026/2026-10-27_contextra-four-scenarios-prep_r01/helpers')
import batch_job as b
import remote

temp = P('/tmp/codex/ltb1021/20261102-retry7-recording')
temp.mkdir(parents=True, exist_ok=True, mode=0o700)
socket = str(temp / 'ssh.sock')
remote.SSH[1:1] = ['-o', 'ControlMaster=auto', '-o', 'ControlPersist=60', '-o', 'ControlPath=' + socket]
b.SCP = ['scp'] + remote.SSH[1:-3] + ['-P', remote.SSH[-2]]
b.LOCAL = ROOT
b.REMOTE = '/storage/contextra/experiments/action-model-eval/2026/' + ROOT.name
reports = ROOT / 'reports'
reports.mkdir()

def command(args, sudo=False, timeout=45):
    r = b.run(args, sudo=sudo, timeout=timeout)
    if r.returncode:
        err = re.sub(r'[a-zA-Z]+://[^\s]+', '<endpoint omitted>', r.stderr.decode(errors='replace'))
        raise RuntimeError(json.dumps({'returncode': r.returncode, 'stderr': err[-1500:]}))
    return r.stdout

b.command = command

def transfer(sources, dest, timeout=240):
    r = subprocess.run(b.SCP + sources + [dest], capture_output=True, timeout=timeout)
    if r.returncode:
        err = re.sub(r'[a-zA-Z]+://[^\s]+', '<endpoint omitted>', r.stderr.decode(errors='replace'))
        raise RuntimeError(json.dumps({'phase': 'transfer', 'returncode': r.returncode, 'stderr': err[-1500:]}))

b.transfer = transfer
results = []
before = None
try:
    prior = json.loads((PRIOR / 'manifest.json').read_text())
    targets = [x for x in prior['targets'] if x['camera_id'] in IDS]
    assert [x['camera_id'] for x in targets] == IDS
    sys.path.insert(0, '/tmp/codex/ltb1021/2026-11-01_icerti-web-ssh-connect_r01')
    from remote_read import powershell
    ps = P('/data/codex-artifacts/contextra/field-maintenance/2026/2026-11-01_icerti-web-media-analysis_r01/helpers/db_schema.ps1').read_text()
    a = ps.index('SELECT TABLE_NAME'); z = ps.index('ROLLBACK;', a)
    ps = ps[:a] + 'SELECT camera_id,camera_name,(deleted_at_wall_ms IS NULL),assignment_state FROM CAMERA WHERE camera_id IN (' + ','.join(map(str, IDS)) + ') ORDER BY camera_id;\n' + ps[z:]
    web = json.loads(powershell(ps, timeout=35))
    assert web['exit_code'] == 0
    rows = [x.split('\t') for x in web['stdout'].strip().splitlines()[1:]]
    valid_web = {int(x[0]): x[1:] for x in rows}
    b.save(ROOT / 'web_registration.json', web)
    b.save(ROOT / 'manifest.json', {'targets': targets, 'seconds': 60, 'additional_capture_attempts_per_channel': 1, 'prior': str(PRIOR)})
    b.TARGETS = [(x['camera_id'], x['input_sha256']) for x in targets]
    b.OLD = PRIOR / 'helpers/adapted'
    b.SNAPSHOT_CODE = b.SNAPSHOT_CODE.replace("targets={22:'crowd',23:'loitering',26:'intrusion',29:'fire'}", 'targets=' + repr(dict(b.TARGETS))).replace('config=s.get(scenario)', "config={'enabled':hashlib.sha256(str(s.get('uri','')).encode()).hexdigest()==scenario}")
    src = P(b.__file__).read_text()
    a = src.index('def container('); z = src.index('\n\ndef main()', a)
    exec(src[a:z].replace('20261027-batch-r01', '20261102-retry7-r01').replace("['--seconds','120'", "['--seconds','60'"), b.__dict__)
    b.save(reports / 'safety_before.json', b.safety())
    assert command(['docker','image','inspect',b.IMAGE,'--format','{{.Id}}'],sudo=True).decode().strip()==b.IMAGE
    toolcode = "import shutil,pathlib,yaml;assert shutil.which('ffmpeg') and shutil.which('ffprobe');assert pathlib.Path('/usr/share/fonts/truetype/dejavu/DejaVuSans.ttf').is_file();print('TOOLS_READY')"
    print(command(['docker','run','--rm','--pull','never','--runtime','runc','--network','none','--read-only','--cpus','1','--memory','512m','--pids-limit','64','-e','NVIDIA_VISIBLE_DEVICES=void','--entrypoint','python3',b.IMAGE,'-B','-c',toolcode],sudo=True).decode(),flush=True)
    before = b.snapshot(); b.save(reports / 'operational_before.json', before)
    command(['mkdir','-m','700',b.REMOTE],sudo=True)
    command(['chown','si:si',b.REMOTE],sudo=True)
    command(['mkdir',b.REMOTE+'/reports',b.REMOTE+'/helpers'])
    for target in targets:
        cid = target['camera_id']
        b.safety()
        current = b.snapshot()
        assert current['containers']==before['containers'], 'Common service identity changed'
        t = current['targets'][str(cid)]
        if valid_web.get(cid) != [target['name'],'1','ASSIGNED'] or not t.get('scenario_config',{}).get('enabled') or not t.get('source_present'):
            results.append({'camera_id':cid,'status':'DEFERRED_REGISTRATION'})
            continue
        loc = ROOT / f'camera{cid}'; loc.mkdir()
        for folder in ('helpers','inputs','reports'):
            (loc/folder).mkdir()
        b.save(loc/'reports/registration.json', {'config_sha256':current['config_sha256'], **t})
        command(['mkdir',b.REMOTE+f'/camera{cid}',b.REMOTE+f'/camera{cid}/helpers',b.REMOTE+f'/camera{cid}/inputs',b.REMOTE+f'/camera{cid}/reports'])
        for name in ('capture.py','review_video.py'):
            ast.parse((b.OLD/name).read_text()); shutil.copy2(b.OLD/name,loc/'helpers'/name)
        transfer([str(p) for p in (loc/'helpers').iterdir()],b.SSH[-1]+':'+b.REMOTE+f'/camera{cid}/helpers/')
        code = b.container(cid,'record')
        if code:
            row={'camera_id':cid,'status':'DEFERRED_CAPTURE','returncode':code}
            results.append(row);print(json.dumps(row),flush=True)
            if code in (137,139): break
            continue
        code = b.container(cid,'review')
        if code:
            results.append({'camera_id':cid,'status':'DEFERRED_REVIEW','returncode':code})
            if code in (137,139): break
            continue
        for folder,names in [('inputs',[f'camera{cid}_original.mp4',f'camera{cid}_review.mp4',f'camera{cid}_frame_map.csv']),('reports',['capture.json','original_probe.json','review_probe.json','video_validation.json'])]:
            transfer([b.SSH[-1]+':'+b.REMOTE+f'/camera{cid}/{folder}/'+n for n in names],str(loc/folder)+'/')
        v=json.loads((loc/'reports/video_validation.json').read_text())
        for p in (loc/'inputs').iterdir():
            h=hashlib.sha256()
            with p.open('rb') as f:
                for chunk in iter(lambda:f.read(1048576),b''):h.update(chunk)
            assert h.hexdigest()==v[p.name]['sha256'] and p.stat().st_size==v[p.name]['bytes']
        with (loc/f'inputs/camera{cid}_frame_map.csv').open() as f: mapped=list(csv.DictReader(f))
        assert [int(x['frame_1based']) for x in mapped]==list(range(1,v['frame_count']+1))
        assert max(abs(float(x['elapsed_seconds'])-float(x['review_pts_seconds'])) for x in mapped)<0.001
        b.save(loc/'reports/transfer_validation.json',{'files':3,'size_sha256_match':True,'frame_map_rows':len(mapped),'sequential':True})
        row={'camera_id':cid,'status':'READY','frames':v['frame_count'],'elapsed_seconds':v['elapsed_seconds']}
        results.append(row);print(json.dumps(row),flush=True)
except Exception as e:
    b.save(reports/'common_failure.json',{'error':str(e),'action':'Stop new captures; preserve completed results'})
    print('COMMON_FAILURE: '+str(e),flush=True)
finally:
    for cid in IDS:
        if cid not in [x['camera_id'] for x in results]:results.append({'camera_id':cid,'status':'NOT_RUN'})
    b.save(reports/'batch_results.json',results)
    if before is not None:
        try:
            after=b.snapshot();b.save(reports/'operational_after.json',after)
            a={s['stream_id']:s for s in before['streams']};z={s['stream_id']:s for s in after['streams']}
            comparison={k:before[k]==after[k] for k in ('config_sha256','targets','containers')}
            comparison['channel_state_changes']=[k for k in sorted(set(a)|set(z)) if a.get(k)!=z.get(k)]
            comparison['temporary_containers_remaining']=command(['docker','ps','-aq','--filter','name=^/contextra-(record|review)-camera(12|13|17|18|19|20|21)-20261102-retry7-r01$'],sudo=True).decode().splitlines()
            b.save(reports/'operational_comparison.json',comparison)
            b.save(reports/'safety_after.json',b.safety())
        except Exception as e:b.save(reports/'final_snapshot_failure.json',{'error':str(e)})
    print(json.dumps(results),flush=True)
    subprocess.run(b.SSH[:-3]+['-O','exit']+b.SSH[-3:],capture_output=True,timeout=20)
