"""CPU-only helpers; AST extraction executes exact boundary functions, not servers.

These tests deliberately do not claim to exercise FastAPI/ASGI authentication,
PostgreSQL, the SDK or Nginx. Full server acceptance belongs to Codex.
"""
import ast
from copy import deepcopy
from pathlib import Path
from app.contracts.frames import FrameEnvelope, FrameTime, DetectionObservation
from app.contracts.visualization import FrameResult, VisualizationCommand
from app.intrusion import IntrusionConfig, IntrusionEvent, ScenarioDecision
from app.loitering import LoiteringConfig, LoiteringEvent, LoiteringDecision

ROOT = Path(__file__).resolve().parents[1]
WALL_MS = 1758000005123


def frame(seq=50, seconds=5.0, *, source='77', run='run', generation=1, synced=True):
    timing = FrameTime('anchor',1758000000000,0,90000,450000,450000,
                      1758000005000,'SYNCED','RTP_ZERO_BASE') if synced else None
    return FrameEnvelope('server',source,'stream',run,generation,seq,
        None if seconds is None else round(seconds*1e9),WALL_MS,640,360,'revision',timing)


def result(kind='LOITERING', *, synced=True):
    f=frame(synced=synced)
    obs=DetectionObservation(f,'RFDETRMedium',0,.9,(.1,.2,.3,.8))
    if kind is None:
        return FrameResult(f,(VisualizationCommand(obs,'person',track_id=1),),camera_id=77)
    cfg_type,event_type,decision_type=(LoiteringConfig,LoiteringEvent,LoiteringDecision) if kind=='LOITERING' else (IntrusionConfig,IntrusionEvent,ScenarioDecision)
    cfg=cfg_type(enabled=True,roi_name='Zone A',dwell_seconds=5.,grace_seconds=1.,rearm_seconds=1.,retention_seconds=60.)
    event=event_type('event-01',f,1,'primary','0',.9,obs.bbox_xyxy,'RFDETRMedium',WALL_MS)
    decision=decision_type((.2,.8),True,kind,.5,'event-01')
    command=VisualizationCommand(obs,'person',color='#FF3030',track_id=1,event_ids=('event-01',),scenario=decision)
    return FrameResult(f,(command,),(event,),cfg,'a'*64,camera_id=77)


def event_payload(value):
    return dict(value.events[0].payload(),cameraId=77,
                scenarioConfig=value.snapshot()['scenarioConfig'])


def node_function(path, function, namespace=None, owner=None):
    """Run a verbatim function AST with explicit dependency fakes where necessary."""
    tree=ast.parse((ROOT/path).read_text())
    candidates=tree.body
    if owner:
        candidates=next(n for n in tree.body if isinstance(n,ast.ClassDef) and n.name==owner).body
    node=deepcopy(next(n for n in candidates if isinstance(n,(ast.FunctionDef,ast.AsyncFunctionDef)) and n.name==function))
    node.decorator_list=[]
    module=ast.fix_missing_locations(ast.Module(body=[node],type_ignores=[]))
    scope={} if namespace is None else dict(namespace)
    exec(compile(module,str(ROOT/path),'exec'),scope)
    return scope[function]
