import contextlib,glob,hashlib,importlib.util,json,os,sys,unittest
from dataclasses import asdict
from pathlib import Path
from unittest.mock import patch
os.umask(0o077)
R=Path('/evidence');mode=os.environ['VERIFY_MODE'];root=Path('/source' if mode=='source' else '/opt/contextra')
assert Path.cwd()==root and not glob.glob('/dev/nvidia*')
expected=json.loads((R/'expected-source.json').read_text())
with (R/(mode+'-verify.log')).open('x') as log,contextlib.redirect_stdout(log),contextlib.redirect_stderr(log):
    import app.intrusion as current
    import app.det_result_path as result_path
    imports={}
    for module,relative in [(current,'app/intrusion.py'),(result_path,'app/det_result_path.py')]:
        path=Path(module.__file__).resolve();assert path==root/relative
        digest=hashlib.sha256(path.read_bytes()).hexdigest();assert digest==expected[relative]
        imports[relative]=dict(path=str(path),sha256=digest)
    suite=unittest.defaultTestLoader.loadTestsFromNames(['test_intrusion.IntrusionTest',
        'test_intrusion_connection.ScenarioConnectionTest',
        'test_intrusion_connection.IntrusionSettingsTest.test_intrusion_http_and_save_failure',
        'test_intrusion_preparation.IntrusionPreparationTest'])
    result=unittest.TextTestRunner(stream=log,verbosity=2).run(suite)
    s=importlib.util.spec_from_file_location('baseline_intrusion',R/'baseline_intrusion.py')
    baseline=importlib.util.module_from_spec(s);sys.modules[s.name]=baseline;s.loader.exec_module(baseline)
    from app.contracts.frames import DetectionObservation
    from test_intrusion import frame
    now=[0.];config=dict(enabled=True,roi=((0.,0.),(.5,0.),(.5,1.),(0.,1.)))
    before=baseline.Intrusion(baseline.IntrusionConfig(**config),clock=lambda:now[0])
    after=current.Intrusion(current.IntrusionConfig(**config),clock=lambda:now[0])
    comparisons=0
    with patch('uuid.uuid4',return_value='comparison-event'):
        for generation in (1,2):
            before.clear();after.clear()
            for seq in range(60):
                now[0]=seq*.1+(31 if seq>=45 else 0)
                pts=None if seq==35 else seq*.1
                f=frame(seq,pts,generation)
                empty=seq in (3,4,24,36)
                score=.5 if seq in (7,8,9) else .9
                box=(.6,.1,.8,.8) if 15<=seq<=28 else (.1,.1,.3,.8)
                obs=() if empty else (DetectionObservation(f,'RFDETRMedium',0,score,box),)
                ids=() if empty else (None if seq==37 else 1,)
                a,e=before.evaluate(f,obs,ids);b,z=after.evaluate(f,obs,ids)
                assert [asdict(v) if v else None for v in a]==[asdict(v) if v else None for v in b]
                assert [v.payload() for v in e]==[v.payload() for v in z]
                assert {k:asdict(v) for k,v in before.states.items()}=={k:asdict(v) for k,v in after.states.items()}
                comparisons+=1
    summary=dict(tests=result.testsRun,errors=len(result.errors),failures=len(result.failures),baseline_frames=comparisons,imports=imports,gpu_devices=0)
(R/(mode+'-verify.json')).write_text(json.dumps(summary,indent=2));print(json.dumps(summary));assert result.wasSuccessful()
