"""Part-local adapter around frozen feature and decision functions."""
import bootstrap
from bootstrap import SOURCE, BASELINE
from dataclasses import replace
import hashlib
import json
import os
from pathlib import Path
import runpy
import shutil
import statistics
import time
import inputs
from integrated import Integrated
from features import Settings
from state import Episode
from evaluate_integrated import group_states
from evaluate import stats


def sha(path): return hashlib.sha256(path.read_bytes()).hexdigest()


def main():
    out = Path('/out'); clip = os.environ['CLIP']
    assert clip in json.loads((out/'plan.json').read_text())['parts']
    candidate = json.loads((out/'candidates.json').read_text())
    assert candidate['frozen'] and sha(out/'candidates.json') == sha(SOURCE/'candidates.json')
    dest = out/'download/SELF_L01/data'/clip
    dest.mkdir(parents=True, exist_ok=True)
    complete = dest/'summary.json'
    if complete.exists():
        assert sha(dest/'B_Integrated_diagnostics.jsonl') == json.loads(complete.read_text())['sha256']
        print('REUSED', clip); return
    raw_root = out/'l01_raw'/clip
    raw_root.mkdir(parents=True, exist_ok=True)
    for name in ('specs.json','self_maps.json','cases.json'):
        shutil.copy2(out/name, raw_root/name)
    inputs.OUT = raw_root
    runpy.run_path(str(BASELINE/'helpers/extract_features.py'), run_name='__main__')
    inputs.OUT = out
    import augment
    augment.OUT = out
    augment.BASELINE = raw_root
    augment.main()
    runpy.run_path(str(SOURCE/'helpers/enhance_motion.py'), run_name='__main__')
    cfg = candidate['candidates'][0]['config']
    engine = Integrated(cfg); ep = Episode(replace(Settings(), display_hold=1.0)); holds = {}
    records=[]; times=[]; first=None
    feature_path=out/'work'/clip/'enriched.jsonl'
    for line in feature_path.open():
        r=json.loads(line)
        if first is None:first=r['seconds']
        start=time.perf_counter(); v=engine.step(r)
        episode=ep.step(r['seconds'],v['hit'],v['quiet'])
        groups=group_states(r,v,r['seconds'],holds)
        times.append((time.perf_counter()-start)*1000)
        source_pairs={p['token']:p for p in r['pairs']}
        for p in v['pairs']:
            p['insufficiency_reasons']=source_pairs[p['token']]['feature_rejections']
            p['relation']=source_pairs[p['token']]['relation']
            for f in p['diagnostics']:
                assert first <= f['input_start'] < f['input_end'] == r['seconds']
        record=dict(sample=r['sample'],source_frame=r['source_frame'],seconds=r['seconds'],
            displayed=r['displayed'],reference=r['reference'],case_ids=r['case_ids'],
            dedupe_key=r['dedupe_key'],review_frame=r['review_frame'],part_id=clip.removeprefix('SELF_'),
            groups=groups,episode=episode,valid_people=len(r['bodies']),rejected=r['rejected'],
            ambiguous=r['ambiguous'],pairs_over_cap=r['pairs_over_cap'],retained_absent=r['retained_absent'],**v)
        records.append(record)
    assert not records[0]['hit'] and not records[0]['episode']['display']
    trace=dest/'B_Integrated_diagnostics.jsonl'
    with trace.open('w') as f:
        for r in records:f.write(json.dumps(r,separators=(',',':'))+'\n')
    summary=dict(clip=clip,part_id=records[0]['part_id'],trace=trace.name,sha256=sha(trace),
        candidate_sha256=sha(out/'candidates.json'),feature_sha256=sha(feature_path),stats=stats(records),
        observations=len(records),first_pts=records[0]['seconds'],last_pts=records[-1]['seconds'],
        independent_engine=True,first_hit=False,first_display=False,cross_boundary_features=0,
        identity_namespace='(part_id,T)',decision_p95_ms=statistics.quantiles(times,n=100,method='inclusive')[94],
        decision_max_ms=max(times),frozen_sources={str(p.relative_to(Path('/evidence'))):sha(p) for p in
            (SOURCE/'helpers/integrated.py',SOURCE/'helpers/augment.py',SOURCE/'helpers/enhance_motion.py',
             SOURCE/'helpers/evaluate_integrated.py',BASELINE/'helpers/geometry.py',BASELINE/'helpers/motion.py')})
    complete.write_text(json.dumps(summary,indent=2)); print('L01_DIAG_PASS',clip,len(records),flush=True)


if __name__=='__main__':main()
